/*
* Copyright (c) 2021-2023 Virtual Park LLC. All Rights Reserved.
*
* No one other than us is allowed to copy, use, inspect, imitate, or even see this source code.
*/
function dataURItoBlob(dataURI){var byteString=atob(dataURI.split(',')[1]);var mimeString=dataURI.split(',')[0].split(':')[1].split(';')[0];var ab=new ArrayBuffer(byteString.length);var ia=new Uint8Array(ab);for(var i=0;i0
return!isNaN(str)&&!isNaN(parseFloat(str))&&parseFloat(str)>0}
var app=angular.module('vpark',['ngSanitize']).controller('c-main',['$scope',function($scope){$scope.title="";}]);app.service('vService',function($http){var _self={_self:this,bearer:null,vParkUserConnected:null,loggedIn:false,state:null,scope:null,urls:{},migrate:window.migrate,init:function(scope){_self.scope=scope;_self.state=scope.state;},ajax:{call:function(url,method,auth,data,callback,error){if(auth&&!_self.loggedIn){alert("You are not logged in.");return;}
$.ajax({url:url,contentType:"application/json",method:method,headers:auth?{Authorization:_self.bearer,Migrate:_self.migrate}:{Migrate:_self.migrate},data:data&&method!="GET"?JSON.stringify(data):null,success:function(data,textStatus,xhr){callback?callback(data):null;},error:function(data){$("#errorNotif").text(data.responseJSON.message).fadeIn(1000,()=>{setTimeout(()=>{$("#errorNotif").fadeOut(2000);},4000);});error?error(data):null;}});}},auth:{register:(email,password)=>{gtag('event','click',{'event_category':"Click",'event_label':"Register"});if(!_self.state.user.agree){alert("Please complete the form above.");return;}
if(!_self.state.user.email||_self.state.user.email.length<6){alert("Please fill out the email & password fields before clicking the register button.");return;}
if(!_self.state.user.password||_self.state.user.password.length<8){alert("Password must be at least 8 characters.");return;}
$.post("/api/register",_self.state.user,(data)=>{if(data.status_code==200){fbq('track','CompleteRegistration',{value:0.1,currency:'USD'});gtag('event','sign_up',{"method":"vpark"});gtag('event','conversion',{'send_to':'AW-782748339/oqSNCPPl3oQYELOVn_UC'});alert("Please open your email to confirm your account within the next 60 mins. Check your mailbox quota/junk folder in the next 10 minutes if you can't find it. Quite often, users entered an invalid email address or use a corporate email with domain whitelisting.");mixpanel.track("Account",{"button":"register"});location.reload();}else{alert(data.message);}},"json");},login:function(userInfo,callback){_self.ajax.call("/api/login","POST",false,userInfo,(data)=>{_self.bearer="Bearer "+data.access_token;localStorage.setItem('access_token',_self.bearer);_self.loggedIn=true;callback?callback():null;},(e)=>{alert(e.responseJSON.message);});},logout:function(callback){_self.ajax.call("/api/logout","POST",true,{},(data)=>{_self.loggedIn=false;_self.bearer=null;localStorage.removeItem('access_token');callback?callback():null;},(e)=>{alert(e.responseJSON.message);});},validate:function(callback,error){if(window.external_bearer){_self.auth.waitForBearer=setInterval(()=>{if(!window.access_token)return;_self.bearer=window.access_token;clearInterval(_self.auth.waitForBearer);if(_self.bearer)_self.loggedIn=true;callback?callback():null;},1000);return;}
else _self.bearer=localStorage.getItem('access_token');if(_self.bearer)_self.loggedIn=true;callback?callback():null;return _self.bearer;},updatePassword:()=>{if(_self.state.user.curPass.length<8){alert("Invalid password");return;}
if(_self.state.user.newPass.length<8){alert("Invalid password");return;}
if(_self.state.user.newPass2!=_self.state.user.newPass){alert("New passwords don't match");return;}
_self.ajax.call("/api/update-password","POST",true,{oldPass:_self.state.user.curPass,newPass:_self.state.user.newPass},(data)=>{_self.state.user.curPass="";_self.state.user.newPass="";_self.state.user.newPass2="";_self.state.dashboard.sub2page='password-updated';});},resetPassword:()=>{$.post("/api/reset-password",_self.state.user,(data)=>{location.href="https://www.vpark.io";},"json").fail((data)=>{alert("Password reset failed.")});},forgotPassword:()=>{if(!_self.state.user.email||_self.state.user.email.length<6){alert("Please enter a valid email");return;}
$.post("/api/forgot-password",_self.state.user,(data)=>{alert(data.message);},"json").fail((e)=>{alert(e.responseJSON.message);});},check2faRequirement:(email,password)=>{if(!_self.state.user.email||_self.state.user.email.length<6){alert("Please enter a valid email");return;}
if(!_self.state.user.password||_self.state.user.password.length<8){alert("Password must be at least 8 characters.");return;}
if(_self.state.user.secret&&_self.state.user.secret.length>4){_self.scope.login(email,password);return;}
$.post("/api/2fa/requirement",_self.state.user,(data)=>{if(data.status_code==200){_self.state.userNeeds2FA=data.google2fa;if(!_self.state.userNeeds2FA)_self.scope.login(email,password);else _self.scope.$apply();}
else alert("Login failed: "+data.message);},"json").fail((e)=>{alert(e.responseJSON.message);});},},api:{account:{db:{processing:false},uploadNewProfilePic:()=>{if($('#profilePicSelect')[0].files[0].size>1024*1024){alert("File is too big. 1 MB Max.");return;}
var formData=new FormData();formData.append('file',$('#profilePicSelect')[0].files[0]);$.ajax({url:'/api/user/profile/picture',headers:{Authorization:_self.bearer,},type:'POST',data:formData,processData:false,contentType:false,success:function(data){_self.state.user.picture='https://d3o58t2pcb2bu7.cloudfront.net/users/'+_self.state.user.hash+'/thumbnail.jpg';_self.scope.$apply();}});},saveProfile:()=>{if(_self.state.user.url_id&&(_self.state.user.url_id.length<6||!_self.state.user.url_id.match(/^[0-9a-zA-Z_]+$/)))alert("Username must be at least 6 alphanumeric chars");if(_self.state.accountConnectionChanged&&!confirm("To continue, please confirm that you agree to the latest vPark Land's terms and conditions."))return;_self.ajax.call("/api/profile","PUT",true,{name:_self.state.user.name,country:_self.state.user.country,phone:_self.state.user.phone,headline:_self.state.user.headline,url_id:_self.state.user.url_id,vpark_id:_self.state.user.vpark_id,readyplayerme:_self.state.user.readyplayerme,cloth:_self.state.user.cloth,},(data)=>{_self.state.accountConnectionChanged=false;_self.state.vParkUserConnected=_self.state.user.vpark_id;_self.state.user.name=data.name;_self.state.profileUpdated=true;_self.scope.$apply();});},rotateCoupon:(hash)=>{_self.ajax.call("/api/user/coupon/rotates","POST",true,null,(data)=>{_self.state.user.coupon=data.referral;});},setAvatar:(url,callback)=>{_self.ajax.call("/api/profile/avatar","PUT",true,{url:url},(data)=>{callback?callback():null;});},setNotification:(profile,callback)=>{_self.ajax.call("/api/profile/notification","PUT",true,{mail_transaction:profile.mail_transaction,mail_crime:profile.mail_crime,mail_update:profile.mail_update,mail_runners:profile.mail_runners,mail_bio:profile.mail_bio,},(data)=>{callback?callback():null;});},avatarStatus:(callback)=>{_self.ajax.call("/api/profile/avatar/status","GET",true,null,(data)=>{callback?callback(data):null;});},setAvatarLocation:(address,unit,callback)=>{_self.ajax.call("/api/world/player/me/location","PUT",true,{address:address,unit:unit,},(data)=>{callback?callback():null;});},setAvatarLocation2:(hash,x,y,callback)=>{_self.ajax.call("/api/world/player/me/location/hack","PUT",true,{hash:hash,x:x,y:y,},(data)=>{window.ue4('reloadWorldRaw',{hash:hash});callback?callback():null;});},getFinancial:(callback)=>{_self.ajax.call("/api/user/financial","GET",true,null,(data)=>{_self.state.user.financial=data;callback?callback():null;});},buyLife:(callback)=>{if(!_self.loggedIn)return;if(!confirm("You are paying 20000 vDs to ensoul your vHuman. Please confirm to continue."))return;_self.api.account.db.processing=true;_self.ajax.call(`/api/user/biology/life`,"POST",true,(data)=>{_self.api.account.db.processing=false;callback?callback():null;},(data)=>{_self.api.account.db.processing=false;error?error(data):0;});},inviteUser:(hash,address,callback)=>{if(!hash||hash.length<5)return;_self.ajax.call(`/api/world/player/${hash}/invite`,"POST",true,{address:address,},(data)=>{callback?callback():null;});},myInvites:(callback)=>{_self.ajax.call(`/api/world/player/me/invites`,"GET",true,null,(data)=>{callback?callback(data):null;});},cancelInvite:(hash,callback)=>{_self.ajax.call(`/api/world/player/${invite}/invite`,"DELETE",true,null,(data)=>{callback?callback(data):null;});},cancelInvites:(callback)=>{_self.ajax.call(`/api/world/player/invites`,"DELETE",true,null,(data)=>{callback?callback(data):null;});},get2FA:(callback)=>{_self.ajax.call("/api/2fa/google","GET",true,null,(data)=>{_self.state.qr=data;_self.scope.$apply();callback?callback():null;});},setup2FA:(callback)=>{_self.ajax.call("/api/2fa/google","POST",true,null,(data)=>{_self.state.backup_code=data.backup_code;_self.api.account.get2FA(callback);});},verify2FA:(code)=>{if(!code||!code.length)code=prompt("Enter the code from your authenticator app.");_self.ajax.call("/api/2fa/google","PUT",true,{secret:code},(data)=>{_self.state.user.google2fa_verified=true;_self.state.backup_code=null;_self.state.dashboard.sub2page=null;_self.state.user.entered2FA=null;_self.scope.$apply();alert("Your 2FA is now enabled.");});},remove2FA:(code)=>{if(!code||!code.length)code=prompt("Enter the code from your authenticator app.");_self.ajax.call("/api/2fa/google","DELETE",true,{secret:code},(data)=>{_self.state.user.google2fa_verified=false;_self.state.qr=null;_self.state.dashboard.sub2page=null;_self.state.user.entered2FA=null;_self.scope.$apply();alert("Your 2FA is now disabled.");});}},business:{db:{processing:false},contact:(sid,callback,error)=>{if(!_self.state.contact.first||!_self.state.contact.last||!_self.state.contact.title||!_self.state.contact.company||!_self.state.contact.email||!_self.state.contact.country||!_self.state.contact.industry||!_self.state.contact.phone)return;if(_self.api.business.db.processing)return;_self.api.business.db.processing=true;_self.ajax.call(`/api/business/contact`,"POST",false,_self.state.contact,(data)=>{_self.api.business.db.processing=false;_self.state.contact={done:true};_self.scope.$apply();callback?callback(data):0;},(data)=>{_self.api.business.db.processing=false;error?error(data):0;});},bizContact:(sid,callback,error)=>{if(!_self.state.contact.first||!_self.state.contact.last||!_self.state.contact.email||!_self.state.contact.country)return;if(_self.api.business.db.processing)return;_self.api.business.db.processing=true;_self.ajax.call(`/api/business/contact`,"POST",false,_self.state.contact,(data)=>{_self.api.business.db.processing=false;_self.state.contact={done:true};_self.scope.$apply();callback?callback(data):0;},(data)=>{_self.api.business.db.processing=false;error?error(data):0;});},},story:{db:{processing:false},get:(x1,y1,x2,y2,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.story.db.processing)return;if(!x1)[x1,y1,x2,y2]=_self.scope.getMapBound();if(_self.scope.lastRandomLandCoords[0]==x1&&_self.scope.lastRandomLandCoords[1]==y1&&_self.scope.lastRandomLandCoords[2]==x2&&_self.scope.lastRandomLandCoords[3]==y2)return;_self.scope.lastRandomLandCoords=[x1,y1,x2,y2];_self.api.story.db.processing=true;_self.ajax.call(`/api/vhuman/stories/${x1}/${y1}/${x2}/${y2}`,"GET",true,{},(data)=>{_self.api.story.db.processing=false;_self.api.story.db.data=data;if(_self.state.current&&_self.state.current.path=='map/news')_self.state.current.data=data;callback?callback(data):0;},(data)=>{_self.api.story.db.processing=false;error?error(data):0;});},pay:(sid,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.story.db.processing)return;_self.api.story.db.processing=true;_self.ajax.call(`/api/vhuman/stories/${sid}/pay`,"POST",true,{},(data)=>{_self.api.story.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.story.db.processing=false;error?error(data):0;});},close:(sid,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.story.db.processing)return;_self.api.story.db.processing=true;_self.ajax.call(`/api/vhuman/stories/${sid}`,"DELETE",true,{},(data)=>{_self.api.story.db.processing=false;_self.api.story.db.data=_self.api.story.db.data.filter(e=>e.id!=sid);callback?callback(data):0;},(data)=>{_self.api.story.db.processing=false;error?error(data):0;});},fine:(sid,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.story.db.processing)return;let price=prompt("How much vD you are asking? \n30% vPARK Fee Applies.");if(price.length<1)return;_self.api.story.db.processing=true;_self.ajax.call(`/api/vhuman/stories/${sid}/fine`,"POST",true,{amount:parseFloat(price)},(data)=>{_self.api.story.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.story.db.processing=false;error?error(data):0;});},scold:(sid,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.story.db.processing)return;_self.api.story.db.processing=true;_self.ajax.call(`/api/vhuman/stories/${sid}/scold`,"POST",true,{},(data)=>{_self.api.story.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.story.db.processing=false;error?error(data):0;});},punish:(c,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.story.db.processing)return;_self.api.story.db.processing=true;_self.ajax.call(`/api/vhuman/stories/${c.id}/punish`,"POST",true,{},(data)=>{_self.api.story.db.processing=false;c.judged=1;c.jail++;callback?callback(data):0;},(data)=>{_self.api.story.db.processing=false;error?error(data):0;});},},social:{db:{processing:false},post:(blob,filename)=>{if(!_self.loggedIn)return;_self.state.social.new.parent=_self.state.social.new.parent?_self.state.social.new.parent:null;const data={x:_self.state.social.my_gps?_self.state.social.my_gps.x:null,y:_self.state.social.my_gps?_self.state.social.my_gps.y:null,caption:_self.state.social.new.caption,parent:_self.state.social.new.parent,type:_self.state.social.new.type?_self.state.social.new.type:0,hash:_self.state.social.new.hash,playlist_id:_self.state.social.playlist.id,};let fn={0:'recording.mp3',2:'recording.mp3',3:'file.png',};var formData=new FormData();formData.append('data',JSON.stringify(data));if(blob){let file=new File([blob],fn[data.type]);formData.append('file',file);}
_self.api.social.db.processing=true;$.ajax({url:'/api/vhuman/posts',headers:{Authorization:_self.bearer,},type:'POST',data:formData,processData:false,contentType:false,success:function(data){_self.api.social.db.processing=false;let p=null;if(_self.state.social.user.vhuman)p=_self.state.social.user.vhuman.posts;else if(_self.state.social.playlist&&_self.state.social.playlist.posts)p=_self.state.social.playlist.posts;let i=p.findIndex(e=>e.hash==data.hash);if(~i)p[i]=data;else p.unshift(data);_self.state.social.new={};},error:(e)=>{alert(e.responseJSON.message);_self.api.social.db.processing=false;},});},user:(hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/user/${hash}`,"GET",true,null,(data)=>{_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},support:(hash,amount,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/${hash}/give`,"POST",true,{amount:amount},(data)=>{_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},delete:(p,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;if(!confirm("Are you sure?"))return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/posts/${p.hash}`,"DELETE",true,null,(data)=>{_self.api.social.db.processing=false;if(_self.state.social.user.vhuman)_self.state.social.user.vhuman.posts=_self.state.social.user.vhuman.posts.filter(e=>e.hash!=p.hash);else if(_self.state.social.playlist&&_self.state.social.playlist.posts)_self.state.social.playlist.posts=_self.state.social.playlist.posts.filter(e=>e.hash!=p.hash);callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},get:(parent,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/posts${parent? '/'+parent+'/comments' : ''}`,"GET",true,null,(data)=>{_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},comment:(p,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/posts/${p.hash}/comment`,"POST",true,{caption:p.newComment},(data)=>{_self.api.social.db.processing=false;p.comments.unshift(data);p.newComment='';callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},order:(p,step,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/posts/${p.hash}/order`,"PUT",true,{step:step},(data)=>{for(var i in data.updates)
_self.state.social.user.vhuman.posts.forEach(e=>{if(e.hash==i)e.order=data.updates[i];})
_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},updateLocation:(p,callback,error)=>{let x=parseInt(prompt("Enter vPARK X Coord..."));let y=parseInt(prompt("Enter vPARK Y Coord..."));if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/posts/${p.hash}/location`,"PUT",true,{x:x,y:y},(data)=>{p.x=x;p.y=y;_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},updateCaption:(p,callback,error)=>{let x=prompt("Enter new caption...");if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/posts/${p.hash}/caption`,"PUT",true,{caption:x},(data)=>{p.caption=x;_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},like:(p,value,callback,error)=>{if(value==p.like)return;if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/posts/${p.hash}/like`,"POST",true,{value:value},(data)=>{p.like=value;_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},follow:(hash,value,callback,error)=>{if(value==_self.state.social.user.followed)return;if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/${hash}/follow`,"POST",true,{value:value},(data)=>{_self.state.social.user.followed=value;_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},playlist:{get:(hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/playlists/${hash}`,"GET",true,null,(data)=>{_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},ask:(hash,caption,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/playlists/${hash}/ask`,"POST",true,{caption:caption},(data)=>{_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},like:(p,value,callback,error)=>{if(value==p.like)return;if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/playlists/${p.hash}/like`,"POST",true,{value:value},(data)=>{p.like=value;_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},activate:(p,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/playlists/${p.hash}/activate`,"POST",true,null,(data)=>{p.private=true;_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},set:(p,callback,error)=>{if(!confirm("Are you sure?"))return;if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/posts/${p.hash}/playlist`,"POST",true,{playlist_id:p.playlist_id},(data)=>{_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},follow:(p,value,callback,error)=>{if(value&&p.id==_self.state.social.user.listening)return;if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/playlists/${p.hash}/follow`,"POST",true,{value:value},(data)=>{if(value==1){_self.state.social.user.listening=p.id;_self.state.social.user.vhuman.listening=p;}
else _self.state.social.user.listening=null;_self.api.social.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},delete:(p,callback,error)=>{if(!confirm("Are you sure?"))return;if(!_self.loggedIn)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/playlists/${p.hash}`,"DELETE",true,null,(data)=>{_self.api.social.db.processing=false;_self.state.social.user.vhuman.playlists=_self.state.social.user.vhuman.playlists.filter(e=>e.hash!=p.hash);callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},create:(callback,error)=>{if(!confirm("Are you sure?"))return;if(!_self.loggedIn)return;if(!_self.state.social.playlist.new||_self.state.social.playlist.new.length<5)return;if(_self.api.social.db.processing)return;_self.api.social.db.processing=true;_self.ajax.call(`/api/vhuman/playlists`,"POST",true,{title:_self.state.social.playlist.new},(data)=>{_self.api.social.db.processing=false;_self.state.social.user.vhuman.playlists.push(data);_self.state.social.playlist.new=null;callback?callback(data):0;},(data)=>{_self.api.social.db.processing=false;error?error(data):0;});},}},route:{db:{processing:false},mine:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.route.db.processing)return;_self.api.route.db.processing=true;_self.ajax.call(`/api/run/routes/mine`,"GET",true,null,(data)=>{_self.api.route.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.route.db.processing=false;error?error(data):0;});},create:(callback,error)=>{let name=prompt("Enter a new route name");if(!_self.loggedIn)return;if(_self.api.route.db.processing)return;_self.api.route.db.processing=true;_self.ajax.call(`/api/run/routes`,"POST",true,{name:name},(data)=>{_self.api.route.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.route.db.processing=false;error?error(data):0;});},update:(r,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.route.db.processing)return;_self.api.route.db.processing=true;_self.ajax.call(`/api/run/routes/${r.hash}`,"PUT",true,r,(data)=>{_self.api.route.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.route.db.processing=false;error?error(data):0;});},get:(hash,min_dist,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.route.db.processing)return;_self.api.route.db.processing=true;_self.ajax.call(`/api/run/routes/${hash}?min_distance=${min_dist}`,"GET",true,null,(data)=>{_self.api.route.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.route.db.processing=false;error?error(data):0;});},add:(hash,props,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.route.db.processing)return;_self.api.route.db.processing=true;_self.ajax.call(`/api/run/routes/${hash}/add`,"POST",true,{properties:props},(data)=>{_self.api.route.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.route.db.processing=false;error?error(data):0;});},remove:(hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.route.db.processing)return;_self.api.route.db.processing=true;_self.ajax.call(`/api/run/routes/${hash}/remove`,"POST",true,null,(data)=>{_self.api.route.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.route.db.processing=false;error?error(data):0;});},clear:(hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.route.db.processing)return;_self.api.route.db.processing=true;_self.ajax.call(`/api/run/routes/${hash}/clear`,"POST",true,null,(data)=>{_self.api.route.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.route.db.processing=false;error?error(data):0;});},delete:(hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.route.db.processing)return;_self.api.route.db.processing=true;_self.ajax.call(`/api/run/routes/${hash}`,"DELETE",true,null,(data)=>{_self.api.route.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.route.db.processing=false;error?error(data):0;});},},realestate:{db:{processing:false},getMyAccess:(property_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.realestate.processing)return;_self.api.realestate.processing=true;_self.ajax.call(`/api/properties/${property_hash}/access/me`,"GET",true,null,(data)=>{_self.api.realestate.processing=false;callback?callback(data):0;},(data)=>{_self.api.realestate.processing=false;error?error(data):0;});},rent:(property_hash,days,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.realestate.processing)return;days=parseInt(days);_self.api.realestate.processing=true;_self.ajax.call(`/api/properties/${property_hash}/rent`,"POST",true,{days:days},(data)=>{_self.api.realestate.processing=false;_self.api.realestate.db.unit=data.hash;_self.state.world.realestate.access=true;_self.state.world.realestate.success=true;callback?callback(data):0;},(data)=>{_self.api.realestate.processing=false;error?error(data):0;});},editor:{getAssets:(property_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.realestate.db.processing)return;_self.api.realestate.db.processing=true;_self.ajax.call(`/api/properties/${property_hash}/assets`,"GET",true,null,(data)=>{_self.api.realestate.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.realestate.db.processing=false;error?error(data):0;});},updateAssets:(property_hash,elements,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.realestate.db.processing)return;_self.api.realestate.db.processing=true;_self.ajax.call(`/api/properties/${property_hash}/assets`,"PUT",true,{elements:elements},(data)=>{_self.api.realestate.db.processing=false;callback?callback(data):0;if(window.ue4)ue4('editor',{cmd:'refresh',data:elements});},(data)=>{_self.api.realestate.db.processing=false;error?error(data):0;});},},installation:{getInstallables:(element_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.realestate.processing)return;_self.api.realestate.processing=true;_self.ajax.call(`/api/properties/installations/${element_hash}/matched`,"GET",true,null,(data)=>{_self.api.realestate.processing=false;callback?callback(data):0;},(data)=>{_self.api.realestate.processing=false;error?error(data):0;});},install:(bag_hash,element_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.realestate.processing)return;_self.api.realestate.processing=true;_self.ajax.call(`/api/properties/installations/${element_hash}/${bag_hash}`,"PUT",true,null,(data)=>{_self.api.realestate.processing=false;callback?callback(data):0;},(data)=>{_self.api.realestate.processing=false;error?error(data):0;});},setPrice:(element_hash,price,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.realestate.processing)return;_self.api.realestate.processing=true;_self.ajax.call(`/api/properties/installations/${element_hash}/price`,"POST",true,{price:price},(data)=>{_self.api.realestate.processing=false;callback?callback(data):0;},(data)=>{_self.api.realestate.processing=false;error?error(data):0;});},counterfeit:(element_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.realestate.processing)return;_self.api.realestate.processing=true;_self.ajax.call(`/api/properties/installations/${element_hash}/counterfeit`,"POST",true,{},(data)=>{_self.api.realestate.processing=false;callback?callback(data):0;},(data)=>{_self.api.realestate.processing=false;error?error(data):0;});},buy:(element_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.realestate.processing)return;_self.api.realestate.processing=true;_self.ajax.call(`/api/properties/installations/${element_hash}/buy`,"POST",true,{},(data)=>{_self.api.realestate.processing=false;callback?callback(data):0;},(data)=>{_self.api.realestate.processing=false;error?error(data):0;});},remove:(element_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.realestate.processing)return;_self.api.realestate.processing=true;_self.ajax.call(`/api/properties/installations/${element_hash}`,"DELETE",true,null,(data)=>{_self.api.realestate.processing=false;callback?callback(data):0;},(data)=>{_self.api.realestate.processing=false;error?error(data):0;});},},},bio:{db:{processing:false},sleep:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.bio.processing)return;_self.api.bio.processing=true;_self.ajax.call(`/api/user/biology/sleep`,"POST",true,null,(data)=>{_self.api.bio.processing=false;callback?callback(data):0;},(data)=>{_self.api.bio.processing=false;error?error(data):0;});},kill:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.bio.processing)return;if(!confirm("Please confirm you want to let this vHuman die. Various access, goods and real estates may go away. This action is final."))return;_self.api.bio.processing=true;_self.ajax.call(`/api/user/biology/kill`,"POST",true,null,(data)=>{_self.api.bio.processing=false;callback?callback(data):0;},(data)=>{_self.api.bio.processing=false;error?error(data):0;});},wakeup:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.bio.processing)return;_self.api.bio.processing=true;_self.ajax.call(`/api/user/biology/wakeup`,"POST",true,null,(data)=>{_self.api.bio.processing=false;callback?callback(data):0;},(data)=>{_self.api.bio.processing=false;error?error(data):0;});},stamina:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.bio.processing)return;_self.api.bio.processing=true;_self.ajax.call(`/api/user/biology/status`,"GET",true,null,(data)=>{_self.api.bio.processing=false;callback?callback(data):0;},(data)=>{_self.api.bio.processing=false;error?error(data):0;});},},bag:{db:{processing:false},list:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.bag.processing)return;_self.api.bag.processing=true;_self.ajax.call(`/api/user/bag`,"GET",true,null,(data)=>{_self.api.bag.processing=false;callback?callback(data):0;},(data)=>{_self.api.bag.processing=false;error?error(data):0;});},consume:(bag_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.bag.processing)return;_self.api.bag.processing=true;_self.ajax.call(`/api/user/bag/${bag_hash}/consume`,"POST",true,null,(data)=>{_self.api.bag.processing=false;callback?callback(data):0;},(data)=>{_self.api.bag.processing=false;error?error(data):0;});},unequip:(bag_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.bag.processing)return;_self.api.bag.processing=true;_self.ajax.call(`/api/user/bag/${bag_hash}/unequip`,"POST",true,{element:null},(data)=>{_self.api.bag.processing=false;callback?callback(data):0;},(data)=>{_self.api.bag.processing=false;error?error(data):0;});},equip:(bag_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.bag.processing)return;_self.api.bag.processing=true;_self.ajax.call(`/api/user/bag/${bag_hash}/equip`,"POST",true,null,(data)=>{_self.api.bag.processing=false;callback?callback(data):0;},(data)=>{_self.api.bag.processing=false;error?error(data):0;});},delete:(bag_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.bag.processing)return;_self.api.bag.processing=true;_self.ajax.call(`/api/user/bag/${bag_hash}`,"DELETE",true,null,(data)=>{_self.api.bag.processing=false;callback?callback(data):0;},(data)=>{_self.api.bag.processing=false;error?error(data):0;});},},fundraise:{db:{processing:false},mine:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.state.world.fundraise.campaigns=[];_self.ajax.call(`/api/commerce/campaigns/mine`,"GET",true,null,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;_self.state.world.fundraise.campaigns=data;_self.api.nft.mine();},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},availableRoutes:(callback,error)=>{if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.state.world.fundraise.routes=[];_self.ajax.call(`/api/run/routes`,"GET",false,null,(data)=>{_self.api.fundraise.db.processing=false;_self.state.world.fundraise.routes=data;callback?callback(data):0;},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},myFundraisings:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.state.world.fundraise.campaigns=[];_self.ajax.call(`/api/commerce/campaigns/fundraisings/mine`,"GET",true,null,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;_self.state.world.fundraise.campaigns=data;for(d in data)data[d].open=data[d].open?moment.tz(data[d].open,'America/Los_Angeles').toDate():null;},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},list:(callback,error)=>{if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.state.world.fundraise.campaigns=[];_self.ajax.call(`/api/commerce/campaigns/available`,"GET",false,null,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;_self.state.world.fundraise.campaigns=data;_self.api.nft.mine();},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},getCampaign:(hash,callback,error)=>{if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.ajax.call(`/api/commerce/campaigns/${hash}`,"GET",false,null,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;if(!_self.state.world.fundraise)_self.state.world.fundraise={campaign:null,campaigns:[]};_self.state.world.fundraise.campaign=data;},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},getResult:(hash,callback,error)=>{if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.ajax.call(`/api/commerce/campaigns/${hash}/results`,"GET",false,null,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;if(!_self.state.world.fundraise)_self.state.world.fundraise={campaign:{hash:hash},campaigns:[]};_self.state.world.fundraise.results=data;},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},getFundraising:(hash,callback,error,bag=1)=>{if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.ajax.call(`/api/commerce/campaigns/fundraisings/${hash}`,"GET",true,null,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;if(!_self.state.world.fundraise)_self.state.world.fundraise={campaign:null,campaigns:[]};data.costreamers=0;data.pledges=0;for(var i in data.reviews){if(_self.state.user&&data.reviews[i].url_id==_self.state.user.url_id){data.like=data.reviews[i].like;data.pledge=data.reviews[i].pledge;}
data.costreamers+=data.reviews[i].like?data.reviews[i].like:0;data.pledges+=data.reviews[i].pledge>0?1:0;data.reviews[i].x=Math.random();data.reviews[i].y=Math.random();}
_self.state.world.fundraise.campaign=data;if(_self.loggedIn&&bag)_self.api.bag.list((d)=>{_self.state.world.bag.items=d;});_self.scope.$apply();},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},support:(hash,callback,error)=>{if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.ajax.call(`/api/commerce/campaigns/fundraisings/${hash}/join`,"POST",true,{like:_self.state.world.fundraise.campaign.like,pledge:_self.state.world.fundraise.campaign.pledge},(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;_self.api.fundraise.getFundraising(hash,null,null,0);},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},give:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.ajax.call(`/api/commerce/campaigns/fundraisings/${_self.state.world.fundraise.campaign.hash}`,"POST",true,_self.state.world.fundraise.campaign,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;_self.state.world.fundraise.campaign=data;_self.state.world.fundraise.campaign.gave=true;},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},createCampaign:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.ajax.call(`/api/commerce/campaigns`,"POST",true,_self.state.world.fundraise.campaign,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;_self.api.fundraise.mine();_self.state.world.fundraise.campaign={};},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},joinCampaign:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.state.world.fundraise.campaign.timezone=_self.ajax.call(`/api/commerce/campaigns/${_self.state.world.fundraise.campaign.hash}`,"POST",true,_self.state.world.fundraise.campaign,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;_self.state.world.fundraise.campaign={};fbq('track','Purchase',{currency:"USD",value:5});gtag('event','conversion',{'currency':'USD','value':5});gtag('event','conversion',{'send_to':'AW-782748339/LB-ZCL25qO0BELOVn_UC'});mixpanel.track("Joined a race",{"button":"buy"});},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},updateFundraising:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.ajax.call(`/api/commerce/campaigns/fundraisings/${_self.state.world.fundraise.campaign.hash}`,"PUT",true,_self.state.world.fundraise.campaign,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},sponsorCampaign:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.ajax.call(`/api/commerce/campaigns/${_self.state.world.fundraise.campaign.hash}/sponsor`,"POST",true,_self.state.world.fundraise.sponsor,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;_self.state.world.fundraise.sponsor={cap:10000,target:1000};_self.state.world.fundraise.sponsor.gave=true;},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},updateCampaign:(callback,error)=>{if(!_self.loggedIn)return;if(_self.api.fundraise.db.processing)return;_self.api.fundraise.db.processing=true;_self.ajax.call(`/api/commerce/campaigns/${_self.state.world.fundraise.campaign.hash}`,"PUT",true,_self.state.world.fundraise.campaign,(data)=>{_self.api.fundraise.db.processing=false;callback?callback(data):0;_self.state.world.fundraise.campaign={};},(data)=>{_self.api.fundraise.db.processing=false;error?error(data):0;});},},shop:{db:{processing:false},list:(store_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.shop.processing)return;_self.api.shop.processing=true;_self.ajax.call(`/api/commerce/stores/${store_hash}/products`,"GET",true,null,(data)=>{_self.api.shop.processing=false;callback?callback(data):0;_self.state.world.shop.items=data.inventories;_self.state.world.shop.buy=data.inventories.filter(e=>(e.transaction_type&1)>0);_self.state.world.shop.supply=data.inventories.filter(e=>(e.transaction_type&2)>0);_self.state.world.shop.storage=_self.state.world.shop.buy;_self.state.world.shop.bag=data.bags;},(data)=>{_self.api.shop.processing=false;error?error(data):0;});},buyBulk:(items,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.shop.processing)return;_self.api.shop.processing=true;_self.ajax.call(`/api/commerce/order/bulk`,"POST",true,{items:items},(data)=>{_self.api.shop.processing=false;callback?callback(data):0;},(data)=>{_self.api.shop.processing=false;error?error(data):0;});},sell:(items,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.shop.processing)return;_self.api.shop.processing=true;_self.ajax.call(`/api/commerce/sell/bulk`,"POST",true,{items:items},(data)=>{_self.api.shop.processing=false;callback?callback(data):0;},(data)=>{_self.api.shop.processing=false;error?error(data):0;});},store:(store_hash,bag_hash,quantity,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.shop.processing)return;_self.api.shop.processing=true;_self.ajax.call(`/api/commerce/stores/${store_hash}/add/${bag_hash}`,"POST",true,{quantity:quantity},(data)=>{_self.api.shop.processing=false;callback?callback(data):0;},(data)=>{_self.api.shop.processing=false;error?error(data):0;});},takeout:(store_hash,inventory_hash,quantity,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.shop.processing)return;_self.api.shop.processing=true;_self.ajax.call(`/api/commerce/stores/${store_hash}/remove/${inventory_hash}`,"POST",true,{quantity:quantity},(data)=>{_self.api.shop.processing=false;callback?callback(data):0;},(data)=>{_self.api.shop.processing=false;error?error(data):0;});},setPrice:(store_hash,inventory_hash,price,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.shop.processing)return;_self.api.shop.processing=true;_self.ajax.call(`/api/commerce/stores/${store_hash}/price/${inventory_hash}`,"put",true,{price:price},(data)=>{_self.api.shop.processing=false;callback?callback(data):0;},(data)=>{_self.api.shop.processing=false;error?error(data):0;});},setInventoryPrice:(inventory_hash,callback,error)=>{if(!_self.loggedIn)return;if(_self.api.shop.processing)return;var s=prompt("Enter a new price for this item in vD currency.");if(!s||s=='')return;if(!s||isNaN(s)||!Number.isInteger(parseInt(s))){alert("Invalid price");return;}
_self.api.shop.processing=true;_self.ajax.call(`/api/commerce/inventories/${inventory_hash}/price`,"put",true,{price:s},(data)=>{_self.api.shop.processing=false;callback?callback(data):0;},(data)=>{_self.api.shop.processing=false;error?error(data):0;});},},wallet:{deposit:()=>{if(_self.state.amountToDeposit<3)return;if(isNaN(_self.state.amountToDeposit))return;_self.ajax.call("/api/land/order","POST",true,{action:"deposit",total:_self.state.amountToDeposit},(data)=>{location.href=data.result.links.find(e=>e.rel=='approve'&&e.method=="GET").href;});},checkWithdrawal:()=>{_self.ajax.call("/api/profile/payout","GET",true,null,(data)=>{_self.state.payout=data;_self.scope.$apply();});},withdraw:()=>{if(_self.state.amountToWithdraw!==0){if(_self.state.amountToWithdraw<50)return;if(_self.state.amountToWithdraw>1500)return;if(isNaN(_self.state.amountToWithdraw))return;}
_self.ajax.call("/api/profile/payout","POST",true,{amount:_self.state.amountToWithdraw},(data)=>{_self.state.payout=data;_self.scope.$apply();});},loadTransactions:(offset,callback)=>{if(!_self.loggedIn){return;}
if(_self.state.transactions){offset=Math.min(Math.floor(offset / _self.state.transactions.limit)*_self.state.transactions.limit,Math.floor((_self.state.transactions.total-1)/ _self.state.transactions.limit)*_self.state.transactions.limit);offset=Math.max(offset,0);if(offset==_self.state.transactions.offset)return;}
_self.ajax.call("/api/profile/transaction/history/"+offset,"GET",true,null,(data)=>{data.offset=parseInt(data.offset);data.total=parseInt(data.total);data.limit=parseInt(data.limit);_self.state.transactions=data;_self.scope.$apply();callback?callback():0;});},tipalti:{url:()=>{if(!_self.state.user.verified){alert("Please verify your ID first.");return;}
_self.ajax.call("/api/profile/verification/tax/tipalti/url","GET",true,null,(data)=>{window.open(data);_self.state.tipaltiWidgetUrl=data;_self.scope.$apply();});},status:()=>{_self.ajax.call("/api/profile/verification/tax/tipalti/status","GET",true,null,(data)=>{if(data.message=="SUBMITTED"||data.message=="SUBMITTED (TIN_VALIDATED)"){_self.state.user.tax_verified=true;}
else{alert("If you have submitted an entry, your verification in progress. Otherwise please make a submission.");}
_self.scope.$apply();});},},diamond:{db:{processing:false},getStats:(callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/diamond/stats`,"GET",true,null,(data)=>{_self.state.diamond.exchange.stats=data;callback?callback(data):0;},()=>{});},getHistory:(step,callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/diamond/history/price?step=${step}`,"GET",true,null,(data)=>{_self.state.diamond.exchange.history=data;callback?callback(data):0;},()=>{});},getWallet:(callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/diamond/balance`,"GET",true,null,(data)=>{_self.state.diamond.exchange.balance=data;_self.state.user.cash=data.usd;_self.state.user.voken=data.diamonds;callback?callback(data):0;},()=>{});},getBuyEstimate:(usd,room,callback)=>{if(!isPositiveNumeric(usd))return;if(_self.api.wallet.diamond.processing)return;_self.api.wallet.diamond.processing=1;_self.ajax.call(`/api/diamond/buy?usd=${usd}&cd=${room}`,"GET",true,null,(data)=>{_self.api.wallet.diamond.processing=0;if(!data.diamonds)return;_self.state.diamond.exchange.estimate=data;callback?callback(data):0;},()=>{_self.api.wallet.diamond.processing=0;});},getSellEstimate:(diamonds,callback)=>{if(!isPositiveNumeric(diamonds))return;if(_self.api.wallet.diamond.processing)return;_self.api.wallet.diamond.processing=1;_self.ajax.call(`/api/diamond/sell?diamonds=${diamonds}`,"GET",true,null,(data)=>{_self.api.wallet.diamond.processing=0;if(!data.net_income)return;_self.state.diamond.exchange.estimate=data;callback?callback(data):0;},()=>{_self.api.wallet.diamond.processing=0;});},getSendEstimate:(address,diamonds,callback)=>{if(!isPositiveNumeric(diamonds)||!address||address.length<10)return;if(_self.api.wallet.diamond.processing)return;_self.api.wallet.diamond.processing=1;_self.ajax.call(`/api/diamond/send?diamonds=${diamonds}&address=${address}`,"GET",true,null,(data)=>{_self.api.wallet.diamond.processing=0;if(!data.transfer)return;_self.state.diamond.exchange.estimate=data;callback?callback(data):0;},()=>{_self.api.wallet.diamond.processing=0;});},buy:(usd,room,callback)=>{if(!isPositiveNumeric(usd))return;if(_self.api.wallet.diamond.processing)return;_self.api.wallet.diamond.processing=1;_self.state.diamond.exchange.result=null;_self.ajax.call(`/api/diamond/buy`,"POST",true,{usd:usd,cd:room},(data)=>{_self.state.diamond.exchange.result=data;_self.api.wallet.diamond.processing=0;callback?callback(data):0;},()=>{_self.api.wallet.diamond.processing=0;});return{};},sell:(diamonds,callback)=>{if(!isPositiveNumeric(diamonds))return;if(_self.api.wallet.diamond.processing)return;_self.api.wallet.diamond.processing=1;_self.state.diamond.exchange.result=null;_self.ajax.call(`/api/diamond/sell`,"POST",true,{diamonds:diamonds},(data)=>{_self.state.diamond.exchange.result=data;_self.api.wallet.diamond.processing=0;callback?callback(data):0;},()=>{_self.api.wallet.diamond.processing=0;});return{};},send:(address,diamonds,callback)=>{if(!isPositiveNumeric(diamonds)||!address||address.length<10)return;if(_self.api.wallet.diamond.processing)return;_self.api.wallet.diamond.processing=1;_self.state.diamond.exchange.result=null;_self.ajax.call(`/api/diamond/send`,"POST",true,{diamonds:diamonds,address:address},(data)=>{_self.state.diamond.exchange.result=data;_self.api.wallet.diamond.processing=0;callback?callback(data):0;},()=>{_self.api.wallet.diamond.processing=0;});return{};},},},digicard:{search:'',sort:"date",asc:1,filter:'',submitting:false,create:()=>{if(_self.api.digicard.submitting)return;if(!_self.loggedIn)return;if(_self.state.digicards.newCard.royalty<1||_self.state.digicards.newCard.royalty>20)return;if(_self.state.digicards.newCard.title.length<3)return;if(!_self.state.digicards.newCard.agree)return;_self.state.digicards.newCard.makeCDT='make_cdt';var formData=new FormData();let input=$('#digicard_file')[0];if(!input.files.length)return;let tinput=$('#digicard_u')[0];if(!tinput.files.length)return;formData.append('orig',input.files[0]);formData.append('thumbnail',tinput.files[0]);formData.append('code',"cdt");formData.append('title',_self.state.digicards.newCard.title);formData.append('website',_self.state.digicards.newCard.website?_self.state.digicards.newCard.website:'');formData.append('description',_self.state.digicards.newCard.description?_self.state.digicards.newCard.description:'');formData.append('thumb_filetype',tinput.value.split('.').pop().toLowerCase());formData.append('filetype',input.value.split('.').pop().toLowerCase());_self.api.digicard.submitting=true;_self.scope.zoomToSpace();_self.scope.showCreationProgress();gtag('event','conversion',{'currency':'USD','value':1});gtag('event','conversion',{'send_to':'AW-782748339/LB-ZCL25qO0BELOVn_UC'});$.ajax({url:`/api/digicard/card`,headers:{Authorization:_self.bearer,},method:"POST",data:formData,processData:false,contentType:false,success:function(data,textStatus,xhr){_self.api.digicard.submitting=false;fbq('track','SubmitApplication');_self.state.digicards.newCard={royalty:10};$('#digicard_file')[0].value='';$('#digicard_thumb')[0].value='';$("#new_digicard_thumb_prev").css("background","url(/images/icons/dachshund.svg) no-repeat center center / 50%");$("#new_digicard_thumb_prev").css("background-color","transparent");$("#new_digicard_thumbnail").css("background","transparent");if(_self.state.user.my_cdts)_self.state.user.my_cdts.push(data);_self.scope.$apply();_self.scope.hideCreationProgress();alert("Your asset(s) have been created and is available in the artwork section page.");},error:function(data){_self.api.digicard.submitting=false;alert(data.responseJSON.message);}});},delete:(card,callback)=>{if(!confirm("Please confirm you want to delete this card. This action is final."))return;if(_self.api.digicard.submitting)return;_self.api.digicard.submitting=true;_self.ajax.call(`/api/digicard/card/${card.hash}`,"DELETE",true,null,(data)=>{_self.api.digicard.submitting=false;_self.state.user.my_digicards=_self.state.user.my_digicards.filter(e=>e.id!=card.id);let x=$('#digicard_file');if(x&&x.length)x[0].value='';_self.scope.$apply();callback?callback():0;},()=>{_self.api.digicard.submitting=false;});},submitForex:(content)=>{if(!confirm("Please confirm you want to invest at least USD $2,500. We will share your email for ForexAutoPilot to contact you. Depending on your country of residence, SmartX bot brokers might not be able to accommodate you. You'll earn cashback by registering through us, 25% from the bot fee commission SmartX is giving us. Please make sure you have read mission #2 before continuing."))return;_self.ajax.call(`/api/digicard/smartx/${content.id}/submit`,"POST",true,null,(data)=>{alert("Submission successful.");});},buy:(content)=>{if(!confirm("Please confirm you want to buy this digicard. This action is final and non-refundable. Terms and conditions apply."))return;_self.ajax.call(`/api/digicard/card/${content.digicard}/content/${content.id}/buy`,"POST",true,null,(data)=>{alert("Purchase successful.");});},sell:(card)=>{if(!_self.state.loggedIn)return;var s=prompt("Enter a desired minimum price in USD ($10 min) to sell this DigiCard. Once this card is set with a minimum price, you cannot change the price or cancel the listing.");if(!s||s=='')return;if(!s||isNaN(s)||!Number.isInteger(parseInt(s))){alert("Invalid price");return;}
s=parseInt(s);if(s<10){alert("A price of a DigiCard artwork must be at least $10");return;}
_self.ajax.call(`/api/digicard/card/${card.hash}/price`,"PUT",true,{price:s},(data)=>{card.listed=s;_self.scope.$apply();});},like:(card)=>{if(!_self.state.loggedIn)return;_self.ajax.call(`/api/digicard/card/${card.hash}/like`,"POST",true,null,(data)=>{card.likes=data;_self.scope.$apply();});},list:(offset=0,limit=28,callback)=>{_self.state.digicards.showDigicards=true;_self.state.usableDigicards=[];_self.ajax.call(`/api/digicard/cards/${offset}/${limit}?sort=${_self.api.digicard.sort}&sort_asc=${_self.api.digicard.asc? 1:0}&filter=${_self.api.digicard.filter}&search=${_self.api.digicard.search}`,"GET",true,null,(data)=>{_self.state.usableDigicards=data.data;_self.state.db.digicards=data;_self.scope.$apply();});callback?callback():0;},getMine:(callback)=>{if(!_self.state.loggedIn)return;_self.ajax.call(`/api/digicard/cards/mine`,"GET",true,null,(data)=>{_self.state.user.digicards=data.created;_self.state.user.my_digicards=data.purchased;_self.state.user.total_nft_likes=0;data.created.map(e=>{_self.state.user.total_nft_likes+=e.likes;})
data.purchased.map(e=>{_self.state.user.total_nft_likes+=e.likes;})
_self.scope.$apply();callback?callback():null;});}},nft:{search:'',sort:"date",asc:1,filter:'',submitting:false,afropunk:{db:{price:0},getPrice:()=>{_self.ajax.call(`/api/nft/creation/afropunk/price`,"GET",true,null,(data)=>{_self.api.nft.afropunk.db.price=parseInt(data.price);_self.api.nft.afropunk.db.available=parseInt(data.available);});},purchase:(img,callback)=>{if(!_self.loggedIn)return;if(window.afroupdated<10){alert("Please customize the afro punk more.");return-1;}
if(!confirm("Please confirm you want to claim this afro punk. This action is final. Please wait after confirming and do not click the claim button again or you might get charged twice."))return-1;_self.ajax.call(`/api/nft/creation/afropunk/price`,"GET",true,null,(data)=>{if(_self.api.nft.afropunk.db.price!=parseInt(data.price)&&!confirm("Others have also claimed another Afropunk, the price has changed and it now costs $"+parseInt(data.price)))return;_self.api.nft.afropunk.db.price=parseInt(data.price);_self.api.nft.afropunk.db.available=parseInt(data.available);_self.ajax.call(`/api/nft/creation/afropunk/purchase`,"POST",true,null,(data)=>{if(_self.api.digicard.submitting)return;var formData=new FormData();formData.append('orig',dataURItoBlob(img));formData.append('thumbnail',dataURItoBlob(img));formData.append('code',"vpark_nft");formData.append('title',data.title);formData.append('royalty',10);formData.append('website','https://www.vpark.io');formData.append('description','Unique collectible characters with proof of ownership stored on the vPark blockchain. This project is run by vPark.');formData.append('thumb_filetype',"png");formData.append('filetype',"png");_self.api.digicard.submitting=true;_self.scope.zoomToSpace();_self.scope.showCreationProgress(img);$.ajax({url:`/api/digicard/card`,headers:{Authorization:_self.bearer,},method:"POST",data:formData,processData:false,contentType:false,success:function(data,textStatus,xhr){_self.api.digicard.submitting=false;_self.state.digicards.newCard={royalty:10};_self.state.user.my_digicards.push(data);_self.scope.$apply();_self.scope.hideCreationProgress();alert("Your DigiCard has been created and can be seen under the My DigiCard page. Don't forget to set price for your NFT & DigiCard. You can also now join the Afro Punk Club chat in New York City area. Invite your friends to join and share your afropunk on social media.");callback?callback():0;},error:function(data){_self.api.digicard.submitting=false;alert(data.responseJSON.message);}});});});},},get:(hash,callback)=>{_self.ajax.call(`/api/digicard/cdt/${hash}`,"GET",false,null,(data)=>{_self.state.selectedDigicard=data;_self.scope.$apply();callback?callback():0;});},list:(offset=0,limit=28,callback)=>{_self.state.shop.showDigicards=true;_self.state.usableCDTs=[];_self.ajax.call(`/api/digicard/cdt/0/${offset}/${limit}?sort=${_self.api.nft.sort}&sort_asc=${_self.api.nft.asc? 1:0}&filter=${_self.api.nft.filter}&search=${_self.api.nft.search}`,"GET",false,null,(data)=>{_self.state.usableCDTs=data.data;_self.state.db.cdts=data;_self.scope.$apply();callback?callback():0;});},mine:(callback)=>{if(!_self.state.loggedIn)return;_self.ajax.call(`/api/digicard/cdts/mine`,"GET",true,null,(data)=>{_self.state.user.cdts=data.created;_self.state.user.my_cdts=data.purchased;_self.scope.$apply();callback?callback():null;});},sell:(card)=>{if(!_self.state.loggedIn)return;var s=prompt("Enter a desired price in USD ($10 min) to sell this artwork.");if(!s||s=='')return;if(!s||isNaN(s)||!Number.isInteger(parseInt(s))){alert("Invalid price");return;}
s=parseInt(s);if(s<10){alert("A price of a CDT must be at least $10");return;}
_self.ajax.call(`/api/digicard/card/${card.hash}/price`,"PUT",true,{price:s},(data)=>{card.listed=s;_self.scope.$apply();});},delete:(card,callback)=>{if(!confirm("Please confirm you want to delete this CDT. This action is final."))return;if(_self.api.nft.submitting)return;_self.api.nft.submitting=true;_self.ajax.call(`/api/digicard/card/${card.hash}`,"DELETE",true,null,(data)=>{_self.api.nft.submitting=false;_self.state.user.my_cdts=_self.state.user.my_cdts.filter(e=>e.id!=card.id);let x=$('#digicard_file');if(x&&x.length)x[0].value='';_self.scope.$apply();callback?callback():0;},()=>{_self.api.nft.submitting=false;});},buy:(c,callback)=>{if(!confirm("Please confirm you want to buy this collectible digital item. This action is final and non-refundable. Terms and conditions apply."))return;_self.ajax.call(`/api/digicard/cdt/0/${c.hash}/buy`,"POST",true,null,(data)=>{alert("Purchase successful.");callback?callback():0;});},transfer:(c,callback)=>{var rec=prompt("Enter recipient's user hash you want to give this NFT to.");if(!rec||rec.length<10)return;if(!confirm("Please confirm you want to transfer this nft. This action is final and non-refundable. Terms and conditions apply."))return;_self.ajax.call(`/api/digicard/cdt/0/${c.hash}/transfer`,"POST",true,{recipient:rec},(data)=>{alert("Transfer successful.");callback?callback():0;});}},digitalGoods:{search:'',sort:"date",asc:1,filter:'',submitting:false,mine:(callback)=>{if(!_self.state.loggedIn)return;_self.ajax.call(`/api/world/collections/3d/mine`,"GET",true,null,(data)=>{_self.state.user.my_goods=data.purchased;_self.scope.$apply();callback?callback():null;});},sell:(c)=>{if(!_self.state.loggedIn)return;var s=prompt("Enter a desired price in 1000 vD to sell this item.");if(!s||s=='')return;if(!s||isNaN(s)||!Number.isInteger(parseInt(s))){alert("Invalid price");return;}
s=parseInt(s);if(s<10){alert("A price of a digital good must be at least 1000 vD");return;}
_self.ajax.call(`/api/world/collections/3d/${c.hash}/price`,"PUT",true,{price:s},(data)=>{c.listed=s;_self.scope.$apply();});},buy:(c,callback)=>{if(!confirm("Please confirm you want to buy this collectible digital item. This action is final and non-refundable. Terms and conditions apply."))return;_self.ajax.call(`/api/world/collections/3d/${c.hash}/buy`,"POST",true,null,(data)=>{alert("Purchase successful.");callback?callback():0;});},delete:(card,callback)=>{if(!confirm("Please confirm you want to delete this item. This action is final."))return;if(_self.api.digitalGoods.submitting)return;_self.api.digitalGoods.submitting=true;_self.ajax.call(`/api/world/collections/3d/${card.hash}`,"DELETE",true,null,(data)=>{_self.api.digitalGoods.submitting=false;_self.state.user.my_cdts=_self.state.user.my_cdts.filter(e=>e.id!=card.id);let x=$('#digicard_file');if(x&&x.length)x[0].value='';_self.scope.$apply();callback?callback():0;},()=>{_self.api.digitalGoods.submitting=false;});},transfer:(c,callback)=>{var rec=prompt("Enter recipient's user hash you want to give this item to.");if(!rec||rec.length<10)return;if(!confirm("Please confirm you want to transfer this good. This action is final and non-refundable. Terms and conditions apply."))return;_self.ajax.call(`/api/world/collections/3d/${c.hash}/transfer`,"POST",true,{recipient:rec},(data)=>{alert("Transfer successful.");callback?callback():0;});}},land:{getLandPrice:(x,y)=>{_self.ajax.call("/api/land/price/"+x+"/"+y,"GET",true,null,(data)=>{_self.state.landInfo=data;if(_self.state.landInfo.price<=0)alert("Land in this area not available.");});},getCountryPrice:(country)=>{if(_self.state.countryPrices[country])return _self.state.countryPrices[country].price;_self.state.countryPrices[country]={price:0.1};_self.ajax.call("/api/land/price/"+country,"GET",true,null,(data)=>{_self.state.countryPrices[country]=data;_self.scope.$apply();});return 0.1;},setGroupName:(group_id,callback)=>{let newName=prompt("Enter new name");if(newName.length<1)return;_self.ajax.call("/api/land/"+group_id+"/rename","PUT",true,{title:newName},(data)=>{newName=data.title;let g=_self.state.user.lands.find(e=>e.group_id==group_id);if(g)g.title=newName;if(_self.state.selectedGroup&&_self.state.selectedGroup.group_id==group_id)_self.state.selectedGroup.title=newName;_self.scope.$apply();});},showPurchase:(s,group_id,callback,updatemap=true)=>{if(s){let sqrData=_self.scope.getOccupiedSquareData(s);group_id=sqrData.group_id;}
_self.api.land.clearPreparedLands();_self.ajax.call("/api/land/"+group_id,"GET",true,null,(data)=>{_self.state.selectedGroup=data;if(updatemap)
_self.state.pages=["map","land",group_id];_self.scope.$apply();callback?callback():null;});},prepareLand:()=>{if(_self.state.landPrepared)return;if(new Date()-_self.scope.buyLandTick<1000)return;if(_self.state.preparingLand)return;_self.scope.buyLandTick=new Date();_self.state.preparingLand=true;fbq('track','InitiateCheckout',{currency:"USD",value:1});mixpanel.track("Land Prepare",{"button":"Prepare"});_self.ajax.call("/api/land","POST",true,{lands:_self.state.selectedSquares},(data)=>{[data.x,data.y]=_self.state.selectedSquares[0].split("-");_self.state.landPrepared=data;_self.state.selectedGroup=data;_self.state.pages=["map","land",data.group_id];_self.state.preparingLand=false;_self.scope.$apply();},(data)=>{_self.state.landPrepared=true;_self.state.preparingLand=false;_self.api.land.clearPreparedLands();});},buyLand:()=>{if(!_self.state.selectedGroup)return;if(_self.state.landPrepared!==_self.state.selectedGroup&&_self.state.selectedGroup.listed==0)return;if(_self.state.user.hash==_self.state.selectedGroup.buyer_hash)return;if(_self.state.user.cash+_self.state.user.valuation+_self.state.selectedGroup.price<50)alert("This is a courtesy reminder that there is a minimum withdrawal amount and fee associated with it. Your purchase amount and account balance are low, make sure you can sell it above the minimum withdrawal if you intent to sell it soon.");if(!confirm("Are you sure you want to buy this land squares? No cancellation allowed."))return;fbq('track','Purchase',{currency:"USD",value:_self.state.selectedGroup.listed});gtag('event','conversion',{'currency':'USD','value':_self.state.selectedGroup.listed});gtag('event','conversion',{'send_to':'AW-782748339/LB-ZCL25qO0BELOVn_UC'});mixpanel.track("Land Purchase",{"button":"buy"});_self.state.loading=true;_self.ajax.call("/api/land/order","POST",true,{action:_self.state.selectedGroup.group_id,coupon:_self.state.coupon_code},(data)=>{location.href=data.result.links.find(e=>e.rel=='approve'&&e.method=="GET").href;},(data)=>{_self.state.loading=false;_self.scope.$apply();});_self.scope.$apply();},buyLandWithCash:(callback)=>{if(!_self.state.loggedIn){alert("Please login");return;}
if(!_self.state.selectedGroup)return;if(_self.state.landPrepared!==_self.state.selectedGroup&&_self.state.selectedGroup.listed==0)return;if(_self.state.user.hash==_self.state.selectedGroup.buyer_hash)return;if(_self.state.user.cash+_self.state.user.valuation+_self.state.selectedGroup.price<50)alert("This is a courtesy reminder that there is a minimum withdrawal amount and fee associated with it. Your purchase amount and account balance are low, make sure you can sell it above the minimum withdrawal if you intent to sell it soon.");if(_self.state.selectedGroup.stamped&&!confirm("[IMPORTANT NOTICE]\n\nThis land has been stamped by another user, there will be different commission fee and royalty that come along with your future resale. I declare that I am fully aware about the difference between unstamped and stamped lands."))return;if(!confirm("Are you sure you want to buy this land squares? No cancellation allowed."))return;_self.state.loading=true;_self.ajax.call("/api/land/"+_self.state.selectedGroup.group_id,"POST",true,{coupon:_self.state.coupon_code},(data)=>{let pr=_self.state.selectedGroup.bidded_price?_self.state.selectedGroup.bidded_price:_self.state.selectedGroup.listed;fbq('track','Purchase',{currency:"USD",value:pr});gtag('event','conversion',{'currency':'USD','value':pr});gtag('event','conversion',{'send_to':'AW-782748339/LB-ZCL25qO0BELOVn_UC'});mixpanel.track("Land Purchase",{"button":"buy"});_self.state.coupon_code=null;_self.state.loading=false;_self.api.land.showPurchase(null,_self.state.selectedGroup.group_id);_self.scope.clearSelected();_self.scope.clearGridCache();_self.scope.refreshGrid(1);_self.scope.validate(0);callback?callback():null;},(data)=>{_self.state.loading=false;_self.scope.$apply();});},rent:(callback)=>{if(!_self.state.loggedIn)return;if(!_self.state.selectedGroup)return;if(_self.state.landPrepared!==_self.state.selectedGroup&&_self.state.selectedGroup.lease_price==0)return;if(!confirm("Are you sure you want to rent this land group for +30 days?"))return;_self.state.loading=true;_self.ajax.call(`/api/land/${_self.state.selectedGroup.group_id}/rent`,"POST",true,{},(data)=>{_self.state.loading=false;callback?callback():null;},(data)=>{_self.state.loading=false;_self.scope.$apply();});},getAvailableLandTax:(callback)=>{if(!_self.state.loggedIn)return;_self.state.loading=true;_self.ajax.call(`/api/finebruh/landtax`,"GET",true,{},(data)=>{_self.state.loading=false;callback?callback(data):null;},(data)=>{_self.state.loading=false;_self.scope.$apply();});},claimLandTax:(groupid,callback)=>{if(!_self.state.loggedIn)return;_self.state.loading=true;_self.ajax.call(`/api/finebruh/landtax/`+groupid,"POST",true,{},(data)=>{_self.state.loading=false;callback?callback(data):null;},(data)=>{_self.state.loading=false;_self.scope.$apply();});},cutdownTree:(x,y,callback)=>{_self.ajax.call(`/api/land/cutdown/${x}/${y}`,"PUT",true,{},(data)=>{callback?callback(data):null;});},clearPreparedLands:()=>{if(!_self.state.loggedIn){return;}
if(!_self.state.landPrepared)return;_self.state.landPrepared=null;_self.ajax.call("/api/land","DELETE",true,null);},setLand360Url:(land)=>{if(!confirm("You are about to change virtual 360 room url connected to this virtual land. When other users click the view button from the marketplace, they will have an option to visit your virtual room instead of the map. Make sure your virtual 360 room is not expired or deleted. Setting the url to blank will remove this functionality from your land. Please confirm to continue."))return;let url=prompt("Enter a 360 property url found on 360.vpark.io account page.");_self.ajax.call("/api/land/"+land.group_id+"/360/url","PUT",true,{property_url:url},(data)=>{_self.state.user.lands.find(e=>e.group_id==land.group_id).property_url=url;_self.scope.$apply();});},setGroupLeasePrice:(group_id,callback)=>{let price=parseFloat(prompt("Enter a valid vDiamonds you want to charge for rent @ 30 days. You can set the price to 0 to disable future lease."));if(isNaN(price)||(price>0&&price<1)){alert("Invalid price");return;}
let g=_self.state.user.lands.find(e=>e.group_id==group_id);if(g.lease_price==price){return;}
if(g.lease_price==0)
if(prompt("I understand there is a 5% commission fee per lease. You won't be able to build or extend subscription on lands you lease. Please type \"AGREE\" to continue.")!="AGREE")return;_self.ajax.call("/api/land/"+group_id+"/rent/price","PUT",true,{price:price},(data)=>{_self.state.user.lands.find(e=>e.group_id==group_id).lease_price=price;_self.scope.$apply();});},setGroupPrice:(group_id)=>{let price=prompt("Enter a valid total rounded price in USD you want to sell these squares for. The amount for listing should be at least $1. You can set the price to $0 to remove your land from listing.");if(price.length<1)return;if(isNaN(price)||(price>0&&price<1)){alert("Invalid price");return;}
let g=_self.state.user.lands.find(e=>e.group_id==group_id);if(g.listed==price){return;}
if(g.listed==0)
if(prompt("I understand that there "+(g.stamped?"are fees up to 15% for stamped land":"is a 5% commission fee")+" vPark will take from the listed price. I agree that my listing may be cached on multiple servers, and changes may not immediately occur. I understand that any purchases made between my changes are valid and final and that vPark will use the previous state for the transaction. Please type \"AGREE\" to continue.")!="AGREE")return;_self.ajax.call("/api/land/"+group_id+"/price","PUT",true,{price:price},(data)=>{_self.state.user.lands.find(e=>e.group_id==group_id).listed=price;_self.scope.$apply();});},stampGroup:(group_id)=>{if(!confirm("Do you want to stamp this virtual land?"))return;_self.ajax.call("/api/land/"+group_id+"/stamp","POST",true,null,(data)=>{if(prompt("IMPORTANT NOTICE.\nBy stamping this land, you will earn royalties whenever your land is resold where you are neither the buyer nor seller. You will earn 9% royalties for each sale in our 360 properties, or 5% when traded in the marketplace. You will get more royalties in 360 properties since this is a premium feature. vPark's commission fee is 5% for 360 properties and 10% for the marketplace. Stamping land does not give you any privileges or rights to dictate our company's direction or services limitations. If you plan to resell the land from a 360 room, please try building a virtual property from 360.vpark.io to make sure your computer and browser meet the minimum requirement to enter it. You accept the product as-is. Please type \"AGREE\" in capital letters to confirm you accept this. This action is 100% final, nonrefundable, indisputable, and irreversible.")!="AGREE"){return;}
_self.ajax.call("/api/land/"+group_id+"/stamp","PUT",true,null,(data)=>{alert("Congrats! You have stamped your land. You can now display it under Virtual Properties.");});});},loadGroupsOnSale:(x1,y1,x2,y2)=>{if(!_self.loggedIn)return;if(!x1)[x1,y1,x2,y2]=_self.scope.getMapBound();if(_self.scope.lastRandomLandCoords[0]==x1&&_self.scope.lastRandomLandCoords[1]==y1&&_self.scope.lastRandomLandCoords[2]==x2&&_self.scope.lastRandomLandCoords[3]==y2)return;_self.scope.lastRandomLandCoords=[x1,y1,x2,y2];_self.ajax.call(`/api/land/available/${x1}/${y1}/${x2}/${y2}`,"GET",true,null,(data)=>{_self.state.landsOnSaleOnMap=data;if(_self.state.current&&_self.state.current.path=='map/land')_self.state.current.data=data;_self.scope.$apply();});},setGroupOffer:(group_id,idx)=>{let price=prompt("Enter a valid total rounded price in USD you want to sell these squares for. A bid should be at least $1. You can set the offer to $0 to remove your bid. Any changes you made will remove the acceptance or state of your previous offer. User can see your offer for 30 days.");if(!price||price.length<1)return;if(isNaN(price)||(price>0&&price<1)){alert("Invalid price");return;}
_self.ajax.call("/api/land/"+group_id+"/offer","POST",true,{price:price},(data)=>{if(idx){let l=_self.state.purchasableLands.listed[idx];if(l&&l.group_id==group_id)l.my_offer=data.price;}
_self.scope.$apply();});},deleteGroupOffer:(group,idx)=>{if(!confirm("Please confirm you want to delete this bid."))return;_self.ajax.call("/api/land/"+group.group_id+"/offer/"+group.offer_details[idx].hash,"DELETE",true,null,(data)=>{group.offers-=1;group.offer_details.splice(idx,1);_self.scope.$apply();});},acceptGroupOffer:(group,idx)=>{if(!group.listed){alert("You must first list your property on the market before accepting a bid.");return;}
if(!confirm("Are you sure you want to let this user buy your property for $"+group.offer_details[idx].price+"?"))return;_self.ajax.call("/api/land/"+group.group_id+"/offer/"+group.offer_details[idx].hash,"PUT",true,null,(data)=>{group.offer_details[idx].accepted=1;_self.scope.$apply();alert("You've accepted a bid, the bidder can now complete the purchase with the new price.");});},loadGroupOffer:(group)=>{_self.ajax.call("/api/land/"+group.group_id+"/offers","GET",true,null,(data)=>{group.offer_details=data;_self.scope.$apply();});},loadMarket:(sort,offset,callback)=>{if(!_self.state.loggedIn)return;if(_self.state.purchasableLands){offset=Math.min(Math.floor(offset / _self.state.purchasableLands.limit)*_self.state.purchasableLands.limit,Math.floor((_self.state.purchasableLands.available-1)/ _self.state.purchasableLands.limit)*_self.state.purchasableLands.limit);offset=Math.max(offset,0);if(offset==_self.state.purchasableLands.offset&&_self.state.marketSort==_self.state.lastMarketSort&&_self.state.marketCountry==_self.state.lastMarketCountry&&_self.state.marketMaxPrice==_self.state.lastMarketMaxPrice&&_self.state.marketMinPrice==_self.state.lastMarketMinPrice&&_self.state.marketCountry==_self.state.lastMarketCountry&&_self.state.marketOffer==_self.state.lastMarketOffer&&_self.state.marketSearch==_self.state.lastMarketSearch&&_self.state.lastMarketAsc==_self.state.marketAsc){callback?callback():null;return;}}
_self.state.lastMarketSort=sort;_self.state.lastMarketCountry=_self.state.marketCountry;_self.state.lastMarketOffer=_self.state.marketOffer;_self.state.lastMarketMaxPrice=_self.state.marketMaxPrice;_self.state.lastMarketMinPrice=_self.state.marketMinPrice;_self.state.lastMarketAsc=_self.state.marketAsc;_self.state.lastMarketSearch=_self.state.marketSearch;var query="?";if(_self.state.marketOffer)query+="offer=1";if(_self.state.marketMaxPrice&&parseFloat(_self.state.marketMaxPrice)>0)query+="&max_sqr_price="+_self.state.marketMaxPrice;if(_self.state.marketMinPrice&&parseFloat(_self.state.marketMinPrice)>0)query+="&min_sqr_price="+_self.state.marketMinPrice;if(_self.state.marketAsc)query+="&sort_asc=asc";if(_self.state.marketSearch)query+="&search="+encodeURIComponent(_self.state.marketSearch);let url="/api/land/available/"+_self.state.marketCountry+"/"+sort+"/"+offset+query;_self.urls.loadMarket=url;_self.ajax.call(_self.urls.loadMarket,"GET",true,null,(data)=>{if(_self.urls.loadMarket!=url)return;data.offset=parseInt(data.offset);data.available=parseInt(data.available);data.limit=parseInt(data.limit);_self.state.purchasableLands=data;callback?callback():null;_self.scope.$apply();});},},economy:{landtoken:{search:"",start:(land)=>{if(land.fund)return;var s=_self.state.landToken.newTokenAmount;if(!s||s=='')s=prompt("Enter desired tokens for this investment (we recommend 1T - 20T for start). Your property will be open for token raising for 30 days, failure to complete the token gathering will result in this fund being cancelled.");if(!s||s=='')return;if(!s||isNaN(s)||!Number.isInteger(parseInt(s))){alert("Invalid token input");return;}
s=parseInt(s);if(s<1){alert("Invalid token input");return;}
_self.ajax.call(`/api/land/${land.group_id}/investment`,"POST",true,{tokens:s,purpose:_self.state.landToken.newTokenPurpose,type:_self.state.landToken.newTokenType,duration:_self.state.landToken.newTokenDuration},(data)=>{land.fund=data;land.fundStatus="open";_self.scope.$apply();});},list:(offset=0,callback)=>{_self.ajax.call(`/api/land/open/investments?offset=${offset}&search=${_self.api.economy.landtoken.search}`,"GET",true,null,(data)=>{_self.state.landfunds.offset=parseInt(data.offset);_self.state.landfunds.available=parseInt(data.total);_self.state.landfunds.limit=parseInt(data.limit);_self.state.investableLands=data.investments;_self.scope.$apply();callback?callback():0;});},mine:(offset=0,callback)=>{_self.ajax.call(`/api/land/me/investments`,"GET",true,null,(data)=>{_self.state.myInvestedLands=data;_self.scope.$apply();callback?callback():0;});},get:(group_id,callback)=>{_self.ajax.call(`/api/land/${group_id}/investment`,"GET",true,null,(data)=>{callback?callback(data):0;});},getSimple:(group_id,callback)=>{_self.ajax.call(`/api/land/${group_id}/investment/latest/simple`,"GET",true,null,(data)=>{callback?callback(data):0;});},cancel:(land)=>{if(!land.fund)return;if(!confirm("This action is non-cancellable, all tokens will return to the original owners."))return;_self.ajax.call(`/api/land/${land.group_id}/investment`,"DELETE",true,null,(data)=>{land.fund=null;land.fundStatus=null;_self.scope.$apply();});},shop:(land)=>{if(!_self.state.loggedIn)return;_self.api.economy.content.list(land);_self.api.economy.content.ofLand(land);_self.state.shopInvestmentLand=land;},invest:(landfund)=>{if(!_self.state.loggedIn)return;var s=_self.state.landfunds.investingAmount;if(!s||s=='')s=prompt("How many tokens will you put here? This action is final and non-cancellable.");else if(!confirm("This action is non-cancellable, proceed?"))return;if(!s||s=='')return;if(!s||isNaN(s)||!Number.isInteger(parseInt(s))){alert("Invalid token input");return;}
s=parseInt(s);if(s<1){alert("Invalid token input");return;}
_self.ajax.call(`/api/land/${landfund.group_id}/investment/join`,"POST",true,{tokens:s},(data)=>{landfund.invested=(landfund.invested?landfund.invested:0)+s;alert("You've successfully invest in this land.")
_self.scope.$apply();});},},content:{search:'',create:(callback)=>{if(!_self.state.loggedIn)return;if(_self.state.terranews.newContent.tokens<0||_self.state.terranews.newContent.tokens>10000)return;if(_self.state.terranews.newContent.title.length<5)return;if(_self.state.terranews.newContent.content.length<5&&!_self.state.terranews.newContent.file)return;var formData=new FormData();let input=$('#tn_content_file')[0];if(input.files[0])formData.append('file',input.files[0]);formData.append('price',_self.state.terranews.newContent.tokens);formData.append('title',_self.state.terranews.newContent.title);formData.append('content',_self.state.terranews.newContent.content);formData.append('digicard',_self.state.terranews.newContent.digicard?_self.state.terranews.newContent.digicard:null);$.ajax({url:`/api/terranews/contents`,headers:{Authorization:_self.bearer,},method:"POST",data:formData,processData:false,contentType:false,success:function(data,textStatus,xhr){_self.state.terranews.newContent={tokens:1};$('#tn_content_file')[0].value='';_self.state.mycontent.submitted=true;_self.state.user.contents=data;_self.scope.$apply();callback?callback():null;},error:function(data){alert(data.responseJSON.message);}});},delete:(content,callback)=>{if(!_self.state.loggedIn)return;if(!confirm("This action is final, this content will be permanently deleted."))return;_self.ajax.call(`/api/terranews/contents/${content.id}`,"DELETE",true,null,(data)=>{_self.state.user.contents=data;_self.scope.$apply();callback?callback():0;});},setPrice:(content,callback)=>{if(!_self.state.loggedIn||!content)return;var s=prompt("How many tokens do you want to sell this for? This action is final and non-cancellable.");if(!confirm("This action is non-cancellable, proceed?"))return;if(!s||s=='')return;if(!s||isNaN(s)||!Number.isInteger(parseInt(s))){alert("Invalid token input");return;}
s=parseInt(s);if(s<1){alert("Invalid token input");return;}
_self.ajax.call(`/api/terranews/contents/${content.id}/price`,"PUT",true,{price:s},(data)=>{content.price=s;_self.scope.$apply();callback?callback():0;});},buy:(land,content,callback)=>{if(!_self.state.loggedIn||!land||!content)return;if(!confirm("This action is non-cancellable, the remaining tokens on your invested land will be spent to purchase this content."+(content.bidded&&!content.bidded_accepted?"There is a pending negotiation, this purchase will use the original token price set by the content creator.":"")))return;_self.ajax.call(`/api/terranews/contents/${content.id}/purchase`,"POST",true,{group_id:land.group_id},(data)=>{_self.scope.$apply();alert("Purchase successful. If this caption contains a purchasable NFT, it is automatically set to 2x the minimum price. To change the listing price, visit your account -> properties -> select land -> investment page. Thank you for supporting this business.");callback?callback():0;});},list:(land,offset=0,limit=20,callback)=>{if(!_self.state.loggedIn)return;_self.ajax.call(`/api/terranews/contents/purchasable/${land && land.fund? land.fund.id : null}?search=${_self.api.economy.content.search}&limit=${limit}&offset=${offset}`,"GET",true,null,(data)=>{_self.state.purchasableContents=data.data;_self.state.db.content=data;_self.scope.$apply();callback?callback():0;});},ofLand:(land,offset=0,limit=20,callback)=>{if(!_self.state.loggedIn)return;_self.state.purchasedContents=[];_self.ajax.call(`/api/terranews/contents/purchased/${land.group_id}?search=${_self.api.economy.content.search}&limit=${limit}&offset=${offset}`,"GET",true,null,(data)=>{_self.state.purchasedContents=data.data;_self.state.db.purchasedContent=data;_self.scope.$apply();callback?callback():0;});},ofInvestment:(investment,offset=0,limit=20,callback)=>{if(!_self.state.loggedIn)return;_self.state.db.investedContent=[];_self.ajax.call(`/api/terranews/contents/invested/${investment.id}?search=${_self.api.economy.content.search}&limit=${limit}&offset=${offset}`,"GET",true,null,(data)=>{_self.state.db.investedContent=data;_self.scope.$apply();callback?callback():0;});},loadBids:(content)=>{if(!_self.state.loggedIn||!content)return;_self.ajax.call(`/api/terranews/contents/${content.id}/bids`,"GET",true,null,(data)=>{content.bids=data;_self.scope.$apply();});},digicard:{setPrice:(content)=>{if(!_self.state.loggedIn||!content||!content.digicard)return;var s=prompt(`Enter desired price to sell this Digicard. $${content.digicard_min_price} (pre-fee) of sales will be given to the Digicard seller and ${content.digicard_royalty*100+2.5}% of the remaining will be deducted as vPark fee and royalty to the original artist. The rest will be distributed to you, your token investors, and the creator of this content. You can enter 0 to hide the content from TerraNews.`);if(!s||s=='')return;if(!s||isNaN(s)||!Number.isInteger(parseInt(s))){alert("Invalid price input");return;}
s=parseInt(s);if(s!=0&&s{content.digicard_price=s;_self.scope.$apply();});},buy:(content)=>{if(!_self.state.loggedIn||!content)return;if(content.attachment&&!confirm("This content contains a meme, please be mindful that you are purchasing the DigiCard with NFT, not the Meme."))return;if(!confirm("This action is final, in order to resell this asset, you must list them through vPark Land investment."))return;_self.ajax.call(`/api/digicard/card/${content.digicard}/content/${content.id}/buy`,"POST",true,null,(data)=>{alert("Congrats!");});},},approveBid:(content,bid)=>{if(!_self.state.loggedIn||!content)return;_self.ajax.call(`/api/terranews/contents/${content.id}/bid/${bid.investment_id}/approve`,"POST",true,null,(data)=>{bid.accepted=true;_self.scope.$apply();});},counterBid:(content,bid)=>{if(!_self.state.loggedIn||!bid||!content)return;var s=prompt("Enter desired tokens to counter this presented price.");if(!s||s=='')return;if(!s||isNaN(s)||!Number.isInteger(parseInt(s))){alert("Invalid token input");return;}
s=parseInt(s);if(s<0){alert("Invalid token input");return;}
_self.ajax.call(`/api/terranews/contents/${content.id}/bid/${bid.investment_id}`,"POST",true,null,(data)=>{content.bidded=s;_self.scope.$apply();alert("New price request submitted.");});},rejectBid:(content,bid)=>{if(!_self.state.loggedIn||!content)return;_self.ajax.call(`/api/terranews/contents/${content.id}/bid/${bid.investment_id}/reject`,"POST",true,null,(data)=>{content.bids=content.bids.filter(function(el){return el.id!=bid.id;});content.offers--;_self.scope.$apply();});},negotiate:(land,content)=>{if(!_self.state.loggedIn||!land||!content)return;var s=prompt("Enter desired tokens for this purchase (1 token minimum).");if(!s||s=='')return;if(!s||isNaN(s)||!Number.isInteger(parseInt(s))){alert("Invalid token input");return;}
s=parseInt(s);if(s<1){alert("Invalid token input");return;}
_self.ajax.call(`/api/terranews/contents/${content.id}/bid/${land.fund.id}`,"POST",true,{price:s},(data)=>{content.bidded=s;_self.scope.$apply();alert("New price request submitted.");});},}},terranews:{loadPollResult:(content)=>{if(!_self.state.loggedIn)return;_self.ajax.call("/api/terranews/contents/"+content.id+"/polls","GET",true,null,(data)=>{for(var i in content.polls){let p=content.polls[i];for(var x in data){let r=data[x];if(r.id==p.id){content.hasAnswers=r.response!=null;p.response=r.response;p.results=r.results;p.answer=p.response?p.response.option_id:null;}}}
_self.scope.$apply();});},loadWaitlist:()=>{if(!_self.loggedIn)return;_self.ajax.call("/api/terratalk/waitlist/terratalk","GET",true,{},(data)=>{_self.state.terranews['waitlist']=data;_self.scope.$apply();});},joinWaitlist:()=>{_self.ajax.call("/api/terratalk/waitlist/terratalk","POST",true,{},(data)=>{_self.api.terranews.loadWaitlist();_self.scope.$apply();});},loadMyContent:(callback)=>{_self.ajax.call("/api/terranews/contents/mine","GET",true,{},(data)=>{_self.state.user.contents=data;_self.scope.$apply();callback?callback():0;});},loadContents:(x1,y1,x2,y2)=>{_self.ajax.call(`/api/terranews/contents/${x1}/${y1}/${x2}/${y2}?offset=0`,"GET",true,{},(data)=>{_self.state.terranews['contents']=data;if(_self.state.current&&_self.state.current.path=='map/news')_self.state.current.data=data;var q=true;for(var i in data)if(data[i].type==2){_self.api.terranews.loadPollResult(data[i]);q=false;}
if(q)_self.scope.$apply();});},loadDigicards:(x1,y1,x2,y2)=>{_self.ajax.call(`/api/terranews/digicards/${x1}/${y1}/${x2}/${y2}?offset=0`,"GET",true,{},(data)=>{_self.state.terranews['digicards']=data;if(_self.state.current&&_self.state.current.path=='map/digicard')_self.state.current.data=data;});},loadAds:(x1,y1,x2,y2)=>{_self.ajax.call(`/api/terranews/ads/${x1}/${y1}/${x2}/${y2}?offset=0`,"GET",true,{},(data)=>{_self.state.terranews['ads']=data;_self.scope.$apply();});_self.api.terranews.loadScans(x1,y1,x2,y2);},loadingScans:false,loadScans:(x1,y1,x2,y2)=>{if(_self.api.terranews.loadingScans)return;_self.api.terranews.loadingScans=true;_self.ajax.call(`/api/terranews/scans/${x1}/${y1}/${x2}/${y2}?offset=0`,"GET",true,{},(data)=>{_self.state.terranews['scans']=data.scans;_self.api.terranews.loadingScans=false;_self.scope.$apply();},()=>{_self.api.terranews.loadingScans=false;});},likeContent:(content,upvote)=>{_self.ajax.call(`/api/terranews/contents/${content.id}/vote/${upvote}`,"POST",true,{},(data)=>{content.likes=data;_self.scope.$apply();});},pollSubmit:(content)=>{if(!_self.state.loggedIn)return;var answers={};for(var i in content.polls){if(!content.polls[i].answer)return;answers[content.polls[i].id]=content.polls[i].answer==0?content.polls[i].new_answer:content.polls[i].answer;if(!answers[content.polls[i].id]||answers[content.polls[i].id]=='')return;}
_self.ajax.call("/api/terranews/contents/"+content.id+"/polls","POST",true,answers,(data)=>{_self.api.terranews.loadPollResult(content);_self.scope.$apply();});},},landchat:{loadUnread:()=>{if(!_self.loggedIn)return;_self.ajax.call("/api/landchat/unread","GET",true,{},(data)=>{_self.state.landchats.unread=data.unread;});},loadStatus:(group_code,v)=>{if(!_self.loggedIn)return;_self.ajax.call("/api/landchat/waitlist/general","GET",true,{},(data)=>{_self.state.landchats[group_code]=data;_self.api.landchat.loadContents(group_code,0);});},loadContents:(group_code,thread)=>{if(!_self.state.loggedIn)return;_self.api.landchat.loadUnread();let preQ=_self.state.landchats["contents"]?_self.state.landchats["contents"].length:0;_self.ajax.call("/api/landchat/contents/"+group_code+"/"+thread,"GET",true,{},(data)=>{if(_self.state.landchatGroup!=group_code)return;_self.state.landchats["contents"]=data.length?data:null;_self.scope.$apply();if(data&&preQ!=data.length){setTimeout(()=>{_self.scope.LCScrollDown();},1000);}},(e)=>{_self.state.landchats["contents"]=null;_self.state.showLandchat=false;_self.scope.$apply();});},postContents:(group_code,thread,content)=>{if(!_self.state.loggedIn)return;if(!content||content.length<1)return;_self.ajax.call("/api/landchat/contents/"+group_code+"/"+thread,"POST",true,{content:content,thread:thread,group_code:group_code,},(data)=>{data.hash=_self.state.user.hash;data.name=_self.state.user.name;_self.state.landchats["contents"].unshift(data);_self.state.landchatNewContent="";_self.scope.$apply();_self.scope.LCScrollDown();});},joinWaitlist:(group_code)=>{if(!_self.state.loggedIn)return;if(_self.state.user.voting_powers[group_code]==0){alert("More square ownership is needed to join this chat");return;}
_self.ajax.call("/api/landchat/waitlist/general","POST",true,{},(data)=>{_self.api.landchat.loadStatus(group_code);_self.scope.$apply();});},},v360:{db:{},createRoom:(room_hash,callback)=>{if(!_self.loggedIn)return;if(_self.api.v360.creating)return;_self.api.v360.creating=true;_self.ajax.call("/api/360/rooms/"+room_hash,"POST",true,{},(data)=>{_self.api.v360.db[room_hash]=data;_self.api.v360.creating=false;callback?callback():0;},()=>{_self.api.v360.creating=false;});},getRoom:(room_hash,callback)=>{if(!_self.loggedIn)return;_self.ajax.call("/api/360/rooms/"+room_hash,"GET",true,{},(data)=>{_self.api.v360.db[room_hash]=data;callback?callback():0;});},},store:{db:{},create:(callback)=>{if(!_self.loggedIn)return;if(_self.api.store.creating)return;_self.api.store.creating=true;_self.ajax.call(`/api/commerce/stores`,"POST",true,{},(data)=>{_self.api.store.creating=false;callback?callback(data):0;},()=>{_self.api.store.creating=false;});},getStores:(callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/commerce/stores`,"GET",true,{},(data)=>{callback?callback(data):0;},()=>{});},getStore:(store_hash,callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/commerce/stores/${store_hash}`,"GET",true,{},(data)=>{callback?callback(data):0;},()=>{});},save:(store,callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/commerce/stores/${store.hash}`,"PUT",true,store,(data)=>{callback?callback(data):0;},()=>{});},addProduct:(store,callback)=>{if(!_self.loggedIn)return;if(_self.api.store.creating)return;_self.api.store.creating=true;_self.ajax.call(`/api/commerce/stores/${store.hash}/products`,"POST",true,{},(data)=>{_self.api.store.creating=false;callback?callback(data):0;},()=>{_self.api.store.creating=false;});},getProducts:(store,callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/commerce/stores/${store.hash}/products`,"GET",true,{},(data)=>{callback?callback(data):0;},()=>{});},getProduct:(product_hash,callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/commerce/products/${product_hash}`,"GET",true,{},(data)=>{callback?callback(data):0;},()=>{});},deleteProduct:(product_hash,callback)=>{},saveProduct:(product,callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/commerce/products/${product_hash}`,"PUT",true,product,(data)=>{callback?callback(data):0;},()=>{});},},property:{db:{},get:(property_hash,callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/properties/${property_hash}/detail`,"GET",true,{},(data)=>{callback?callback(data):0;},()=>{});},getSetting:(property_hash,callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/properties/${property_hash}/setting`,"GET",true,{},(data)=>{callback?callback(data):0;},()=>{});},update:(property,callback)=>{if(!_self.loggedIn)return;_self.ajax.call(`/api/properties/${property.hash}`,"PUT",true,{},(data)=>{callback?callback(data):0;},()=>{});},item:{db:{},update:(property_hash,item,callback)=>{if(!_self.loggedIn)return;if(_self.api.property.item.creating)return;_self.api.property.item.creating=true;_self.ajax.call(`/api/properties/${property_hash}/items/${item.id}`,"PUT",true,item,(data)=>{_self.api.property.item.creating=false;callback?callback(data):0;},()=>{_self.api.property.item.creating=false;});},},},landsquare:{db:{},createRoom:(sqr,group_id,x,y,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;if(!confirm("You are about to build a vPARK 360 Room (360.vpark.io) on this land square. The property will have 360 surrounding the environment, You can only change the 360 background to default 360s on vpark or to those taken around you land location and uploaded to vPARK by other users. I understand that it also costs money to build and demolish a property associated to each land square. This action is final."))return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/${group_id}/square/${x}/${y}/build/360room`,"POST",true,{},(data)=>{sqr.property=data.property_hash;sqr.type=data.type;_self.api.landsquare.creating=false;alert("Building property successful.");callback?callback():0;},()=>{_self.api.landsquare.creating=false;});},build:(x,y,project,month,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build`,"POST",true,{project:project,unit:month},(data)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat="Building property successful.";_self.state.selectedToBuild=null;let sqr=_self.state.selectedREToBuild;sqr.type=data.type;sqr.address=data.hash;sqr.re_type=data.type;sqr.builder=_self.state.user.hash;_self.state.selectedREToBuild=null;callback?callback(data):0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},renew:(hash,month,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/properties/${hash}/renew`,"POST",true,{unit:month},(data)=>{_self.api.landsquare.creating=false;callback?callback(data):0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},createStreetLight:(x,y,str,val,url,scale,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build/streetlight`,"POST",true,{text:str,value:val,scale:scale,url:url},(data)=>{_self.api.landsquare.creating=false;ue4('reloadRealEstates');ue4('closeWindow');_self.api.landsquare.db.stat="Building property successful.";callback?callback():0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},rotateRing:(hash,address,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/properties/${hash}/ring/face`,"PUT",true,{address:address},(data)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat="Building modification successful.";callback?callback(data):0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},setRingInteraction:(hash,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/properties/${hash}/ring/interaction`,"PUT",true,null,(data)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat="Building modification successful.";callback?callback(data):0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},setTeleport:(hash,address,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/properties/${hash}/teleport/destination`,"PUT",true,null,(data)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat="Building modification successful.";callback?callback(data):0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},setTalkshow:(hash,address,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/properties/${hash}/theater/playlist`,"PUT",true,{address:address},(data)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat="Building modification successful.";callback?callback(data):0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},createRing:(x,y,str,val,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build/ring`,"POST",true,{text:str,value:val},(data)=>{_self.api.landsquare.creating=false;ue4('reloadRealEstates');ue4('closeWindow');_self.api.landsquare.db.stat="Building property successful.";callback?callback():0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},createBrainStation:(x,y,str,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build/brain`,"POST",true,{text:str},(data)=>{_self.api.landsquare.creating=false;ue4('reloadRealEstates');ue4('closeWindow');_self.api.landsquare.db.stat="Building property successful.";callback?callback():0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},createHouse:(x,y,str,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build/house`,"POST",true,{text:str},(data)=>{_self.api.landsquare.creating=false;ue4('reloadRealEstates');ue4('closeWindow');_self.api.landsquare.db.stat="Building property successful.";callback?callback():0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},createSmallHouse:(x,y,str,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build/smallhouse`,"POST",true,{text:str},(data)=>{_self.api.landsquare.creating=false;ue4('reloadRealEstates');ue4('closeWindow');_self.api.landsquare.db.stat="Building property successful.";callback?callback():0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},createRestaurant:(x,y,str,builder,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build/restaurant`,"POST",true,{text:str,builder:builder},(data)=>{_self.api.landsquare.creating=false;ue4('reloadRealEstates');ue4('closeWindow');_self.api.landsquare.db.stat="Building property successful.";callback?callback():0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},createTower:(x,y,str,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build/tower`,"POST",true,{text:str},(data)=>{_self.api.landsquare.creating=false;ue4('reloadRealEstates');ue4('closeWindow');_self.api.landsquare.db.stat="Building property successful.";callback?callback():0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},create360Theater:(x,y,str,url,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build/360theater`,"POST",true,{text:str,url:url},(data)=>{_self.api.landsquare.creating=false;ue4('reloadRealEstates');ue4('closeWindow');_self.api.landsquare.db.stat="Building property successful.";callback?callback():0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},createCommerceBuilding:(x,y,str,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build/commerce`,"POST",true,{text:str},(data)=>{_self.api.landsquare.creating=false;ue4('reloadRealEstates');ue4('closeWindow');_self.api.landsquare.db.stat="Building property successful.";callback?callback():0;},(err)=>{_self.api.landsquare.creating=false;_self.api.landsquare.db.stat=err;});},createCustomProperty:(x,y,type,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build/produce`,"POST",true,{type:type},(data)=>{_self.api.landsquare.creating=false;ue4('reloadRealEstates');ue4('closeWindow');alert("Building property successful.");callback?callback():0;},()=>{_self.api.landsquare.creating=false;});},createTeleport:(x,y,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/any/square/${x}/${y}/build/teleport`,"POST",true,{},(data)=>{_self.api.landsquare.creating=false;ue4('reloadRealEstates');ue4('closeWindow');alert("Building property successful.");callback?callback():0;},()=>{_self.api.landsquare.creating=false;});},createGallery:(sqr,group_id,x,y,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;if(!confirm("You are about to build a HD Gallery (still on development but open for presale) on this land square. The property will have 360 surrounding the environment, if there is no 360 taken around your land you cannot set what 360 your viewers will see. The 360s will either be dynamic or static. I understand that it also costs money to build and demolish a property associated to each land square. This action is final."))return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/${group_id}/square/${x}/${y}/build/gallery`,"POST",true,{},(data)=>{sqr.property=data.property_hash;sqr.type=data.type;_self.api.landsquare.creating=false;alert("Building property successful.");callback?callback():0;},()=>{_self.api.landsquare.creating=false;});},create360Island:(sqr,group_id,x,y,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.creating)return;if(!confirm("You are about to build a 360 Island (still on development but open for presale) on this land square. To create a 360 land, you need to provide or reuse other's 360 videos. Your 360s on AWS or other hosting services, or contact us if you want us to host your media. I understand that it also costs money to build and demolish a property associated to each land square. This action is final."))return;_self.api.landsquare.creating=true;_self.ajax.call(`/api/land/${group_id}/square/${x}/${y}/build/360island`,"POST",true,{},(data)=>{sqr.property=data.property_hash;sqr.type=data.type;_self.api.landsquare.creating=false;alert("Building property successful.");callback?callback():0;},()=>{_self.api.landsquare.creating=false;});},destroyProperty:(sqr,callback)=>{if(!_self.loggedIn)return;if(_self.api.landsquare.deleting)return;if(!confirm("Demolishing this property costs $5 per square. This action is final and non-refundable. You will lose access to the property you built."))return;_self.api.landsquare.deleting=true;_self.ajax.call(`/api/properties/${sqr.coord.property}/demolish`,"DELETE",true,{},(data)=>{sqr.property=null;sqr.coord.property=null;sqr.coord.type=0;_self.api.landsquare.deleting=false;alert("Demolishing property successful.");callback?callback():0;},()=>{_self.api.landsquare.deleting=false;});},},}}
return _self;});let planetSizeMult=0.03*300;let planetMult=160;let VP_360_URL="https://virtualpark.s3.amazonaws.com/static";let resizeC=0;Number.prototype.clamp=function(min,max){return Math.min(Math.max(this,min),max);};function setOpacity(obj,opacity){if(!obj)return;obj.traverse(child=>{if(child instanceof THREE.Mesh){child.material.opacity=opacity;if(child.material.uniforms&&child.material.uniforms.opacity)child.material.uniforms.opacity.value=opacity;}});}
var customMaterialAtmosphere=new THREE.ShaderMaterial({uniforms:{"c":{type:"f",value:0.73},"p":{type:"f",value:4.0}},blending:THREE.AdditiveBlending,depthTest:true,depthWrite:false,transparent:true,vertexShader:["varying vec3 vNormal;","void main() ","{"," vNormal = normalize( normalMatrix * normal );"," gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );","}",].join("\n"),fragmentShader:["uniform float c;","uniform float p;","varying vec3 vNormal;","void main() ","{"," float intensity = pow( c - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) ), p ); "," gl_FragColor = vec4( 1.0, 0.8, 0.4, 0.5 ) * intensity;","}"].join("\n")});var earthAtmosphere=new THREE.ShaderMaterial({uniforms:{"c":{type:"f",value:0.76},"p":{type:"f",value:6.0},"lightPos":{type:"v3",value:new THREE.Vector3()}},blending:THREE.AdditiveBlending,depthTest:true,depthWrite:false,transparent:true,vertexShader:["varying vec3 vNormal;","varying vec3 vertPos;","uniform vec3 lightPos;","void main() ","{"," vNormal = normalize( normalMatrix * normal );"," vec4 vertPos4 = modelViewMatrix * vec4( lightPos - position, 1.0 );"," vertPos = vertPos4.xyz;"," gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );","}",].join("\n"),fragmentShader:["uniform float c;","uniform float p;","varying vec3 vNormal;","varying vec3 vertPos;","void main() ","{","vec3 N = normalize(vNormal);","vec3 L = normalize(vertPos);"," float lambertian = dot(N, L);"," lambertian = (lambertian < 0.0 ? lambertian / 2.0: lambertian) + 0.3;"," lambertian = min(max(lambertian, 0.0), 1.0);","float specular = 0.0;"," float intensity = pow( c - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) ), p ); "," gl_FragColor = vec4( 0.52, 0.80, 0.92, 1.0 ) * intensity * lambertian;","}"].join("\n")});var sunFlareMaterial=new THREE.ShaderMaterial({uniforms:{"iChannel0":{type:"t",value:null},"iTime":{type:"f",value:0},"iResolution":{type:"v2",value:new THREE.Vector2($("#3d-container").width(),$("#3d-container").height())},},blending:THREE.AdditiveBlending,depthTest:true,depthWrite:false,transparent:true,vertexShader:["varying vec2 uv2;","void main() ","{"," uv2 = uv;"," gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );","}",].join("\n"),fragmentShader:["uniform float c;","varying vec2 uv2;","uniform vec2 iResolution;","uniform float iTime;","uniform sampler2D iChannel0;","void main() ","{"," vec2 I = uv2;"," vec4 O = vec4(0);"," vec2 P = (I-.5)*3.5/1.0;"," for(float i = 1.;i<1.7;i+=.01)"," {"," vec3 R = vec3(P,sqrt(i-dot(P,P)));"," R.xy *= mat2(cos(i*.1),sin(i*.1),sin(i*.1),-cos(i*.1));"," O += .0025*pow(texture2D(iChannel0,R.xy/sqrt(R.z)-.1*iTime),vec4(i*4.-3.));"," }"," O *= vec4(.65,.4,.25,1)/(abs(dot(P,P)-1.)+.2),pow(1.-sqrt(max(1.-dot(P,P),0.)),2.);"," float D = 1.2-length(P+P.y*.4+.1*cos(P.x*6.+2.2*iTime));"," O += vec4(2,1,.5,2)*(exp(-dot(P,P))*1.5 +"," .2*smoothstep(.8,.3,cos(iTime/8.+P.x+P.y*.4))*(cos(P.y*8.+iTime*2.)*.3+.7)*exp(cos(P.x+P.y*.4+iTime*2.)"," -10.*abs((cos(D*38.+P.x*17.+10.*iTime)*.1+.9)*D)));"," gl_FragColor = O;","}"].join("\n")});function createPlanetMaterial(texture,lightposition){return new THREE.ShaderMaterial({transparent:true,vertexShader:['precision highp float;','precision highp int;','varying vec2 vUv;',"varying vec3 vNormal;","varying vec3 vertPos;","uniform vec3 lightPos;",'void main() {',' vUv = uv;'," vNormal = normalize( normalMatrix * normal );"," vec4 vertPos4 = modelViewMatrix * vec4( lightPos - position, 1.0 );"," vertPos = vertPos4.xyz;",' gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);','}'].join('\n'),fragmentShader:['precision highp float;','precision highp int;','varying vec2 vUv;','varying float dis;','uniform sampler2D image;','uniform float opacity;',"varying vec3 vNormal;","varying vec3 vertPos;",'void main() {'," vec3 N = normalize(vNormal);"," vec3 L = normalize(vertPos);"," float lambertian = dot(N, L);"," lambertian = (lambertian < 0.0 ? lambertian / 2.0: lambertian) + 0.3;"," lambertian = min(max(lambertian, 0.0), 1.0);",' vec3 color = texture2D( image, vec2(vUv.x,vUv.y) ).xyz * lambertian;',' gl_FragColor = vec4(color.xyz, opacity);','}'].join('\n'),uniforms:{lightPos:{type:'v3',value:lightposition},opacity:{type:'f',value:1},image:{type:'t',value:texture},}});}
let VP_STATE_MAIN=0;let VP_STATE_CREATION=0;class State{constructor(gl){this.gl=gl;this.load();}
load(){;}
render(){;}
unload(){;}
resize(){}
mousemove(mouse){}
call(fn,params){return this[fn](params);}
camera=null;setupControl=()=>{this.controls=new THREE.OrbitControls(this.camera,this.gl.renderer.domElement);this.controls.enableDamping=true;this.controls.dampingFactor=0.1;this.controls.minPolarAngle=Math.PI*.2;this.controls.maxPolarAngle=Math.PI*.8;this.controls.minDistance=0;this.controls.maxDistance=70;this.controls.maxZoom=3;this.controls.minZoom=.015;this.controls.zoomSpeed=0.5;this.controls.enableZoom=true;this.controls.enablePan=false;this.controls.rotateSpeed=-1;this.controls.autoRotate=false;this.controls.autoRotateSpeed=.5;}}
class SpaceSafari extends State{resize(){let w=$("#3d-container").width();let h=$("#3d-container").height();if(this.outerspaceCamera){this.outerspaceCamera.aspect=w / h;this.outerspaceCamera.updateProjectionMatrix();}
this.camera.aspect=w / h;this.camera.updateProjectionMatrix();sunFlareMaterial.uniforms['iResolution'].value=new THREE.Vector2(w,h);this.composer.setSize(w,h);}
load(){if(this.loaded)return;this.loaded=true;this.setupCamera();this.setupRenderer();this.setupScene();}
setupRenderer(){this.composer=new THREE.EffectComposer(this.gl.renderer);this.outerspaceScene=new THREE.Scene();this.outerspaceScene.background=null;this.outerspaceRenderPass=new THREE.RenderPass(this.outerspaceScene,this.outerspaceCamera);this.outerspaceRenderPass.renderToScreen=false;this.composer.addPass(this.outerspaceRenderPass);this.renderPass=new THREE.RenderPass(this.scene,this.camera);this.renderPass.clearDepth=true;this.composer.addPass(this.renderPass);}
setupCamera(){let w=$("#3d-container").width();let h=$("#3d-container").height();this.fov=Math.floor(this.gl.wfov / window.innerWidth*window.innerHeight);this.camera=new THREE.OrthographicCamera(w / h*this.gl.orthoHeight /-2,w / h*this.gl.orthoHeight / 2,this.gl.orthoHeight / 2,this.gl.orthoHeight /-2,-200000,200000);this.outerspaceCamera=new THREE.OrthographicCamera(w / h*4000 /-2,w / h*4000 / 2,4000 / 2,4000 /-2,-200000,200000);this.camera.fov=(Math.atan(Math.tan(((this.gl.wfov / 2)*Math.PI)/ 180)/ this.camera.aspect)*2*180)/ Math.PI;this.setupControl();}
setupScene(){var planetDist=40*planetMult;var planetAngle=-Math.PI/2;this.scene=new THREE.Scene();this.scene.background=null;this.ambientLight=new THREE.AmbientLight(0x111111,0.1);this.scene.add(this.ambientLight);this.light=new THREE.PointLight(0xe9a675,2,200);this.light.castShadow=true;this.light.shadow.mapSize.width=1024;this.light.shadow.mapSize.height=1024;this.light.shadow.camera.near=0.1;this.light.shadow.camera.far=100000;this.light.shadow.camera.fov=120;this.light.position.set(-18,25.5,18);this.scene.add(this.light);this.camera.position.set(0,1.7,1.5);this.controls.target.set(0,1.7,1.4);this.outerspace=new THREE.Mesh(new THREE.SphereGeometry(5000,32,32),new THREE.MeshBasicMaterial({side:THREE.BackSide,map:(new THREE.TextureLoader()).load(VP_360_URL+"/images/outerspace-thumb.jpg"),color:0x111111}));this.outerspaceScene.add(this.outerspace);var tex=(new THREE.TextureLoader()).load(""+"/images/solars/sun2.png");tex=(new THREE.TextureLoader()).load(VP_360_URL+"/images/earth-thumb.jpg");tex.encoding=THREE.sRGBEncoding;this.sun=new THREE.Mesh(new THREE.SphereGeometry(1000,128,128),new THREE.MeshBasicMaterial({side:THREE.FrontSide,map:tex}));planetAngle=1;this.sun.position.x=Math.sin(planetAngle)*planetDist;this.sun.position.z=Math.cos(planetAngle)*planetDist;this.sun.renderOrder=990;this.scene.add(this.sun);var geometry=new THREE.SphereGeometry(500,256,256);tex=(new THREE.TextureLoader()).load(VP_360_URL+"/images/earth-thumb.jpg");tex.encoding=THREE.sRGBEncoding;this.earth=new THREE.Mesh(geometry,createPlanetMaterial(tex,this.sun.position.clone()));this.earth.position.copy(this.camera.position);this.earth.rotation.set(0,Math.PI/90,0);this.scene.add(this.earth);var corona=new THREE.Mesh(new THREE.SphereGeometry(520,32,32),earthAtmosphere);corona.material.side=THREE.BackSide;corona.material.uniforms.lightPos.value=this.sun.position.clone();corona.receiveShadow=true;this.earth.add(corona);var _=this;var loader=new THREE.FBXLoader();loader.load("/pages/assets/base.fbx",function(model){model.position.set(0,490,0);model.scale.set(100,100,100);model.traverse(function(child){if(child.isMesh){child.castShadow=child.receiveShadow=true;child.material.color.setRGB(1,1,1);child.material=createPlanetMaterial(child.material.map,_.sun.position.clone())}});_.earth.add(model);_.display=model;model.visible=false;_.box=new THREE.Mesh(new THREE.BoxGeometry(.1,2,2),createPlanetMaterial(null,_.sun.position.clone()));_.box.receiveShadow=true;_.box.position.y=2;model.add(_.box);},undefined,function(error){console.error(error);});}
displayOpacity=0;displayDelta=0;render(){if(this.display){this.displayOpacity=(this.displayOpacity+this.displayDelta).clamp(0,1);setOpacity(this.display,this.displayOpacity);if(this.displayOpacity==0)this.display.visible=false;}
let w=$("#3d-container").width();let h=$("#3d-container").height();this.camera.left=w / h*this.gl.orthoHeight /-2;this.camera.right=w / h*this.gl.orthoHeight / 2;if(this.camera.toZoom){this.camera.toZoom=Math.max(parseFloat(this.camera.toZoom.toFixed(3)),0.1);if(this.camera.toZoom=1&&this.gl.map.getZoom()<=1.05){this.gl.map.setZoom(1.1);this.camera.zoom=1;}
var offset=((Math.min(1,Math.max(this.camera.zoom,0.5))-0.35)/ 0.65);offset=Math.sin(offset*Math.PI)*this.gl.orthoHeight*1.5;this.camera.top=offset+this.gl.orthoHeight / 2;this.camera.bottom=offset+this.gl.orthoHeight /-2;this.controls.update();this.drawView(this.camera,0,0,w,h);}
drawView=(cam,vl,vt,vw,vh)=>{let w=$("#3d-container").width();let h=$("#3d-container").height();cam.aspect=vw / vh;cam.updateMatrixWorld(true);cam.updateProjectionMatrix();if(this.outerspaceCamera){this.outerspaceCamera=cam.clone();this.outerspaceCamera.top=150;this.outerspaceCamera.bottom=-150;this.outerspaceCamera.left=w / h*300 /-2;this.outerspaceCamera.right=w / h*300 / 2;this.outerspaceCamera.zoom=0.07;this.outerspaceCamera.updateMatrixWorld(true);this.outerspaceCamera.updateProjectionMatrix();}
if(w!=this.lastW){this.gl.renderer.setSize(w,h);this.composer.setSize(w,h);this.gl.renderer.setViewport(vl,vt,vw,vh);this.gl.renderer.setScissor(vl,vt,vw,vh);}
this.gl.renderer.setScissorTest(true);this.renderPass.camera=cam;if(this.outerspaceCamera){this.gl.renderer.clear();this.gl.renderer.render(this.outerspaceScene,this.outerspaceCamera);this.gl.renderer.clearDepth();this.gl.renderer.render(this.scene,this.camera);}
else this.composer.render();this.lastW=w;}
uploadThumb(data){var tex=new THREE.ImageUtils.loadTexture(data);tex.encoding=THREE.sRGBEncoding;this.box.material.uniforms.image.value=tex;this.box.material.needsUpdate=true;var _=this;var img=new Image();img.onload=function(){_.box.scale.x=1;_.box.scale.y=this.height/this.width;_.display.visible=true;_.displayDelta=0.02;};img.src=data;}
hideDisplay(){this.displayDelta=-0.01;}}
class CreationSpace extends State{resize(){let w=$("#3d-container").width();let h=$("#3d-container").height();this.camera.aspect=w / h;this.camera.updateProjectionMatrix();}
load(){if(this.loaded)return;this.loaded=true;this.setupCamera();this.setupScene();}
setupCamera(){let w=$("#3d-container").width();let h=$("#3d-container").height();this.camera=new THREE.OrthographicCamera(w / h*10 /-2,w / h*10 / 2,10 / 2,10 /-2,0,200);this.setupControl();this.controls.maxZoom=2.2;this.controls.minZoom=.7;this.controls.minPolarAngle=Math.PI/2.1;this.controls.maxPolarAngle=Math.PI/2.1;}
setupScene(){this.scene=new THREE.Scene();this.scene.fog=new THREE.FogExp2(0xffffff,0.025);this.ambientLight=new THREE.AmbientLight(0xffffff,0.5);this.scene.add(this.ambientLight);this.light=new THREE.PointLight(0xffffff,1,100);this.light.castShadow=true;this.light.shadow.mapSize.width=2048;this.light.shadow.mapSize.height=2048;this.light.shadow.camera.near=0.1;this.light.shadow.camera.far=100;this.light.shadow.camera.fov=120;this.light.power=Math.PI*2;this.light.position.set(0,80,0);this.scene.add(this.light);this.camera.position.set(0,6.5,20);this.controls.target.set(0,6,0);var sphere=new THREE.Mesh(new THREE.SphereGeometry(100,32,32),new THREE.MeshPhongMaterial({side:THREE.DoubleSide,color:0xffffff}));sphere.receiveShadow=true;this.scene.add(sphere);var _=this;var loader=new THREE.FBXLoader();loader.load("/pages/assets/pod.fbx",function(model){model.position.set(0,2,0);model.scale.set(2,2,2);model.traverse(function(child){if(child.isMesh){child.castShadow=child.receiveShadow=true;child.material.color.setRGB(1,1,1);}});_.scene.add(model);},undefined,function(error){console.error(error);});this.box=new THREE.Mesh(new THREE.BoxGeometry(5,5,0.2),new THREE.MeshPhongMaterial({side:THREE.DoubleSide,color:0xffffff}));this.box.receiveShadow=true;this.box.position.y=6;this.scene.add(this.box);var plane=new THREE.Mesh(new THREE.CircleGeometry(50,100),new THREE.MeshPhongMaterial({side:THREE.DoubleSide,color:0x7766cc}));plane.receiveShadow=true;plane.rotation.set(Math.PI/2,0,0);this.scene.add(plane);}
uploadThumb(data){var tex=new THREE.ImageUtils.loadTexture(data);tex.encoding=THREE.sRGBEncoding;this.box.material.map=tex;this.box.material.needsUpdate=true;var _=this;var img=new Image();img.onload=function(){_.box.scale.x=1;_.box.scale.y=this.height/this.width;};img.src=data;}
render(){let w=$("#3d-container").width();let h=$("#3d-container").height();this.controls.update();this.camera.aspect=w / h;this.camera.left=w / h*10 /-2;this.camera.right=w / h*10 / 2;this.camera.updateMatrixWorld(true);this.camera.updateProjectionMatrix();this.gl.renderer.clear();this.gl.renderer.render(this.scene,this.camera);}}
class Preview360 extends State{resize(){let w=$("#3d-container").width();let h=$("#3d-container").height();this.camera.aspect=w / h;this.camera.updateProjectionMatrix();}
load(){if(this.loaded)return;this.loaded=true;this.setupCamera();this.setupScene();}
setupCamera(){let w=$("#3d-container").width();let h=$("#3d-container").height();this.camera=new THREE.OrthographicCamera(w / h*this.gl.orthoHeight /-2,w / h*this.gl.orthoHeight / 2,this.gl.orthoHeight / 2,this.gl.orthoHeight /-2,-100,200000);this.setupControl();this.controls.enableDamping=true;this.controls.dampingFactor=0.1;this.controls.minPolarAngle=Math.PI*.4;this.controls.maxPolarAngle=Math.PI*.8;this.controls.minDistance=0;this.controls.maxDistance=70;this.controls.maxZoom=3;this.controls.minZoom=.035;this.controls.zoomSpeed=0.2;this.controls.enableZoom=false;this.controls.enablePan=false;this.controls.autoRotate=false;}
setupScene(){this.scene=new THREE.Scene();this.ambientLight=new THREE.AmbientLight(0xffffff,0.5);this.scene.add(this.ambientLight);this.light=new THREE.PointLight(0xffffff,1,100);this.light.castShadow=true;this.light.shadow.mapSize.width=2048;this.light.shadow.mapSize.height=2048;this.light.shadow.camera.near=0.1;this.light.shadow.camera.far=100;this.light.shadow.camera.fov=120;this.light.power=Math.PI*2;this.light.position.set(0,80,0);this.scene.add(this.light);this.camera.position.set(0,1.7,1.5);this.controls.target.set(0,1.7,1.4);this.sphere=new THREE.Mesh(new THREE.SphereGeometry(600,256,256),new THREE.MeshBasicMaterial({side:THREE.BackSide,map:null}));this.scene.add(this.sphere);let vp=$("#videoPano").get()[0];let _=this;vp.addEventListener("canplaythrough",()=>{_.useVideo();});this.requesting=false;}
set360Video(data){let vp=$("#videoPano").get()[0];let url=data.server+"/rooms/"+data.hash+"/seats/"+(data.video_resolution=="low"?"vlr":"vhr")+"/seat-"+data["seat"]+"."+data.video_type+"?t=";$("#videoPano").attr("src",url);this.requesting=true;gtag('event','page_view',{'page_path':url});}
useVideo(){this.requesting=false;let vp=$("#videoPano").get()[0];var vTex=new THREE.VideoTexture(vp);vTex.minFilter=THREE.LinearFilter;vTex.magFilter=THREE.LinearFilter;vTex.encoding=THREE.sRGBEncoding;vTex.wrapS=vTex.wrapT=THREE.RepeatWrapping;vTex.offset.set(0,0);vTex.repeat.set(-1,1);this.scene.remove(this.sphere);if(this.sphere.material.map)this.sphere.material.map.dispose();this.sphere.material.dispose();this.sphere.geometry.dispose();this.sphere=new THREE.Mesh(new THREE.SphereGeometry(600,64,64),new THREE.MeshBasicMaterial({side:THREE.BackSide,map:vTex}));this.scene.add(this.sphere);}
render(){let w=$("#3d-container").width();let h=$("#3d-container").height();this.controls.update();this.camera.aspect=w / h;this.camera.top=this.gl.orthoHeight / 2;this.camera.bottom=this.gl.orthoHeight /-2;this.camera.left=w / h*this.gl.orthoHeight /-2;this.camera.right=w / h*this.gl.orthoHeight / 2;this.camera.updateMatrixWorld(true);this.camera.updateProjectionMatrix();this.gl.renderer.clear();this.gl.renderer.render(this.scene,this.camera);}
unload(){$("#videoPano").attr("src","");}}
class Shop360 extends State{resize(){let w=$("#3d-container").width();let h=$("#3d-container").height();this.camera.aspect=w / h;this.camera.updateProjectionMatrix();}
load(){if(this.loaded)return;this.loaded=true;this.setupCamera();this.setupScene();}
setupCamera(){let w=$("#3d-container").width();let h=$("#3d-container").height();this.orthoHeight=50;this.camera=new THREE.PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,1000);this.camera.fov=(Math.atan(Math.tan(((60 / 2)*Math.PI)/ 180)/ this.camera.aspect)*2*180)/ Math.PI;this.setupControl();this.controls.enableDamping=true;this.controls.maxZoom=2.2;this.controls.minZoom=.7;this.controls.rotateSpeed=-.5;this.controls.minPolarAngle=Math.PI/6;this.controls.maxPolarAngle=Math.PI/1.7;this.controls.enableZoom=false;this.controls.autoRotate=false;}
getCurrentHighlight(){return this.highlighted;}
visitNextSquare(){let xo=Object.keys(this.squares);let x=xo.indexOf(this.highlighted.x+'');let yo=Object.keys(this.squares[this.highlighted.x]);let y=yo.indexOf(this.highlighted.y+'');let xs=xo.length;let ys=yo.length;if(y0){this.highlightSquare(xo[x],yo[y-1]);return[xo[x],yo[y-1]];}
if(x>0){yo=Object.keys(this.squares[xo[x-1]]);ys=yo.length;this.highlightSquare(xo[x-1],yo[ys-1]);return[xo[x-1],yo[ys-1]];}}
async highlightSquare(x,y){let prev=this.highlighted;this.highlighted=null;if(this.squares&&this.squares[x]&&this.squares[x][y]){this.highlighted=this.squares[x][y];this.updateSquareColor(this.highlighted);}
if(prev)this.updateSquareColor(prev);}
async updateSquareColor(tile){let c=tile.coord?(tile.coord.property?new THREE.Color(0x7766cc):new THREE.Color(0.9,0.9,0.9)):new THREE.Color(0.1,0.1,0.1);if(this.highlighted==tile)c=new THREE.Color(0x4dc631);tile.material.color=c;tile.material.needsUpdate=true;}
landSquareIsMine(x,y){return this.squares&&this.squares[x]&&this.squares[x][y];}
landSquareBuilt(x,y){return this.landSquareIsMine(x,y)&&this.squares[x][y].coord&&this.squares[x][y].coord.property;}
landSquaresAvailable(coords){for(var i in coords){let x=coords[i][0];let y=coords[i][1];if(!this.landSquareIsMine(x,y)||this.landSquareBuilt(x,y))return false;}
return true;}
async setLands(d){let data=d[0];let land=d[1];var loader=new THREE.TextureLoader();loader.crossOrigin='';if(!this.floor)return;if(this.lands!==data){let rnd=Math.random();if(this.items)
for(var i in this.items)if(i>0)
for(var z in this.items[i].children){let t=this.items[i].children[z].children[0];if(t.material.map)t.material.map.dispose();t.material.map=await loader.load(`https://virtualpark.s3.amazonaws.com/lands/${i ? data[Math.floor(Math.random()*Math.min(data.length,100))].group_id : land.group_id}/thumbnail.jpg?`+rnd);t.material.needsUpdate=true;}}
if(this.land!==land){while(this.tiles.children.length>0){this.tiles.children[this.tiles.children.length-1].material.dispose();this.tiles.children[this.tiles.children.length-1].geometry.dispose();this.tiles.remove(this.tiles.children[this.tiles.children.length-1]);}
this.highlighted=null;if(this.items)
for(var z in this.items[0].children){let t=this.items[0].children[z].children[0];loader.crossOrigin='';t.material.map=await loader.load(`https://virtualpark.s3.amazonaws.com/lands/${land.group_id}/thumbnail.jpg?`+Math.random());t.material.needsUpdate=true;}
var squares=[];let maxX=0;let maxY=0;let minX=999999999999;let minY=999999999999;this.squares={};for(var i in land.coords){let l=land.coords[i];if(l.property_id>1)l.property='a';minX=minX>l.x?l.x:minX;minY=minY>l.y?l.y:minY;maxX=maxX{pTexture.encoding=THREE.sRGBEncoding;pTexture.mapping=THREE.EquirectangularReflectionMapping;const sky=pmremGenerator.fromEquirectangular(pTexture).texture;sky.encoding=THREE.sRGBEncoding;_.sky=sky;_.sphere=new THREE.Mesh(new THREE.SphereGeometry(90,32,32),new THREE.MeshPhongMaterial({side:THREE.DoubleSide,map:pTexture}));_.scene.add(this.sphere);var loader=new THREE.TextureLoader();loader.crossOrigin='';let something=await loader.load(VP_360_URL+"/images/outerspace-thumb.jpg");something.encoding=THREE.sRGBEncoding;_.floor=new THREE.Group();_.floor.position.set(0,-5,0);_.scene.add(_.floor);var plane=new THREE.Mesh(new THREE.CircleGeometry(80,100),new THREE.MeshPhongMaterial({side:THREE.DoubleSide,color:0x4dc631}));plane.rotation.set(Math.PI/2,0,0);plane.receiveShadow=true;_.floor.add(plane);var tex=[];let t=await loader.load("https://virtualpark.s3.amazonaws.com/lands/7c6a6b48-fabc-4cb3-a4a0-d672f1528594/thumbnail.jpg");t.encoding=THREE.sRGBEncoding;tex.push(t);let g=null;_.items=[];_.tiles=new THREE.Group();_.tiles.position.set(0,12,-10);_.floor.add(_.tiles);_.items.push(new THREE.Group());_.scene.add(_.items[_.items.length-1]);for(var i=0;i<1;++i){var p=new THREE.Mesh(new THREE.PlaneGeometry(9,9),new THREE.MeshBasicMaterial({side:THREE.DoubleSide,map:tex[Math.floor(Math.random()*tex.length)]}));p.receiveShadow=true;p.castShadow=true;let rad=20;p.position.set(Math.sin(i*Math.PI/2/4)*rad,7,Math.cos(i*Math.PI/2/4)*rad);p.rotation.set(0,i*Math.PI/2/4+Math.PI,0);var frame=new THREE.Mesh(new THREE.BoxGeometry(.2,9,0.2),new THREE.MeshStandardMaterial({side:THREE.DoubleSide,color:0x4dc631}));frame.material.envMap=sky;frame.material.roughness=0.02;frame.material.metalness=1;frame.position.set(-4.4,0,0);p.add(frame);frame=new THREE.Mesh(new THREE.BoxGeometry(.2,9,0.2),new THREE.MeshStandardMaterial({side:THREE.DoubleSide,color:0x4dc631}));frame.material.envMap=sky;frame.material.roughness=0.02;frame.material.metalness=1;frame.position.set(4.4,0,0);p.add(frame);frame=new THREE.Mesh(new THREE.BoxGeometry(9,.2,.2),new THREE.MeshStandardMaterial({side:THREE.DoubleSide,color:0x4dc631}));frame.material.envMap=sky;frame.material.roughness=0.02;frame.material.metalness=1;frame.position.set(0,-4.4,0);p.add(frame);frame=new THREE.Mesh(new THREE.BoxGeometry(9,.2,.2),new THREE.MeshStandardMaterial({side:THREE.DoubleSide,color:0x4dc631}));frame.material.envMap=sky;frame.material.roughness=0.02;frame.material.metalness=1;frame.position.set(0,4.4,0);p.add(frame);g=new THREE.Group();g.add(p);p.scale.set(2,2,2);_.items[_.items.length-1].add(g);}
let circles=[{count:15,rad:50,width:10,height:6,y:3,map:tex},{count:20,rad:60,width:10,height:6,y:11,map:tex},{count:25,rad:55,width:10,height:6,y:20,map:tex},{count:25,rad:80,width:10,height:6,y:4,map:tex},{count:25,rad:60,width:10,height:6,y:30,map:tex},{count:20,rad:80,width:10,height:6,y:35,map:tex},];for(var i in circles){let c=circles[i];_.items.push(new THREE.Group());_.scene.add(_.items[_.items.length-1]);let max=c.count;const points=[];let d=0.3;points.push(new THREE.Vector3(-c.width/2,c.height/2,-0.001));points.push(new THREE.Vector3(-c.width/2-d,c.height/2+d,-0.001));points.push(new THREE.Vector3(c.width/2+d,c.height/2+d,-0.001));points.push(new THREE.Vector3(c.width/2,c.height/2,-0.001));points.push(new THREE.Vector3(-c.width/2,c.height/2,-0.001));points.push(new THREE.Vector3(-c.width/2,-c.height/2,-0.001));points.push(new THREE.Vector3(-c.width/2-d,-c.height/2-d,-0.001));points.push(new THREE.Vector3(c.width/2+d,-c.height/2-d,-0.001));points.push(new THREE.Vector3(c.width/2,-c.height/2,-0.001));points.push(new THREE.Vector3(c.width/2+d,-c.height/2-d,-0.001));points.push(new THREE.Vector3(c.width/2+d,c.height/2+d,-0.001));points.push(new THREE.Vector3(-c.width/2-d,c.height/2+d,-0.001));points.push(new THREE.Vector3(-c.width/2-d,-c.height/2-d,-0.001));let geo=new THREE.BufferGeometry().setFromPoints(points);for(var i=0;i{if(this.inited)return;this.inited=true;this.scope=scope;this.map=map;this.initRenderer();this.states={VP_STATE_MAIN:new SpaceSafari(this),VP_STATE_360:new Preview360(this),VP_STATE_STORE:new Shop360(this),};this.setState("VP_STATE_MAIN");window.addEventListener('resize',()=>{gl.resize();},false);var dc_checkStatus;var dc_element=new Image();var dc_banned=false;Object.defineProperty(dc_element,'id',{get:function(){dc_checkStatus=true;throw new Error("Dev tools checker");}});}
setState(state){if(this.currentState)this.currentState.unload();this.state=state;this.currentState=this.states[state];this.currentState.load();this.resize();}
initRenderer=()=>{let w=$("#3d-container").width();let h=$("#3d-container").height();this.clock=new THREE.Clock();try{this.renderer=new THREE.WebGLRenderer({antialias:true&&!IS_IPHONE&&!IS_IPAD,preserveDrawingBuffer:true});}
catch(err){alert("Your browser 3D rendering has crashed. If refreshing doesn't help, please restart your browser.");}
this.renderer.setPixelRatio(window.devicePixelRatio);this.renderer.setClearColor(0x000000,0);this.renderer.autoClear=false;this.renderer.shadowMap.enabled=true;this.renderer.shadowMap.type=THREE.VSMShadowMap;this.renderer.gammaOutput=true;this.renderer.gammaFactor=2.2;this.renderer.toneMapping=0;this.renderer.toneMappingExposure=1;this.renderer.outputEncoding=THREE.sRGBEncoding;this.renderer.setSize(w,h);this.renderer.domElement.classList.add("fullscreen");this.renderer.domElement.removeAttribute("width");this.renderer.domElement.removeAttribute("height");this.renderer.domElement.id="mainCanvas";this.renderer.domElement.addEventListener("webglcontextlost",function(event){alert("Your computer's 3D web renderer crashes. Reloading. If this occurs twice, please restart your web browser and make sure other applications are closed.")
location.reload();},false);this.renderer.domElement.addEventListener("webglcontextlost",function(event){gl.currentState.controls.zoomIn();},false);this.renderer.domElement.addEventListener("mousemove",function(event){event.preventDefault();let mouse=new THREE.Vector2();mouse.x=(event.clientX / window.innerWidth)*2-1;mouse.y=-(event.clientY / window.innerHeight)*2+1;gl.currentState.mousemove(mouse);},false);$("#3d-container").append(this.renderer.domElement);}
animate=(now)=>{this.currentTime=now/1000.0;var delta=this.clock.getDelta();this.counter+=1;var fpsDelta=Date.now()-this.fpsLast;if(this.fpsLast=5){resizeC=0;this.resize();}
window.renderIsSlow=this.fps+this.lastfps<15?window.renderIsSlow+1:0;if(!window.my_ip)window.my_ip='';}
let desiredFPS=gl.scope.state.zoom>1.05&&!gl.scope.state.showing360?1:5;desiredFPS=1;if(!this.requestingAnimation){this.requestingAnimation=true;if(this.counter/fpsDelta*1000{return;if(gl.scope.state.page!='home')gl.animate(now);});else{setTimeout(function(){requestAnimationFrame((now)=>{gl.animate(now);});},1000 / desiredFPS);}}
this.requestingAnimation=false;this.currentState?this.currentState.render(now):null;}
resize=()=>{let _=this;if(this.t){clearInterval(this.t);}
this.t=setInterval(()=>{let w=$("#3d-container").width();let h=$("#3d-container").height();if(!w)return;_.currentState?_.currentState.resize():null;_.renderer.setSize(w,h);_.renderer.setViewport(0,0,w,h);_.animate();clearInterval(_.t);},1000);}
inited=false;state=VP_STATE_MAIN;counter=0;initialClock=0;lastRecordedClock=0;fpsLast=0;requestingAnimation=false;controls=null;fps=null;lastfps=null;renderPass=null;ambientLight=null;light=null;wfov=100;renderer=null;orthoHeight=450;get camera(){return this.currentState?this.currentState.camera:null;}}
window.renderIsSlow=0;var gl=new GL();/*
* Copyright (c) 2021-2025 Virtual Park LLC. All Rights Reserved.
*
* No one other than us is allowed to copy, use, inspect, imitate, or even see this source code.
*/
//webkitURL is deprecated but nevertheless
URL = window.URL || window.webkitURL;
// shim for AudioContext when it's not avb.
var AudioContext = window.AudioContext || window.webkitAudioContext;
// Opera 8.0+
var isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
// Firefox 1.0+
var isFirefox = typeof InstallTrigger !== 'undefined';
// Safari 3.0+ "[object HTMLElementConstructor]"
var isSafari = /constructor/i.test(window.HTMLElement) || (function(p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification));
// Internet Explorer 6-11
var isIE = /*@cc_on!@*/ false || !!document.documentMode;
// Edge 20+
var isEdge = !isIE && !!window.StyleMedia;
// Chrome 1 - 71
var isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);
// Blink engine detection
var isBlink = (isChrome || isOpera) && !!window.CSS;
const zeroPad = (num, places) => String(num).padStart(places, '0');
Object.fromEntries = (o) => {
var r = {}; for (var i in o) r[o[i][0]] = o[i][1]; return r;
};
var html = $("html").get()[0];
const initZoom = 3;
const defaultZoom = 17;
const newsZoom = 13;
const cityZoom = 8;
const defaultContentZoom = 17;
let lon_span = 360;
let lat_span = 180;
let circum = 40075000;
let tile_s = 2500000;
let tile_w = circum / tile_s;
String.prototype.double = () => parseFloat(self);
function stripHtml(html)
{
let tmp = document.createElement("DIV");
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText || "";
}
// $(".showfocus").css("opacity", 0);
var dc_checkStatus = false;
var dc_element = new Image();
var dc_banned = false;
Object.defineProperty(dc_element, 'id', {
get: function() {
dc_checkStatus=true;
throw new Error("Dev tools checker");
}
});
window.appScope =null;
app.config(function($locationProvider) {
$locationProvider.html5Mode(true);
});
app.filter('unsafe', ['$sce', function($sce){
return function(text) {
return $sce.trustAsHtml(text);
};
}]);
app.directive('onErrorSrc', function() {
return {
link: function(scope, element, attrs) {
element.bind('error', function() {
if (attrs.src != attrs.onErrorSrc) {
attrs.$set('src', attrs.onErrorSrc);
}
});
}
}
})
.directive('myEnter', function () {
return function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
if(event.which === 13) {
scope.$apply(function (){
scope.$eval(attrs.myEnter);
});
event.preventDefault();
}
});
};
})
.config(['$compileProvider', function ($compileProvider) {
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|local|data|chrome-extension):/);
}])
.controller('c-land', ['$scope', '$location', 'vService', function($scope, $location, vService) {
window.appScope =$scope;
$scope.max = function (a,b) {
return Math.max(a,b);
}
$scope.filterNotObject = function (dict) {
let r = {};
Object.entries(dict).filter(([k,v]) => typeof v != 'object' || v==null).map(([k,v]) => r[k] = v );
return r;
}
$scope.filterObject = function (dict) {
let r = {};
Object.entries(dict).filter(([k,v]) => typeof v == 'object').map(([k,v]) => r[k] = v );
return r;
}
$scope.sum = function (arr,e) {
var r = 0; for (var i =0; i< arr.length; i++) r += arr[i][e]; return r;
};
$scope.scrollTop = () => {
$(window).scrollTop(0);
};
$scope.compactNum = (e) => Intl.NumberFormat('en', { notation: 'compact' }).format(e);
$scope.intervals={};
$scope.rotateIntInterval = (x, max, interval) => {
if ($scope.intervals[x]) clearInterval($scope.intervals[x]);
$scope.intervals[x] = setInterval( () => {$scope.rotateInt(x,max);
}, interval);
if (!$scope.state[x]) $scope.state[x] = 0;
}
$scope.rotateInt = (x, max) => {
if (!$scope.state[x]) $scope.state[x] = 0;
$scope.state[x] = ($scope.state[x]+1)%max;
}
$scope.rotateSelection = (_var, data) => {
setInterval( () => {
let k = Object.keys(data);
let i = (k.indexOf($scope.state[_var])+1) % k.length;
$scope.state[_var] = k[i];
}, 6000);
}
let params = $location.search();
let path = params.path;
let pages = window.pages;
if (path) pages = path.split('/');
// $scope.randomString = (l) => {return (Math.random() + 1).toString(16).substring(0);};
$scope.state = {
social: {
new: {},
playlist: {new:'', id: null, hash: null}
},
social: {
new: {},
playlist: {new:'', id: null, hash: null}
},
world: {
route:{price:0},
compass:{},realestate:{installation:{}, editor:{timeout:null},}, bio:{}, installation:{}, bag:{items:[],selectedItem:null, page:0},shop:{quantities:{}, selectedItem:null,selectItemJson: (x)=> {$scope.state.world.shop.selectedItem = JSON.parse(x); $scope.state.world.shop.quantity = 0; }, quantity:0}},
diamond: {exchange: {action: null, usd: 0, estimate: null}},
pages: pages, db: {}, mycontent: {}, terranews: {}, directives:{}, voting:{}, zoom: initZoom, action: 'drag', selectedSquares: [], loggedIn: false,
user: {}, reloadingSquare:true, verification: 'na', content:{ zoom:10, radius:"10", selectedSquares:[]} };
$scope.vService = vService;
vService.init($scope);
$scope.createCalendar = (hash, title,desc,location,time) => {
var icsContent =
"BEGIN:VCALENDAR\r\nPRODID:-//Microsoft Corporation//Outlook 12.0 MIMEDIR//EN\r\nVERSION:2.0\r\nMETHOD:PUBLISH\r\nX-MS-OLK-FORCEINSPECTOROPEN:TRUE\r\nBEGIN:VEVENT\r\nCLASS:PUBLIC\r\nDESCRIPTION:" +
desc +
"\r\nDTEND:" +
moment(time).add(1,"hour").format("YYYYMMDD[T]HHmm[00]") +
"\r\nDTSTART:" +
moment(time).format("YYYYMMDD[T]HHmm[00]") +
"\r\nLOCATION:" +
location +
"\r\nPRIORITY:5\r\nSEQUENCE:0\r\nSUMMARY;LANGUAGE=en-us:vPARK-RUN [" +
title +
"]\r\nTRANSP:OPAQUE\r\nUID:" +
hash +
"\r\nX-MICROSOFT-CDO-BUSYSTATUS:BUSY\r\nX-MICROSOFT-CDO-IMPORTANCE:1\r\nX-MICROSOFT-DISALLOW-COUNTER:FALSE\r\nX-MS-OLK-ALLOWEXTERNCHECK:TRUE\r\nX-MS-OLK-AUTOFILLLOCATION:FALSE\r\nX-MS-OLK-CONFTYPE:0\r\nBEGIN:VALARM\r\nTRIGGER:-PT1440M\r\nACTION:DISPLAY\r\nDESCRIPTION:Reminder\r\nEND:VALARM\r\nEND:VEVENT\r\nEND:VCALENDAR";
return "data:text/plain;charset=utf-8," + encodeURIComponent(icsContent);
}
$scope.state.world.realestate.editor.setPosToCurLoc = () => {
// $scope.state.world.realestate.editor.assets.new.position = {x:0,y:0,z:0};
ue4('getCharacterLocation', function (v) { $scope.state.world.realestate.editor.assets.new.position = v; } );
}
$scope.state.world.realestate.editor.changed = (v,k) => {
if (!window.ue4) return;
let a = $scope.state.world.realestate.editor;
a.timeout ? (clearTimeout(a.timeout )):0;
a.timeout = setTimeout( () => {
if (!v[k] && k.length==1) v[k] = 0;
let e = $scope.state.world.realestate.editor;
if (e && e.assets.new && !(e.assets.new.type in ['spawn']) ) ue4('applyChangeFromEditor', {elements:[e.assets.new]});
}, 1000);
}
$scope.getXYFromLatLon= () => {
let lat = parseFloat(prompt("latitude [-90 - 90]?"));
let lon = parseFloat(prompt("longitude [-180 - 180]?"));
$scope.state.social.my_gps = $scope.c2sqr(lon, lat);
$scope.state.social.my_gps.lon = lon;
$scope.state.social.my_gps.lat = lat;
$scope.state.gettingGPS = false;
}
$scope.getMyGps = () => {
if ($scope.state.gettingGPS) return;
if (navigator.geolocation) {
$scope.state.gettingGPS = true;
navigator.geolocation.getCurrentPosition( (p) => {
$scope.state.social.my_gps = $scope.c2sqr(p.coords.longitude, p.coords.latitude);
$scope.state.social.my_gps.lon = p.coords.longitude;
$scope.state.social.my_gps.lat = p.coords.latitude;
$scope.state.gettingGPS = false;
if (!$scope.locMap) {
mapboxgl.accessToken = 'pk.eyJ1IjoidnBhcmsiLCJhIjoiY2tqZzEwMGY1MmJuMTJ6bjAybWh6bXQ0bSJ9.Y2Rv0ZN4Nh2sj8CGgDL78g';
$scope.locMap = new mapboxgl.Map({
container: 'locMap',
style: 'mapbox://styles/mapbox/satellite-streets-v11',
zoom: 12,
center: [p.coords.longitude, p.coords.latitude],
});
$scope.locMap.on('idle',function(){
$scope.locMap.resize();
});
var nav = new mapboxgl.NavigationControl({ showCompass: false });
$scope.locMap.addControl(nav, 'top-left');
$scope.locMap.addControl(new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true
}));
$scope.locMap.setMaxZoom(18);
$scope.locMap.on('click', (e) => {
$scope.state.social.my_gps = $scope.c2sqr(e.lngLat.lng, e.lngLat.lat);
$scope.state.social.my_gps.lon = e.lngLat.lng;
$scope.state.social.my_gps.lat = e.lngLat.lat;
$scope.locMapMarker.setLngLat(e.lngLat);
});
$scope.locMapMarker = new mapboxgl.Marker().setLngLat([p.coords.longitude, p.coords.latitude]).addTo($scope.locMap);
}
else {
$scope.locMap.setCenter([p.coords.longitude, p.coords.latitude]);
$scope.locMapMarker.setLngLat({lng: p.coords.longitude, lat:p.coords.latitude});
}
}, () => {
$scope.state.gettingGPS = false;
} );
} else {
alert("Geolocation is not supported by this browser.");
}
}
$scope.setupMemoryMap = (xy) => {
if (!xy) return;
let p = $scope.sqr2nw(xy.x,xy.y);
$scope.state.social.my_gps = {x:xy.x,y:xy.y};
$scope.state.social.my_gps.lon = p.lon;
$scope.state.social.my_gps.lat = p.lat;
if (!$scope.locMap) {
mapboxgl.accessToken = 'pk.eyJ1IjoidnBhcmsiLCJhIjoiY2tqZzEwMGY1MmJuMTJ6bjAybWh6bXQ0bSJ9.Y2Rv0ZN4Nh2sj8CGgDL78g';
$scope.locMap = new mapboxgl.Map({
container: 'locMap',
style: 'mapbox://styles/mapbox/satellite-streets-v11',
zoom: 12,
center: [p.lon, p.lat],
});
$scope.locMap.on('idle',function(){
$scope.locMap.resize();
});
var nav = new mapboxgl.NavigationControl({ showCompass: false });
$scope.locMap.addControl(nav, 'top-left');
$scope.locMap.addControl(new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true
}));
$scope.locMap.setMaxZoom(18);
$scope.locMap.on('click', (e) => {
$scope.state.social.my_gps = $scope.c2sqr(e.lngLat.lng, e.lngLat.lat);
$scope.state.social.my_gps.lon = e.lngLat.lng;
$scope.state.social.my_gps.lat = e.lngLat.lat;
$scope.locMapMarker.setLngLat(e.lngLat);
});
$scope.locMapMarker = new mapboxgl.Marker().setLngLat([p.lon, p.lat]).addTo($scope.locMap);
}
else {
$scope.locMap.setCenter([p.lon, p.lat]);
$scope.locMapMarker.setLngLat({lng: p.lon, lat:p.lat});
}
}
$scope.state.world.bio.sleep = () => {
vService.api.bio.sleep( (d) => {
$scope.state.world.bio.status = d;
});
}
$scope.state.world.realestate.installation.installable = (e_hash) => {
$scope.state.world.realestate.installation.installables = [];
vService.api.realestate.installation.getInstallables( e_hash, (d) => {
$scope.state.world.realestate.installation.installables = d;
});
}
$scope.state.world.realestate.installation.setPrice = (e_hash, price) => {
vService.api.realestate.installation.setPrice( e_hash, price, (data) => {
$scope.state.world.realestate.installation.page=0;
});
}
$scope.state.world.realestate.installation.counterfeit = (e_hash) => {
vService.api.realestate.installation.counterfeit( e_hash, (data) => {
$scope.state.world.realestate.installation.page=0;
});
}
$scope.state.world.realestate.installation.buy = (e_hash) => {
vService.api.realestate.installation.buy( e_hash, (data) => {
$scope.state.world.realestate.installation.selected = null;
ue4('installable', {hash:e_hash, glb:null});
});
}
$scope.state.world.realestate.installation.install = (i, e_hash) => {
vService.api.realestate.installation.install( i.hash, e_hash, (data) => {
$scope.state.world.realestate.installation.selected = i;
$scope.state.world.realestate.installation.page=0;
ue4('installable', {hash:e_hash, glb:data.glb});
});
}
$scope.state.world.realestate.installation.remove = (e_hash) => {
vService.api.realestate.installation.remove( e_hash, (d) => {
$scope.state.world.realestate.installation.selected = null;
ue4('installable', {hash:e_hash, glb:null});
});
}
$scope.state.world.bio.wakeup = () => {
vService.api.bio.wakeup( (d) => {
$scope.state.world.bio.status = d;
if (window.ue4 && !d.slept) ue4('wakeup');
});
}
$scope.state.world.bio.getStamina = () => {
vService.api.bio.stamina( (d) => {
$scope.state.world.bio.status = d;
});
}
$scope.state.world.shop.changeQuantity = (mult) => {
let idx = $scope.state.world.shop.selectedIndex;
let i = $scope.state.world.shop[$scope.state.world.shop.subpage][idx];
if (!$scope.state.world.shop.quantities[idx]) $scope.state.world.shop.quantities[idx] = {quantity:0, hash:i.hash, price:i.price};
let q = $scope.state.world.shop.quantities[idx];
let max_selected = $scope.state.world.shop.subpage == "supply" ? i.max_quantity-i.quantity : Math.min(i.quantity, i.max_owned ? i.max_owned : i.quantity);
if (mult > 0) q.quantity = q.quantity <= max_selected - i.step*mult ? q.quantity+i.step*mult : q.quantity;
else if (mult < 0) q.quantity = q.quantity + i.step*mult >=0 ? q.quantity+i.step*mult : q.quantity;
}
$scope.state.world.shop.calculateTotal = () => {
var q = 0;
for (var i in $scope.state.world.shop.quantities)
q += $scope.state.world.shop.quantities[i].price * $scope.state.world.shop.quantities[i].quantity;
return q;
}
$scope.state.world.shop.presalePurchase = (hash,quantity) => {
if (!confirm("Please confirm you want to buy one unit of this item. This action is final, irreversible, and non-refundable.")) return;
$scope.state.world.shop.processing = true;
let x = [{hash:hash, quantity:quantity}];
vService.api.shop.buyBulk(x, (d) => {
if (d.message == 'ok') {
alert("Congrats! Your purchase was successful, you can see your item in the Digital Goods section under My Account.");
}
$scope.state.world.shop.processing = false;
}, (e) => {
$scope.state.world.shop.processing = false;
});
}
$scope.state.world.shop.purchase = () => {
let items = [];
Object.keys($scope.state.world.shop.quantities).map( (i) => { items.push({hash:$scope.state.world.shop.quantities[i].hash, quantity: $scope.state.world.shop.quantities[i].quantity}) } );
if (items.length <=0) return;
$scope.state.world.shop.processing=true;
vService.api.shop.buyBulk( items, (d) => {
if (d.message == 'ok') {
$scope.state.world.shop.selectedItem = null;
$scope.state.world.shop.quantities = {};
$scope.state.world.shop.subpage = null;
$scope.state.world.shop.page = null;
$scope.state.user.voken = d.diamond;
}
$scope.state.world.shop.processing = false;
}, (data) => { $scope.state.world.shop.error = data; $scope.state.world.shop.processing = false; });
}
$scope.state.world.shop.sell = () => {
let items = [];
Object.keys($scope.state.world.shop.quantities).map( (i) => { items.push({hash:$scope.state.world.shop.quantities[i].hash, quantity: $scope.state.world.shop.quantities[i].quantity}) } );
if (items.length <=0) return;
$scope.state.world.shop.processing=true;
vService.api.shop.sell( items, (d) => {
if (d.message == 'ok') {
$scope.state.world.shop.selectedItem = null;
$scope.state.world.shop.quantities = {};
$scope.state.world.shop.subpage = null;
$scope.state.world.shop.page = null;
$scope.state.user.voken = d.diamond;
}
$scope.state.world.shop.processing = false;
}, (data) => { $scope.state.world.shop.error = data; $scope.state.world.shop.processing = false; });
}
$scope.state.world.shop.store = (store_hash) => {
let selectedItem = $scope.state.world.shop[$scope.state.world.shop.subpage][$scope.state.world.shop.selectedIndex];
let q = $scope.state.world.shop.quantities[$scope.state.world.shop.selectedIndex].quantity;
if (q < 1) return;
$scope.state.world.shop.processing=true;
vService.api.shop.store( store_hash, selectedItem.hash, q, (d) => {
// if (d.message == 'ok') {
// $scope.state.world.shop.selectedItem = null;
$scope.state.world.shop.quantities = {};
selectedItem.quantity -= q;
// $scope.state.world.shop.subpage = null;
// $scope.state.world.shop.page = null;
// }
$scope.state.world.shop.processing = false;
}, (data) => { $scope.state.world.shop.error = data; $scope.state.world.shop.processing = false; });
}
$scope.state.world.shop.takeout = (store_hash) => {
let selectedItem = $scope.state.world.shop[$scope.state.world.shop.subpage][$scope.state.world.shop.selectedIndex];
let q = $scope.state.world.shop.quantities[$scope.state.world.shop.selectedIndex].quantity;
if (q < 1) return;
$scope.state.world.shop.processing=true;
vService.api.shop.takeout( store_hash, selectedItem.hash, q, (d) => {
// if (d.message == 'ok') {
// $scope.state.world.shop.selectedItem = null;
$scope.state.world.shop.quantities = {};
selectedItem.quantity -= q;
// $scope.state.world.shop.subpage = null;
// $scope.state.world.shop.page = null;
// }
$scope.state.world.shop.processing = false;
}, (data) => { $scope.state.world.shop.error = data; $scope.state.world.shop.processing = false; });
}
$scope.state.world.shop.setPrice = (store_hash) => {
let selectedItem = $scope.state.world.shop[$scope.state.world.shop.subpage][$scope.state.world.shop.selectedIndex];
$scope.state.world.shop.processing=true;
vService.api.shop.setPrice( store_hash, selectedItem.hash, selectedItem.price, (d) => {
$scope.state.world.shop.processing = false;
}, (data) => { $scope.state.world.shop.processing = false; });
}
$scope.state.world.bag.pagingIndex = 0;
$scope.state.world.bag.currentPagingString = () => {
return ($scope.state.world.bag.pagingIndex+1) + " / " + Math.ceil(($scope.state.world.bag.items.length)/10);
}
$scope.state.world.bag.increaseIndex = (i) => {
$scope.state.world.bag.pagingIndex += i;
$scope.state.world.bag.pagingIndex = Math.max($scope.state.world.bag.pagingIndex, 0);
$scope.state.world.bag.pagingIndex = Math.min($scope.state.world.bag.pagingIndex, Math.ceil(($scope.state.world.bag.items.length)/10)-1);
}
$scope.state.world.bag.loadInvites = (i) => {
vService.api.account.myInvites( (d) => {
$scope.state.world.invites = d;
});
}
$scope.state.world.bag.cancelInvite = (i) => {
vService.api.account.cancelInvite( () => {
$scope.state.world.invites = $scope.state.world.invites.filter( e => e.hash != i.hash);
});
}
$scope.state.world.bag.cancelInvites = () => {
if ($scope.state.world.invites.find( e => e.enough))
vService.api.account.cancelInvites( () => {
$scope.state.world.invites = $scope.state.world.invites.filter( e => e.enough);
});
}
$scope.state.world.bag.filterItems = (items) => {
return items.slice($scope.state.world.bag.pagingIndex*10,$scope.state.world.bag.pagingIndex*10+10);
}
$scope.state.world.bag.consume = () => {
if ($scope.state.world.bag.selectedItem.quantity < 1) return;
$scope.state.world.bag.processing=true;
vService.api.bag.consume( $scope.state.world.bag.selectedItem.hash, (d) => {
if (d.quantity == 0) {
$scope.state.world.bag.items = $scope.state.world.bag.items.filter( (e) => e.hash != $scope.state.world.bag.selectedItem.hash );
$scope.state.world.bag.selectedItem = null;
$scope.state.world.bag.pagingIndex=0;
$scope.state.world.bag.page=1;
}
else $scope.state.world.bag.selectedItem.quantity = d.quantity;
$scope.state.world.bag.processing = false;
}, (data) => { $scope.state.world.bag.processing = false; });
}
$scope.state.world.bag.equip = () => {
if ($scope.state.world.bag.selectedItem.quantity != 1) return;
$scope.state.world.bag.processing=true;
vService.api.bag.equip( $scope.state.world.bag.selectedItem.hash, (d) => {
if (d.message == 'ok') {
$scope.state.world.bag.selectedItem.equipped = 1;
ue4('equipNewItem', d);
}
$scope.state.world.bag.processing = false;
}, (data) => { $scope.state.world.bag.processing = false; });
}
$scope.state.world.bag.unequip = () => {
if ($scope.state.world.bag.selectedItem.quantity != 1) return;
$scope.state.world.bag.processing=true;
vService.api.bag.unequip( $scope.state.world.bag.selectedItem.hash, (d) => {
if (d.message == 'ok') {
$scope.state.world.bag.selectedItem.equipped = 0;
ue4('equipNewItem', {glb:''});
}
$scope.state.world.bag.processing = false;
}, (data) => { $scope.state.world.bag.processing = false; });
}
$scope.state.world.bag.delete = () => {
if ($scope.state.world.bag.selectedItem.quantity < 1) return;
$scope.state.world.bag.processing=true;
vService.api.bag.delete( $scope.state.world.bag.selectedItem.hash, (d) => {
if (d.message == 'ok') {
$scope.state.world.bag.items = $scope.state.world.bag.items.filter( (e) => e.hash != $scope.state.world.bag.selectedItem.hash );
$scope.state.world.bag.selectedItem = null;
$scope.state.world.bag.pagingIndex=0;
$scope.state.world.bag.page=1;
}
$scope.state.world.bag.processing = false;
}, (data) => { $scope.state.world.bag.processing = false; });
}
$scope.state.world.realestate.enter = (address,unit, closeSession) => {
ue4('worldEnterProperty', {levelName: 'vPARK_property', hash:address, unit: unit, closesession: closeSession ? true : false });
}
$scope.state.world.realestate.setDestination = (address,unit) => {
ue4('setWorldDestination', {hash:address, unit: unit });
ue4('closeWindow');
}
setInterval(() => {
// window.dispatchEvent(new Event('resize'));
if ($scope.state.showing360) return;
if (map) if (Math.round($("#map .mapboxgl-canvas").width()) != Math.round($("body").width()) && $("body").width() > 1280) {
map.resize();
// $scope.locMap.resize();
$scope.refreshGrid();
}
$scope.width = window.innerWidth;
$scope.height = window.innerHeight;
// if (window.ue4) {
// ue4('start');
// }
if (!window.my_ip) window.my_ip = '';
if (window.my_ip.length>0 && ! dc_banned) {
// dc_checkStatus = false;
// console.dir(dc_element);
// if (dc_checkStatus) {
// console.log("%c For security reason, you could be blocked from vPark if you continue opening this console. We do not welcome potential hackers in our system.", 'background: #222; color: #bada55');
// $.post("/api/security/ip/ban", {ip: window.my_ip}, function(d) { if (d!="{}") dc_banned = true; } );
// }
}
$scope.$apply();
}, 2000);
$(window).scroll( function(e) {
if ($scope.state.showaccountmenu) {
$scope.state.showaccountmenu = false;
// $scope.$apply();
}
});
$scope.reloadWallet = () => {
$scope.vService.api.wallet.diamond.getStats( () => { $scope.$apply(); });
$scope.vService.api.wallet.diamond.getWallet( () => { $scope.$apply(); });
}
$scope.state.mainTimerCounter = 0;
$scope.state.mainPageMissionPos = 0;
$scope.mainTimer = setInterval( () => {
let c = $scope.state.mainTimerCounter ++;
// if (c % 30 == 0) { $scope.state.mp.c++; $scope.state.mp.f++; $scope.state.mp.s++; $scope.state.mp.q++; }
// if (c % 50 == 0) { $scope.state.mainPageMissionPos = ($scope.state.mainPageMissionPos+1) %3; }
// if ($scope.state.page == "home2") {
// $scope.state.mp.c %= $scope.state.countryStats.length;
// $scope.state.mp.f %= $scope.state.userCountryStats.length;
// $scope.state.mp.s %= $scope.state.big_supporters.length;
// $scope.state.mp.q %= 3;
// }
var z = $('#land-price-led').get();
if (z.length) {
var x = $('#land-price-led').scrollLeft()+2;
var maxScrollLeft = $('#land-price-led').get(0).scrollWidth - $('#land-price-led').get(0).clientWidth;
if (x > maxScrollLeft) x = 0;
$('#land-price-led').scrollLeft(x);
}
// if (c % 120 == 0) {
// var x = $('#land-price-led').scrollLeft()+$(window).width()*0.7;
// var maxScrollLeft = $('#land-price-led').get(0).scrollWidth - $('#land-price-led').get(0).clientWidth;
// if (x > maxScrollLeft) x = 0;
// $('#land-price-led').animate({scrollLeft:x},7000);
// }
// $scope.$apply();
}, 100);
$scope.initializePage = () => {
// console.log("page " +$scope.state.page);
if ($scope.state.page == "market") $scope.vService.api.land.loadMarket('time', $scope.state.purchasableLands ? $scope.state.purchasableLands.offset : 0);
if ($scope.state.page == "") {
$(window).scroll(() => {
if (window.pageYOffset > 0) {
gl.camera.toZoom = .86 - (window.pageYOffset) / window.innerHeight;
}
else gl.camera.toZoom = .86;
});
}
if ($scope.state.page == "home_") {
// location.href = "/dashboard";
$("body").css("min-width", "300px");
$(window).scroll(() => {
$("#earthbanner").css("visibility","visible");
$("#earthbanner").offset({top:Math.max(Math.min(window.pageYOffset+window.innerHeight-$("#earthbanner img").height(),window.innerHeight*3),window.innerHeight)});
});
$("#earthbanner").offset({top:Math.max(Math.min(window.pageYOffset+window.innerHeight-$("#earthbanner img").height(),window.innerHeight*3),window.innerHeight)});
$(window).scroll(_.debounce( function(){
}, 50, { 'leading': true, 'trailing': false }));
$(window).scroll(_.debounce( function(){
$(".sticky-block").each ( (i,e) => {
let t = $(e).offset().top;
if (t > window.innerHeight*5) return;
if (window.pageYOffset+window.innerHeight*.5 > t && t > window.pageYOffset+100)
$('html, body').animate({
scrollTop: t
}, Math.abs(t-window.pageYOffset)/window.innerHeight*1000);
else if (window.pageYOffset-window.innerHeight*.5 < t && t < window.pageYOffset)
$('html, body').animate({
scrollTop: t
}, Math.abs(t-window.pageYOffset)/window.innerHeight*1000);
});
}, 50));
}
if ($scope.state.page == "shop") {
$scope.vService.api.nft.list(0,30);
}
if (~$scope.state.page.indexOf("api/world/ui/resting")) {
$scope.state.world.bio.getStamina();
setInterval( () => { $scope.state.world.bio.getStamina(); }, 30000);
}
if (~$scope.state.page.indexOf("api/world/ui/playlists/") && $scope.state.loggedIn) {
$scope.vService.api.social.playlist.get( $scope.state.page.replace("api/world/ui/playlists/", ""), (data) => {
$scope.state.social.posts = data.posts;
} );
}
if (~$scope.state.page.indexOf("api/world/ui/realestate/") && ~$scope.state.page.indexOf("/editor") && $scope.state.loggedIn) {
if (!$scope.state.world.realestate.editor.assets) {
$scope.vService.api.realestate.editor.getAssets( $scope.state.page.replace("api/world/ui/realestate/", "").replace("/editor", ""), (data) => {
$scope.state.world.realestate.editor.assets = data;
} );
$scope.vService.api.nft.mine();
}
}
$scope.state.world.calcHealthDisplay = (data) =>{
$scope.state.world.health = data;
// data.calories;
// data.water;
$scope.state.world.display_health = {
Energy: {value: data.calories, percent:(data.calories)/120000, markers:[{i:0,color:"black"},{i:0.36,color:"red"},{i:0.727,color:"#cbc00d"}, {i:1,color:"#007e8c"}], unit: "Cal"},
Water: {value: data.water/1000, percent:(data.water-40000)/10000, markers:[{i:0,color:"black"},{i:0.2,color:"red"},{i:0.5,color:"#cbc00d"}, {i:1,color:"#007e8c"}], unit: "L"},
// Weight: {value: data.weight, unit: "kg"},
Stamina: {value: data.stamina, percent:data.stamina/100, markers:[{i:0,color:"black"},{i:0.25,color:"red"},{i:0.5,color:"#cbc00d"}, {i:1,color:"#007e8c"}], unit: "%"},
// Endurance: {value: 50, unit: "%"},
// Life: {value: 2, unit: "Y/o"}
}
};
if ($scope.state.page.indexOf("api/world/ui/menu")==0) {
setInterval( () => {
$scope.vService.api.account.avatarStatus( (data) =>{
$scope.state.world.health = data;
});
}, 30000);
$scope.vService.api.account.avatarStatus( $scope.state.world.calcHealthDisplay );
}
$scope.state.world.healthColor = (i, m) => {
m = m.sort( (a,b) => {return a.i-b.i} );
for (x in m) if ( i <= m[x].i) return m[x].color;
}
if (~$scope.state.page.indexOf("api/world/ui/bag")) {
$scope.vService.api.bag.list( (data) =>{
$scope.state.world.bag.items = data;
$scope.vService.api.account.avatarStatus( (data) =>{
$scope.state.world.health = data;
// data.calories;
// data.water;
$scope.state.world.display_health = {
Stamina: {value: data.stamina, percent:data.stamina/100, markers:[{i:0,color:"black"},{i:0.25,color:"red"},{i:0.5,color:"#cbc00d"}, {i:1,color:"#007e8c"}], unit: "%"},
Energy: {value: data.calories, percent:(data.calories)/120000, markers:[{i:0,color:"black"},{i:0.36,color:"red"},{i:0.727,color:"#cbc00d"}, {i:1,color:"#007e8c"}], unit: "Cal"},
Water: {value: data.water/1000, percent:(data.water-40000)/10000, markers:[{i:0,color:"black"},{i:0.2,color:"red"},{i:0.5,color:"#cbc00d"}, {i:1,color:"#007e8c"}], unit: "L"},
// Weight: {value: data.weight, unit: "kg"},
// Endurance: {value: 50, unit: "%"},
// Life: {value: 2, unit: "Y/o"}
}
} );
} );
}
if ($scope.state.page.indexOf("api/world/ui/shop/")>=0) {
$scope.vService.api.shop.list( $scope.state.page.replace("api/world/ui/shop/",""), (data) =>{
} );
}
if ($scope.state.page.indexOf("api/world/ui/realestate/")>=0 && $scope.state.page.indexOf("/entrance")>=0) {
$scope.vService.api.realestate.getMyAccess( $scope.state.world.realestate.unit, (data) =>{
if (data && data.unit && data.unit.hash) {
$scope.state.world.realestate.unit = data.unit.hash;
}
if (data) {
$scope.state.world.realestate.closeSession = data.closeSession;
$scope.state.world.realestate.access = data.access;
$scope.state.world.realestate.lifespan = data.lifespan;
$scope.state.world.realestate.created_at = data.created_at;
}
$scope.state.world.realestate.success = true;;
} );
}
if ($scope.state.page.indexOf("run") == 0) {
if ($scope.state.pages.length > 2) {
if ($scope.state.pages[1] == 'fundraisings')
$scope.vService.api.fundraise.getFundraising($scope.state.pages[2]);
else if ($scope.state.pages[1] == 'campaigns')
$scope.vService.api.fundraise.getCampaign($scope.state.pages[2]);
else if ($scope.state.pages[1] == 'races')
$scope.vService.api.fundraise.getResult($scope.state.pages[2]);
}
else if ($scope.state.pages.length > 1) {
$scope.state.world.fundraise = {sponsor:null,campaign:{}};
switch ($scope.state.pages[1]) {
case 'campaigns':
vService.api.fundraise.list(); break;
case 'sponsors':
vService.api.fundraise.list();$scope.state.world.fundraise.sponsor ={cap:10000,target:1000}; break;
case 'create':
$scope.vService.api.fundraise.mine();break;
case 'routes': vService.api.fundraise.availableRoutes(); break;
case 'mine': vService.api.fundraise.myFundraisings(); break;
default:;
}
}
// $scope.state.subpages = ''
}
if ($scope.state.page.indexOf("theater") == 0 && $scope.state.loggedIn) {
$scope.state.social.showDonate = false;
if ($scope.state.pages.length > 2) {
$scope.state.social.user = $scope.state.pages[1];
if ($scope.state.pages[1] == 'playlists') {
$scope.vService.api.social.playlist.get($scope.state.pages[2], (data) => {
$scope.state.social.playlist = data;
$scope.state.social.playlists = data;
});
}
}
else if ($scope.state.pages.length > 1) {
$scope.state.social.user = $scope.state.pages[1];
if ($scope.state.pages[1] == 'me') {
$scope.state.world.calcHealthDisplay($scope.state.user.biology);
}
$scope.vService.api.social.user($scope.state.pages[1], (data) => {
$scope.state.social.user = data;
});
}
else {
$scope.vService.api.social.get(null,(data) => {
$scope.state.social.data = data.posts;
$scope.state.social.p = data.posts.length ? data.posts[$scope.state.social.viewIndex] : null;
});
}
// $scope.state.subpages = ''
}
if ($scope.state.page == "digicards") {
location.href = "/dashboard";
$scope.vService.api.digicard.list();
}
if ($scope.state.page == "my-content") {
location.href = "/dashboard";
$scope.vService.api.digicard.list();
$scope.vService.api.terranews.loadMyContent();
}
if ($scope.state.page == "land-tokens") {
location.href = "/dashboard";
$scope.vService.api.economy.landtoken.list();
// $scope.TNContentList();
}
if ($scope.state.loggedIn) {
if ($scope.state.page.indexOf("api/world") >=0) {
if ($scope.state.page.indexOf("api/world/ui/chat") >=0)
$scope.vService.api.landchat.loadContents($scope.state.landchatGroup,0);
}
else {
$scope.reloadWallet();
}
}
if ($scope.state.page == "diamond" && $scope.state.loggedIn) {
gtag('event','page_view', { 'page_path':"diamond", 'page_title' : "Diamond Exchanger",});
$scope.state.landchatGroup = 'grp:diamonds';
$scope.vService.api.landchat.loadContents($scope.state.landchatGroup,0);
var options = {
series: [{
data: [
{x: new Date(2022,2,1),y: [0.001, 0.001, 0.001, 0.001]},
],
}],
chart: {
type: 'candlestick',
height: 450,
width: "90%",
toolbar: {
// show: false,
tools: {download:0}
},
animations: {
speed: 200
},
},
plotOptions: {
candlestick: {
colors: {
upward: 'var(--vp-green)',
downward: 'var(--vp-red)'
}
},
},
xaxis: {
type: 'datetime',
min: moment().subtract(7, 'days').toDate(),
// max: moment().endOf('day').toDate(),
labels: {
formatter: function(val) {
return moment(val).format('MM/DD HH:mm')
}
}
},
yaxis: {
tooltip: {
enabled: true
}
},
};
if (!$scope.state.diamonChart) {
setTimeout( () => { // wait until diamond-chart div appears
$scope.state.diamonChart = new ApexCharts(document.querySelector("#diamond-chart"), options);
$scope.state.diamonChart.render();
$scope.vService.api.wallet.diamond.getHistory( 60, (data) => {
$scope.state.diamond.exchange.history = data;
var d = data.map( (e) => { return {x:new Date(e.timestamp*1000), y:[e.open, e.high, e.low, e.close], timestamp: e.timestamp}});
$scope.state.diamonChart.updateSeries([{data: d }]);
$scope.$apply();
});
setInterval( () => {
if ($scope.state.page == "diamond") {
$scope.vService.api.wallet.diamond.getStats( () => { $scope.$apply(); });
}
}, 3600000);
setInterval( () => {
if ($scope.state.page == "diamond") {
if (!$scope.vService.api.wallet.diamond.processing && $scope.state.diamond.exchange.estimate && !$scope.state.diamond.exchange.result && $scope.state.diamond.exchange.estimate.timestamp < Date.now()/1000-60)
$scope.state.diamond.exchange.estimate = null;
$scope.vService.api.wallet.diamond.getWallet( (data) => {
let prev = $scope.state.diamond.exchange.history.findIndex((e) => e.timestamp == data.history_latest_price.last.timestamp);
if (prev>=0) $scope.state.diamond.exchange.history[prev] = data.history_latest_price.last;
else $scope.state.diamond.exchange.history.push(data.history_latest_price.last);
let now = $scope.state.diamond.exchange.history.findIndex((e) => e.timestamp == data.history_latest_price.current.timestamp);
if (now>=0) $scope.state.diamond.exchange.history[prev] = data.history_latest_price.current;
else $scope.state.diamond.exchange.history.push(data.history_latest_price.current);
var d = $scope.state.diamond.exchange.history.map( (e) => { return {x:new Date(e.timestamp*1000), y:[e.open, e.high, e.low, e.close], timestamp: e.timestamp}});
if ($scope.state.diamonChart.updating) return;
$scope.state.diamonChart.updating = true;
$scope.state.diamonChart.updateSeries([{data: d }]);
$scope.state.diamonChart.false = true;
$scope.$apply();
});
}
}, 15000);
}, 2000);
}
}
if ($scope.state.page == "profile") { $scope.vService.api.wallet.loadTransactions(0);
$scope.vService.api.wallet.checkWithdrawal(); }
if ($scope.state.page.indexOf("dashboard") == 0) {
$scope.state.pages = ["home"];
$scope.initMap = setInterval( () => {
if (!map) {
$scope.initializeMap();
gl.init($scope, map);
}
clearInterval($scope.initMap);
},1000);
// $scope.vService.api.landchat.loadUnread();
$scope.vService.api.land.loadMarket('time', $scope.state.purchasableLands ? $scope.state.purchasableLands.offset : 0);
// $scope.vService.api.terranews.loadWaitlist('general');
if ($scope.state.loggedIn) {
if ($scope.state.user.biology)
$scope.state.world.calcHealthDisplay($scope.state.user.biology);
$scope.vService.api.account.getFinancial( () => {
});
}
$scope.loadStats();
}
// if ($scope.state.loggedIn) {
// console.log("aaaa");
if ($scope.state.page.indexOf("dashboard") == 0 && window.url_param_2 && window.url_param_2.length > 30) { setTimeout($scope.goToGroup(url_param_2,false), 5000); }
if ($scope.state.page.indexOf("dashboard") == 0 && window.newsContentId && window.newsContentId.length > 0) { setTimeout($scope.goToGroup(url_param_2,false), 5000); }
if ($scope.state.page.indexOf("dashboard") == 0 && window.url_param_2 && window.url_param_2.length >= 6) { setTimeout($scope.loadLandowner(url_param_2), 1000);
};
// $scope.vService.api.landchat.loadStatus('general');
// }
if ($scope.state.page == "contents") { $scope.loadContents(); $scope.initializeContentMap(); }
// if ($scope.state.page == "chat") { $scope.vService.api.landchat.loadStatus('general'); }
// if ($scope.state.page == "terranews") { $scope.vService.api.terranews.loadWaitlist('terratalk'); }
if ($scope.state.page == "contents" && window.url_param_2 && window.url_param_2.length > 5) { setTimeout($scope.loadContent(url_param_2), 5000); }
if ($scope.state.page == "votes") $scope.getVotes();
if ($scope.state.page == "origin") {
$scope.loadStats();
gl.init($scope, map);
gl.camera.zoom = 0.81;
}
if ($scope.state.page == "") {
$scope.loadStats();
gl.init($scope, map);
gl.camera.zoom = 0.81;
$scope.showSpaceSafari();
// $scope.vService.ajax.call("https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmedium.com%2Ffeed%2F%40vpark-io", "GET", null, null, (data) => {
$scope.state.medium = {"items":[
{
"title":"Helping Charities Spread Awareness","pubDate":"2023-02-19 16:34:18",
"link":"https://medium.com/@vpark-io/non-runners-can-help-charities-spread-their-messages-d99339871e22",
"thumbnail":"https://miro.medium.com/fit/c/224/224/1*4FXnZrwA5MSIlkjrkpDqXA.png",
"description":"If you haven't already, read our vPARK Economy article to make sense of this one. Lo and behold, virtual-world citizens can help real-world citizens, not use using tokens as tickets like that of NFTs or giving fantasy like that of games. Running is one of those energetic pastimes that…"
},
{
"title":"Designing A Run Course","pubDate":"2023-02-18 16:34:18",
"link":"https://medium.com/@vpark-io/designing-a-race-course-7eb2215e6f91",
"thumbnail":"https://miro.medium.com/fit/c/224/224/1*nN-M-U1PaQ7dU8sq-EI-bQ.png",
"description":"You should be aware that route is a crucial part of race economics as vPARK prepares virtual race fundraising campaigns in partnership with giant organizations. Why? To finish a race, competitors must eat, drink, and sprint through rings. When registering for a race, a runner chooses a sponsor, who will pay…"
},
{
"title":"Renting Out Your Lands","pubDate":"2023-02-18 16:34:18",
"link":"https://medium.com/@vpark-io/rent-out-lands-b756384f10aa",
"thumbnail":"https://miro.medium.com/max/1400/1*v3zHlN2WIR5HEsRk_HVMPw.webp",
"description":"Users who are interested in a specific building can now rent a specific piece of land. This will help users who need some land but won’t buy a new square. The leasing price can be set from the dashboard in Diamond currency. Landowners will lose the capability to extend or build new real estate, ..."
},
{
"title":"A Parallel Economy To Improve Life","pubDate":"2023-02-18 16:34:18",
"link":"https://medium.com/@vpark-io/a-parallel-economy-to-improve-life-6965ec972d02",
"thumbnail":"https://miro.medium.com/fit/c/224/224/0*9wnJwy7am-rOogWO",
"description":"After witnessing the economic failure caused by wars and pandemics, vPARK was founded. One who says “Aren’t we in good shape now?” is most likely overly focused on macro economy, high-paying jobs and"
},
{
"title":"Will Abandonment & Suicide Be The Initial History?","pubDate":"2023-01-19 20:34:18",
"link":"https://medium.com/@vpark-io/will-abandonment-suicide-be-the-initial-history-9f9aa6236c92",
"thumbnail":"https://cdn-images-1.medium.com/max/1024/1*z3irNUN-jfkqAcxtIqnJ4g.png",
"description":"A world without purpose is void and not worth living… This also applies to vHumans. Thousands are facing the first wave of abandonment by their owners, the souls that give it life. Sorrow and..."
},
{
"title":"Building Your Village","pubDate":"2023-01-14 11:30:18",
"link":"https://vpark-io.medium.com/while-a-vhuman-can-be-relocated-to-live-alone-or-in-a-village-leaving-a-governmental-housing-area-405d2e0720ad",
"thumbnail":"https://cdn-images-1.medium.com/max/1024/1*28ljkIzJL1czGefnHgjkKQ.png",
"description":"While a vHuman can be relocated to live alone or in a village, leaving a governmental housing area may present difficulties. The last thing you want is for your vHuman to travel so far that it becomes hungry,"
},
{
"title":"On-going Crimes Mean More Deaths","pubDate":"2023-01-09 18:50:18",
"link":"https://vpark-io.medium.com/on-going-crimes-mean-more-deaths-d11dbb9b0643",
"thumbnail":"https://cdn-images-1.medium.com/max/1024/1*mnUbDvkg1c_iM-2rdjY5jw.png",
"description":"Governmental housing in San Jose is packed. Imagine sharing a home with 1,000 or more additional neighbors, all of whom swap food and ingredients with a single nonprofit restaurant. Built restaurants are st"
},
{
"title":"Raise a vHuman and stay alive.","pubDate":"2023-01-07 00:05:34",
"link":"https://vpark-io.medium.com/raise-a-vhuman-and-stay-alive-91c4ccc51a4?source=rss-27d58c53cdfe------2",
"guid":"https://medium.com/p/91c4ccc51a4","author":"vPARK",
"thumbnail":"https://cdn-images-1.medium.com/max/1024/1*YbB0GnPg3kB5aWATRqRIOg.png",
"description":"A lifelike, intelligent simulation of a human being complete with a biological body, mind, and personality. It can do good and evil, and with your guidance, it will learn to make decisions. It independently"
}
]};
for (var i in $scope.state.medium.items) {
$scope.state.medium.items[i].description = stripHtml($scope.state.medium.items[i].description);
$scope.state.medium.items[i].p = Math.round(Math.random()*50+25);
}
// });
}
$scope.state.tester = [
// '82a7cd3a-e533-507f-ae84-34d20c9d725f', 'bd3da121-1a15-588e-9f00-0a7fe1490a1e','5fc48e89-f67e-5777-b84d-ec07a05eb7f6','99e95b7a-fb42-51a6-bef0-9e4ba62a2d0f'
].indexOf($scope.state.user.hash) >= 0;
}
$scope.refresh= (s) => {
location.href="/"+s;
}
// $scope.$on('$routeChangeStart', function (scope, next, current) {
// if (location.pathname.indexOf("partners/") == 0) window.location.reload();
// });
$scope.$watch("state.page", (newValue, oldValue) => {
if (location.pathname.indexOf("resetPassword") >= 0) return;
if ($scope.state.page == "undefined") $scope.state.page = "";
if (!$scope.state.page) $scope.state.page = "";
if ($scope.state.page=='howto') $scope.state.page = "how-to";
if (!$scope.state.pages || typeof $scope.state.pages != 'object' || (newValue && newValue.indexOf("dashboard") != 0))
$scope.state.pages = newValue.split("/");
$scope.initializePage()
window.history.pushState($scope.state.page, 'vPARK', '/' + $scope.state.page + ($scope.state.page=='landowner' ? '/'+url_param_2 : ''));
// if (newValue == "dashboard" && oldValue != "dashboard" && oldValue && !$scope.state.pages) window.location.reload();
// else
if (newValue == "dashboard" && oldValue != "dashboard" && oldValue) $scope.validate();
// if (window.mixpanel) mixpanel.track("Page Change", {page:$scope.state.page});
$scope.scrollTop();
});
$(window).on('popstate', function (e) {
if (location.pathname.indexOf("/partners/") == 0) window.location.reload();
$scope.state.page = location.pathname.substring(1);
});
$scope.toggleHamburger = (v) => {
var show = false;
if ($scope.hidingHamburger || v===false) $("#mobile-header-menu").slideUp();
else { $("#mobile-header-menu").slideDown(); show = true; $scope.state.showaccountmenu = false; }
$scope.hidingHamburger = show;
};
$scope.occupiedLands = {};
$scope.properties = [];
$scope.vhumans = [];
$scope.billboards = [];
$scope.rings = [];
$scope.others = [];
var contentMap = null;
$scope.initializeContentMap = () => {
if (contentMap) return;
mapboxgl.accessToken = 'pk.eyJ1IjoidnBhcmsiLCJhIjoiY2tqZzEwMGY1MmJuMTJ6bjAybWh6bXQ0bSJ9.Y2Rv0ZN4Nh2sj8CGgDL78g';
contentMap = new mapboxgl.Map({
container: 'submission-map',
style: 'mapbox://styles/mapbox/satellite-streets-v11',
zoom: 12,
center: [-122.447303, 37.753574],
});
var nav = new mapboxgl.NavigationControl({ showCompass: false });
contentMap.addControl(nav, 'top-left');
contentMap.addControl(new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true
}));
contentMap.setMaxZoom(18);
// disable map rotation using right click + drag
contentMap.dragRotate.disable();
contentMap.keyboard.disable();
// disable map rotation using touch rotation gesture
contentMap.touchZoomRotate.disableRotation();
var coordinatesGeocoder = function(query) {
// match anything which looks like a decimal degrees coordinate pair
var matches = query.match(
/^[ ]*(?:Lat: )?(-?\d+\.?\d*)[, ]+(?:Lng: )?(-?\d+\.?\d*)[ ]*$/i
);
if (!matches) {
return null;
}
function coordinateFeature(lng, lat) {
return {
center: [lng, lat],
geometry: {
type: 'Point',
coordinates: [lng, lat]
},
place_name: 'Lat: ' + lat + ' Lng: ' + lng,
place_type: ['coordinate'],
properties: {},
type: 'Feature'
};
}
var coord1 = Number(matches[1]);
var coord2 = Number(matches[2]);
var geocodes = [];
if (coord1 < -90 || coord1 > 90) {
geocodes.push(coordinateFeature(coord1, coord2));
}
if (coord2 < -90 || coord2 > 90) {
geocodes.push(coordinateFeature(coord2, coord1));
}
if (geocodes.length === 0) {
geocodes.push(coordinateFeature(coord1, coord2));
geocodes.push(coordinateFeature(coord2, coord1));
}
return geocodes;
};
contentMap.addControl(
new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
localGeocoder: coordinatesGeocoder,
zoom: 18,
placeholder: 'Try: -40, 170',
mapboxgl: mapboxgl
})
);
contentMap.on('movestart', () => {
$scope.state.content.sqrs = [];
$scope.state.content.reloadingSquare = true;
});
contentMap.on('dragstart', () => {
$scope.state.content.sqrs = [];
$scope.state.content.reloadingSquare = true;
$scope.$apply();
});
contentMap.on('zoom', () => {
$scope.state.content.zoom = contentMap.getZoom();
$scope.$apply();
});
contentMap.on('idle', () => {
$scope.refreshContentGrid();
});
$(window).trigger('resize');
}
$scope.defaultContentZoom = defaultContentZoom;
$scope.defaultZoom = initZoom;
var map;
$scope.initializeMap = async () => {
if (map) { $scope.refreshGrid(); return;}
mapboxgl.accessToken = 'pk.eyJ1IjoidnBhcmsiLCJhIjoiY2tqZzEwMGY1MmJuMTJ6bjAybWh6bXQ0bSJ9.Y2Rv0ZN4Nh2sj8CGgDL78g';
map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/vpark/cktkt5ii22ul218muwm4jui3h',
zoom: initZoom,
attributionControl: false,
center: [-122, 37.38643203244379],
});
await map.once('load');
// Add some 3d terrain
map.addSource('mapbox-dem', {
'type': 'raster-dem',
'url': 'mapbox://mapbox.mapbox-terrain-dem-v1',
'tileSize': 512,
'maxzoom': 17
});
$scope.lastMapBound = map.getBounds();
map.addSource('empty', {
'type': 'geojson',
'data': '/pages/assets/heatmap.geojson'
});
map.addSource('lands', {
'type': 'geojson',
'data': '/pages/assets/heatmap.geojson'
});
map.addLayer({
'id': 'landpurchased-heat',
'type': 'heatmap',
'source': 'lands',
'maxzoom': 9,
'paint': {
// Increase the heatmap weight based on frequency and property magnitude
'heatmap-weight': [
'interpolate',
['linear'],
['get', 'squares'],
0,0,6,1],
// Increase the heatmap color weight weight by zoom level
// heatmap-intensity is a multiplier on top of heatmap-weight
'heatmap-intensity': [
'interpolate',
['linear'],
['zoom'],
0,1,9,3
],
// Color ramp for heatmap. Domain is 0 (low) to 1 (high).
// Begin color ramp at 0-stop with a 0-transparancy color
// to create a blur-like effect.
'heatmap-color': [
'interpolate',
['linear'],
['heatmap-density'],
0,
'rgba(33,102,172,0)',
0.2,
'rgb(103,169,207)',
0.4,
'rgb(209,229,240)',
0.6,
'rgb(253,219,199)',
0.8,
'rgb(239,138,98)',
1,
'rgb(178,138,43)'
],
// Adjust the heatmap radius by zoom level
'heatmap-radius': [
'interpolate',
['linear'],
['zoom'],
0,2,9,20
],
// Transition from heatmap to circle layer by zoom level
'heatmap-opacity': [
'interpolate',
['linear'],
['zoom'],
7,1,9,0
]
}
},
'waterway-label'
);
map.addLayer({
'id': 'empty-heat',
'type': 'heatmap',
'source': 'empty',
'maxzoom': defaultZoom,
'paint': {
// Increase the heatmap weight based on frequency and property magnitude
'heatmap-weight': [
'interpolate',
['linear'],
['get', 'squares'],
0,0,1,1,5,5],
// Increase the heatmap color weight weight by zoom level
// heatmap-intensity is a multiplier on top of heatmap-weight
'heatmap-intensity': [
'interpolate',
['linear'],
['zoom'],
cityZoom,1,newsZoom,3
],
// Color ramp for heatmap. Domain is 0 (low) to 1 (high).
// Begin color ramp at 0-stop with a 0-transparancy color
// to create a blur-like effect.
'heatmap-color': //"rgb(239,138,98)",
[
'interpolate',
['linear'],
['heatmap-density'],
0,
'rgba(33,0,172,0)',
0.2,
'rgba(33,0,172,0.5)',
1,
'rgba(178,100,0,0.5)'
],
// Adjust the heatmap radius by zoom level
'heatmap-radius': [
'interpolate',
['linear'],
['zoom'],
cityZoom,2,newsZoom,10
],
// Transition from heatmap to circle layer by zoom level
'heatmap-opacity': [
'interpolate',
['linear'],
['zoom'],
cityZoom-0.1,0,cityZoom,1,newsZoom,1
]
}
},
'waterway-label'
);
var nav = new mapboxgl.NavigationControl({ showCompass: false });
map.addControl(nav, 'bottom-right');
// map.addControl(new mapboxgl.GeolocateControl({
// positionOptions: {
// enableHighAccuracy: true
// },
// trackUserLocation: true
// }));
map.setMaxZoom(18);
// disable map rotation using right click + drag
map.dragRotate.disable();
map.keyboard.disable();
// disable map rotation using touch rotation gesture
map.touchZoomRotate.disableRotation();
var coordinatesGeocoder = function(query) {
// match anything which looks like a decimal degrees coordinate pair
var matches = query.match(
/^[ ]*(?:Lat: )?(-?\d+\.?\d*)[, ]+(?:Lng: )?(-?\d+\.?\d*)[ ]*$/i
);
if (!matches) {
return null;
}
function coordinateFeature(lng, lat) {
return {
center: [lng, lat],
geometry: {
type: 'Point',
coordinates: [lng, lat]
},
place_name: 'Lat: ' + lat + ' Lng: ' + lng,
place_type: ['coordinate'],
properties: {},
type: 'Feature'
};
}
var coord1 = Number(matches[1]);
var coord2 = Number(matches[2]);
var geocodes = [];
if (coord1 < -90 || coord1 > 90) {
geocodes.push(coordinateFeature(coord1, coord2));
}
if (coord2 < -90 || coord2 > 90) {
geocodes.push(coordinateFeature(coord2, coord1));
}
if (geocodes.length === 0) {
geocodes.push(coordinateFeature(coord1, coord2));
geocodes.push(coordinateFeature(coord2, coord1));
}
return geocodes;
};
map.addControl(
new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
localGeocoder: coordinatesGeocoder,
zoom: 17,
flyTo: { duration: 0 },
placeholder: 'Try: -40, 170',
mapboxgl: mapboxgl
})
);
map.on('movestart', () => {
$scope.properties = [];
$scope.vhumans = [];
$scope.rings = [];
$scope.billboards = [];
$scope.others = [];
$scope.sqrs = [];
$scope.state.reloadingSquare = true;
// if ($scope.state.zoom < defaultZoom) $scope.clearSelected();
// if (!$scope.state.loggedIn) $scope.state.loggingIn = true;
});
map.on('dragstart', () => {
$scope.properties = [];
$scope.vhumans = [];
$scope.rings = [];
$scope.billboards = [];
$scope.others = [];
$scope.sqrs = [];
$scope.state.reloadingSquare = true;
$scope.state.movingMap = true;
// if (!$scope.state.loggedIn) $scope.state.loggingIn = true;
$scope.$apply();
});
$scope.mapDoneZooming = setTimeout( () => {}, 10000);
map.on('zoomstart', () => {
$scope.properties = [];
$scope.vhumans = [];
$scope.rings = [];
$scope.billboards = [];
$scope.others = [];
});
map.on('zoom', () => {
$scope.state.zoom = map.getZoom();
let b = map.getBounds();
// console.log(b);
let p1 = $scope.c2sqr(b._sw.lng, b._ne.lat);
let p2 = $scope.c2sqr(b._ne.lng, b._sw.lat);
$scope.state.currentBound = [p1.x, p1.y, p2.x, p2.y];
// console.log($scope.state.currentBound);
// console.log($scope.state.zoom);
// $scope.state.movingMap = true;
// $scope.updateMapView();
// clearTimeout($scope.mapDoneZooming);
// if ($scope.state.zoom < defaultZoom) $scope.clearSelected();
// $scope.mapDoneZooming = setTimeout( () => {
// if (map.getZoom() > 12) {
// $scope.refreshGrid();
// $scope.updateLeftPanel();
// }
// if (map.getZoom() >= 17) {
// if ($scope.state.pages[0] != "map" &&
// $scope.state.path.indexOf("marketplace/land") !== 0 &&
// $scope.state.path.indexOf("account") !== 0
// ) $scope.state.pages = ["map","land"];
// if (!$scope.state.loggedIn) $scope.state.loggingIn = true;
// }
// }, 1000);
// // $scope.$apply();
});
map.on('idle', () => {
$scope.state.movingMap = false;
$scope.updateMapView();
$scope.updateLeftPanel();
$scope.refreshGrid();
if ($scope.state.loggedIn && $scope.state.page == "dashboard" && window.url_param_2 && window.url_param_2.length > 0) { $scope.vService.api.land.showPurchase(null,url_param_2);
url_param_2 = null; };
});
$(window).trigger('resize');
$scope.refreshGrid();
}
let lastTime = 0.0;
$scope.animationTime = 0.0;
let cycleTime = 0.0;
let day = true;
function frame(time) {
const elapsedTime = (time - lastTime) / 1000.0;
$scope.animationTime += elapsedTime;
cycleTime += elapsedTime;
// if (cycleTime > 10.0) {
// if (day) {
// map.setPaintProperty('sky-day', 'sky-opacity', 1);
// map.setPaintProperty('sky-night', 'sky-opacity', 0);
// map.setFog({ 'color': 'white' });
// } else {
// map.setPaintProperty('sky-day', 'sky-opacity', 0);
// map.setPaintProperty('sky-night', 'sky-opacity', 1);
// map.setFog({ 'color': 'rgba(66, 88, 106, 1.0)' });
// }
// day = !day;
// cycleTime = 0.0;
// }
const rotation = $scope.animationTime * 2.0;
if (rotation > 360*2) return;
if (map.getZoom() > 12 && map.getZoom() < 15) map.setBearing(rotation % 360);
lastTime = time;
if (map.getZoom() > 12 && map.getZoom() < 15) window.requestAnimationFrame(frame);
}
$scope.toggleTerrainView = () => {
if ($scope.state.action != 'sculpt') {
map.setTerrain( {'source': 'mapbox-dem', 'exaggeration': 1.5} );
map.setPitch(70, {duration:2000});
map.setFog({
'range': [0.5, 15],
'color': 'white',
'horizon-blend': 0.5
});
map.dragRotate.enable();
map.dragPan.disable();
map.scrollZoom.disable();
$scope.state.action = 'sculpt';
// window.requestAnimationFrame(frame);
}
else {
$scope.state.action = 'drag';
map.setTerrain( null );
map.setFog(null);
map.setBearing(0);
map.setPitch(0);
map.dragPan.enable();
map.dragRotate.disable();
map.scrollZoom.enable();
$scope.animationTime = 0;
}
$scope.state.lastZoom1 = map.getZoom();
};
$scope.updateMapView = () => {
return;
// if ($scope.state.dashboard.page == 'terranews') {
if (map.getZoom() >= 15 && $scope.state.lastZoom1 < 15) {
map.setTerrain( null );
}
else if (map.getZoom() >= 12 && map.getZoom() < 15 && ($scope.state.lastZoom1 < 12 || $scope.state.lastZoom1 >= 15)) {
map.setTerrain( {'source': 'mapbox-dem', 'exaggeration': 1.5} );
}
else if (map.getZoom() < 12 && $scope.state.lastZoom1 > 12) {
map.setTerrain( null );
}
if (map.getZoom() > 12 && map.getZoom() < 15) {
window.requestAnimationFrame(frame);
}
if (map.getZoom() > 5 && map.getZoom() < 15 && ($scope.state.lastZoom1 < 5 || $scope.state.lastZoom1 >= 15)) {
map.setPitch(70, {duration:2000});
map.setFog({
'range': [0.5, 15],
'color': 'white',
'horizon-blend': 0.5
});
map.dragRotate.enable();
}
else if ((map.getZoom() < 5 && $scope.state.lastZoom1 > 5) || (map.getZoom() >= 15 && $scope.state.lastZoom1 < 15)) {
map.setFog(null);
map.setBearing(0);
map.setPitch(0);
map.dragRotate.disable();
}
// }
// else if ($scope.state.dashboard.lastPage == 'terranews') {
// map.setFog(null);
// map.setBearing(0);
// map.setPitch(0);
// map.dragRotate.disable();
// }
if (map.getZoom() < 12) {
$scope.animationTime = 0;
}
$scope.state.lastZoom1 = map.getZoom();
// $scope.state.dashboard.lastPage = $scope.state.dashboard.page;
}
$scope.setupAnimatedMap = () => {
return;
if (map.getZoom() >= 15) {
map.setTerrain( null );
}
else if (map.getZoom() > 12) {
map.setTerrain( {'source': 'mapbox-dem', 'exaggeration': 1.5} );
}
else if (map.getZoom() < 12) {
map.setTerrain( null );
}
if (map.getZoom() > 12) {
window.requestAnimationFrame(frame);
}
if (map.getZoom() > 5 && map.getZoom() < 15) {
map.setPitch(70, {duration:2000});
map.setFog({
'range': [0.5, 15],
'color': 'white',
'horizon-blend': 0.5
});
map.dragRotate.enable();
}
else if (map.getZoom() < 5 || map.getZoom() >= 15) {
map.setFog(null);
map.setBearing(0);
map.setPitch(0);
map.dragRotate.disable();
}
$scope.state.lastZoom1 = map.getZoom();
$scope.state.dashboard.lastPage = $scope.state.dashboard.page;
}
$scope.mapLatLngToPercent = (lat,lng) => {
// let map_w = $("#map").width();
// let map_h = $("#map").height();
let b = map.getBounds();
let dlat = b._ne.lat - b._sw.lat;
let dlon = b._ne.lng - b._sw.lng;
return [(lat-b._sw.lat)/dlat, (lng-b._sw.lng)/dlon];
}
$scope.mapSqrToPercent = (x,y) => {
let p = $scope.sqr2nw(x,y);
let lat = p.lat; let lng = p.lon;
let b = map.getBounds();
let dlat = b._ne.lat - b._sw.lat;
let dlon = b._ne.lng - b._sw.lng;
return [(lat-b._sw.lat)/dlat, (lng-b._sw.lng)/dlon];
}
$scope.mapYSqrToPercent = (x,y) => {
let p = $scope.sqr2nw(x,y);
y = map.project([p.lon,p.lat]).y;
return y/$("#map").height();
}
$scope.mapXSqrToPercent = (x,y) => {
let p = $scope.sqr2nw(x,y);
let b = map.getBounds();
if (p.lon < b._sw.lng) p.lon += 360;
if (p.lon > b._ne.lng) p.lon -= 360;
x = map.project([p.lon,p.lat]).x;
return x/$("#map").width();
}
// $scope.landchatTimer = null;
$scope.landchatTimer = setInterval( () => {
if ($scope.state.showLandchat) $scope.vService.api.landchat.loadContents($scope.state.landchatGroup,'all');
},5000);
$scope.inLandBuyZoomZone = () => {
return $scope.state.zoom >= defaultZoom;
}
$scope.inCityZoomZone = () => {
return $scope.state.zoom >= cityZoom;
}
$scope.inRingZoomZone = () => {
return $scope.state.zoom < 12;
}
$scope.getMapBound = () => {
let b = map.getBounds();
let p1 = $scope.c2sqr(b._sw.lng, b._ne.lat);
let p2 = $scope.c2sqr(b._ne.lng, b._sw.lat);
return [p1.x,p2.y,p2.x,p1.y];
}
let lastQueried = [0,0,0,0];
$scope.state.currentBound = [0,0,0,0];
$scope.clearGridCache = () => {
$scope.lastMapBound = null;
}
$scope.refreshGrid = (force) => {
if ($scope.state.page != "dashboard") return;
if (window.innerWidth > 1920) return;
if (!$scope.state.loggedIn) {
// $scope.state.loggingIn = true;
return;
}
if ($scope.state.action == 'sculpt') return;
let b = map.getBounds();
if (!force)
if ($scope.lastMapBound && b._ne.lat == $scope.lastMapBound._ne.lat && b._ne.lng == $scope.lastMapBound._ne.lng && b._sw.lat == $scope.lastMapBound._sw.lat && b._sw.lng == $scope.lastMapBound._sw.lng)
return;
$scope.lastMapBound = b;
let dlat = b._ne.lat - b._sw.lat;
let dlon = b._ne.lng - b._sw.lng;
$scope.sqrs = [];
let rf = map.queryRenderedFeatures();
if ($scope.state.zoom >= 5 && rf.length) {
// if (rf[Math.floor(rf.length/2)].properties.iso_3166_1.toLowerCase() != $scope.state.landchatGroup) $scope.state.landchats['contents'] = [];
// $scope.state.landchatGroup = rf[Math.floor(rf.length/2)].properties.iso_3166_1.toLowerCase();
}
// else if ('intl' != $scope.state.landchatGroup) $scope.state.landchats['contents'] = [];
$scope.state.landchatGroup = 'intl';
// if ($scope.state.zoom < 5) $scope.state.landchatGroup = 'intl';
if ($scope.state.showLandchat && $scope.state.zoom > 5) $scope.vService.api.landchat.loadContents($scope.state.landchatGroup,'all');
let p1 = $scope.c2sqr(b._sw.lng, b._ne.lat);
let p2 = $scope.c2sqr(b._ne.lng, b._sw.lat);
$scope.state.currentBound = [p1.x, p1.y, p2.x, p2.y];
let cacheMultiplier = 20;
p1.x -= p1.x % cacheMultiplier;
p2.y -= p2.y % cacheMultiplier;
p2.x += (cacheMultiplier - p2.x % cacheMultiplier)% cacheMultiplier;
p1.y += (cacheMultiplier - p1.y % cacheMultiplier)% cacheMultiplier;
let p0 = $scope.sqr2nw(p1.x, p1.y + 1);
let p00 = $scope.sqr2nw(p2.x + 1, p2.y);
if ($scope.state.path == "")
$scope.vService.api.terranews.loadScans(p1.x,p2.y,p2.x,p1.y);
// else if ($scope.state.path == "map/news")
// $scope.vService.api.terranews.loadContents(p1.x,p2.y,p2.x,p1.y);
else if ($scope.state.path == "map/digicard") {
$scope.vService.api.terranews.loadDigicards(p1.x,p2.y,p2.x,p1.y);
$scope.vService.api.terranews.loadScans(p1.x,p2.y,p2.x,p1.y);
}
else if ($scope.state.path=='map/news')
$scope.vService.api.story.get(p1.x,p2.y,p2.x,p1.y);
else if ($scope.state.terraMode == 2 || $scope.state.dashboard.page=='terranews') {
$scope.vService.api.terranews.loadContents(p1.x,p2.y,p2.x,p1.y);
$scope.vService.api.terranews.loadAds(p1.x,p2.y,p2.x,p1.y);
}
if ($scope.state.path =='map/land' && !$scope.inCityZoomZone())
// if ($scope.state.path.indexOf('map/land') >= 0)
$scope.vService.api.land.loadGroupsOnSale(p1.x,p2.y,p2.x,p1.y);
// con
if (dlat + dlon > /*.015*/ 10 || $scope.state.zoom < cityZoom) { $scope.state.action = "drag";
$scope.$apply(); return; }
if ($scope.state.zoom >= defaultZoom) {
if (p1.x >= lastQueried[0] && p2.y >= lastQueried[1] && p2.x <= lastQueried[2] && p1.y <= lastQueried[3] && !force) ;
else {
lastQueried = [p1.x, p2.y, p2.x, p1.y];
$scope.vService.ajax.call("/api/land/occupied/" + p1.x + "/" + p2.y + "/" + (p2.x + 1) + "/" + (p1.y + 1), "GET", true, null, (data) => {
$scope.occupiedLands = {};
$scope.cutLands = data.empty;
data.lands.forEach( e => $scope.occupiedLands[e.x+"-"+e.y] = e );
for (var i in data.lands)
$scope.state.selectedSquares.filter(e => e !== data.lands[i].x + "-" + data.lands[i].y);
$scope.$apply();
});
}
}
else if ($scope.inCityZoomZone() && !$scope.inLandBuyZoomZone()) {
// $scope.properties = [];
// $scope.vhumans = [];
// $scope.rings = [];
// $scope.billboards = [];
// $scope.others = [];
var c = document.getElementById("routelines");
c.width = c.scrollWidth;
c.height = c.scrollHeight;
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
let ringarea=$scope.inRingZoomZone() ? 1:0;
$scope.vService.ajax.call("/api/land/paths/" + p1.x + "/" + p2.y + "/" + (p2.x + 1) + "/" + (p1.y + 1)+`?billboards=${ringarea^1}&empty=${ringarea}&vhumans=${ringarea^1}&rings=${ringarea}&others=${ringarea^1}`, "GET", true, null, (data) => {
map.getSource('empty').setData(data.empty);
$scope.properties = data.properties;
$scope.vhumans = data.vhumans;
$scope.rings = data.rings;
$scope.billboards = data.billboards;
$scope.others = data.others;
let rs = {};
let rss = {};
let rse = {};
var c = document.getElementById("routelines");
c.width = c.scrollWidth;
c.height = c.scrollHeight;
var ctx = c.getContext("2d");
for (var i in $scope.rings) {
let r = $scope.rings[i];
if (r.i) {
rs[r.i] = (rs[r.i] ? rs[r.i] : []);
rs[r.i].push(r);
if (r.interaction==1) rss[r.i] = r;
if (r.interaction==2) rse[r.i] = r;
}
}
for (var i in rs) {
if (!rss[i] || !rse[i]) continue;
let r = rss[i] ? rss[i] : rs[i][0];
let vr = new THREE.Vector3(r.x,r.y,0);
ctx.beginPath();
ctx.lineWidth = 5;
ctx.strokeStyle = '#ff0000';
// console.log(r);
rs[i].splice(rs[i].findIndex( e => e.x==r.x && e.y==r.y),1);
ctx.moveTo(parseFloat($scope.sqr2coordX(r.x)), parseFloat($scope.sqr2coordY(r.y)));
let deg = 35/180*Math.PI;
let lines = rs[i].length;
while (lines > 0 && r.interaction != 2 && deg < 3) {
let n = null;
let dist = 1000000;
// let angle = 180;
let selected = -1;
for (var ii in rs[i]) {
let q = rs[i][ii];
let vq = new THREE.Vector3(q.x,q.y,0);
let d = vq.clone().sub(vr).length();
if (d == 0) continue;
let dv1 = vq.clone().sub(vr).normalize();
let dv2 = new THREE.Vector3(Math.sin(r.double_data1/180*Math.PI),Math.cos(r.double_data1/180*Math.PI),0);
a = Math.abs(Math.acos(dv2.dot(dv1)));
if (a < deg && dist > d) {
dist = d;
n = q;
selected = ii;
}
}
if (!n) { deg += 25/180*Math.PI; continue; }
lines--;
// console.log(n);
ctx.lineTo(parseFloat($scope.sqr2coordX(n.x)), parseFloat($scope.sqr2coordY(n.y)));
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = '#00ffff';
ctx.moveTo(parseFloat($scope.sqr2coordX(n.x)), parseFloat($scope.sqr2coordY(n.y)));
r = n;
vr = new THREE.Vector3(r.x,r.y,0);
rs[i].splice(parseInt(selected),1);
}
ctx.stroke();
}
$scope.$apply();
});
}
let sqr_span_lon = p00.lon - p0.lon;
let sqr_span_lat = p0.lat - p00.lat;
let map_w = $("#map").width();
let map_h = $("#map").height();
let sqr_w = map_w / dlon * sqr_span_lon / (p2.x - p1.x + 1);
let sqr_h = map_h / dlat * sqr_span_lat / (p1.y - p2.y + 1);
$("#tiles").css("left", (p0.lon - b._sw.lng) / (b._ne.lng - b._sw.lng) * map_w + "px");
$("#tiles").css("top", (b._ne.lat - p0.lat) / (b._ne.lat - b._sw.lat) * map_h + "px");
$scope.sqr2coordX = (x,off=0) => {
return ((x - p1.x) * sqr_w-off) + "px";
};
$scope.sqr2coordY = (y,off=0) => {
return ((p1.y - y) * sqr_h-off) + "px";
};
$scope.state.reloadingSquare = false;
if ($scope.state.zoom < defaultZoom) return;
for (var x = p1.x; x <= p2.x; ++x)
for (var y = p1.y; y >= p2.y; --y) {
let p = $scope.sqr2nw(x, y);
let _x = (x + tile_s) % tile_s;
$scope.sqrs.push({ x: _x, y: y, left: (x - p1.x) * sqr_w + "px", top: (p1.y - y) * sqr_h + "px", width: sqr_w + "px", height: sqr_h + "px" });
}
$scope.$apply();
};
$scope.state.refreshNews = () => {
return;
let b = map.getBounds();
let p1 = $scope.c2sqr(b._sw.lng, b._ne.lat);
let p2 = $scope.c2sqr(b._ne.lng, b._sw.lat);
$scope.vService.api.terranews.loadContents(p1.x,p2.y,p2.x,p1.y);
$scope.vService.api.terranews.loadAds(p1.x,p2.y,p2.x,p1.y);
}
$scope.state.refresh360 = () => {
return;
let b = map.getBounds();
let p1 = $scope.c2sqr(b._sw.lng, b._ne.lat);
let p2 = $scope.c2sqr(b._ne.lng, b._sw.lat);
$scope.vService.api.terranews.loadScans(p1.x,p2.y,p2.x,p1.y);
}
$scope.refreshContentGrid = () => {
if ($scope.state.page != "contents") return;
let b = contentMap.getBounds();
let dlat = b._ne.lat - b._sw.lat;
let dlon = b._ne.lng - b._sw.lng;
$scope.state.content.sqrs = [];
// con
if (dlat + dlon > .015 || $scope.state.content.zoom < defaultContentZoom) { $scope.state.content.action = "drag";
$scope.$apply(); return; }
let p1 = $scope.c2sqr(b._sw.lng, b._ne.lat);
let p2 = $scope.c2sqr(b._ne.lng, b._sw.lat);
let p0 = $scope.sqr2nw(p1.x, p1.y + 1);
let p00 = $scope.sqr2nw(p2.x + 1, p2.y);
// $.ajax({
// url: "/api/land/occupied/" + p1.x + "/" + p2.y + "/" + (p2.x + 1) + "/" + (p1.y + 1),
// contentType: "application/json",
// headers: {
// Authorization: $scope.state.auth,
// },
// method: "GET",
// success: function(data, textStatus, xhr) {
// $scope.occupiedLands = data;
// for (var i in data)
// $scope.state.selectedSquares.filter(e => e !== data[i].x + "-" + data[i].y);
// $scope.$apply();
// }
// });
let sqr_span_lon = p00.lon - p0.lon;
let sqr_span_lat = p0.lat - p00.lat;
let map_w = $("#submission-map").width();
let map_h = $("#submission-map").height();
let sqr_w = map_w / dlon * sqr_span_lon / (p2.x - p1.x + 1);
let sqr_h = map_h / dlat * sqr_span_lat / (p1.y - p2.y + 1);
$("#submission-tiles").css("left", (p0.lon - b._sw.lng) / (b._ne.lng - b._sw.lng) * map_w + "px");
$("#submission-tiles").css("top", (b._ne.lat - p0.lat) / (b._ne.lat - b._sw.lat) * map_h + "px");
for (var x = p1.x; x <= p2.x; ++x)
for (var y = p1.y; y >= p2.y; --y) {
let p = $scope.sqr2nw(x, y);
let _x = (x + tile_s) % tile_s;
$scope.state.content.sqrs.push({ x: _x, y: y, left: (x - p1.x) * sqr_w + "px", top: (p1.y - y) * sqr_h + "px", width: sqr_w + "px", height: sqr_h + "px" });
}
$scope.state.content.reloadingSquare = false;
$scope.calculateLandTaxArea();
$scope.$apply();
};
$scope.c2sqr = (lon, lat) => {
let nlat = lat + 90;
let nlon = lon + 180;
let ilat = nlat / lat_span * tile_s / 2;
let ilon = nlon / lon_span * tile_s;
return { x: Math.floor(ilon), y: Math.floor(ilat) };
}
$scope.sqr2nw = (x, y) => {
return { lat: y / tile_s * lat_span * 2 - 90, lon: x / tile_s * lon_span - 180 };
}
$scope.ellipsis = function(s,d=70) {
if (!s) return "";
if (s.length > d) {
return s.substring(0, d) + '...';
}
return s;
}
$scope.openLink = function(s) {
if (!s) return "";
window.open(s);
if (window.ue4) ue4('openLink',s);
if (window.ue4) ue4('openBrowser',{url:s});
}
$scope.state.openLink = $scope.openLink;
$scope.loginUE4 = function(a,b,c) {
if (!a) return "";
if (!b) return "";
// $("#errorNotif").text("aaww").show();
if (window.ue4) {
// $("#errorNotif").text(JSON.stringify({email:a,password:b,"2fa": c})).show();
window.ue4('login',{email:a,password:b,"2fa": c});
}
}
$scope.ue4 = function(event,d) {
if (window.ue4) window.ue4(event,d);
}
$scope.ue4ClickSound = function() {
if (window.ue4) window.ue4('clickSound');
}
$scope.zoomToSpace = function() {
return;
if (gl.camera) gl.camera.zoom = 0.625;
if ($scope.state.zoom >= 12) map.setZoom(1);
else map.flyTo({ center: map.getCenter(), zoom: 1.05 });
$scope.showSpaceSafari();
}
$scope.state.zoomToSpace = $scope.zoomToSpace;
$scope.zoomToNonPurchase = function(count) {
gl.setState("VP_STATE_MAIN");
$scope.state.showing360 = false;
if ($scope.state.zoom >= defaultZoom) map.setZoom( defaultZoom-1 );
if ($scope.state.zoom <= 1.05) map.setZoom( 5 );
}
$scope.state.zoomToNonPurchase = $scope.zoomToNonPurchase;
$scope.zoomContentToGrid = function(count) {
contentMap.jumpTo({ center: contentMap.getCenter(), zoom: defaultContentZoom });
$scope.state.content.action = "add";
}
$scope.range = function(count) {
if (! count) return [];
return [...Array(Math.ceil(count)).keys()];
}
$scope.zoomToGrid = function(count) {
map.setZoom( defaultZoom );
$scope.state.action = "add";
}
$scope.state.zoomToGrid = $scope.zoomToGrid;
$scope.zoomToNews = function(count) {
map.flyTo({ center: map.getCenter(), zoom: newsZoom });
}
$scope.state.zoomToLayer = function(x) {
let zoom = [1.05,5,12,17][x];
// if (x==0) $scope.zoomToSpace();
//else
map.flyTo({ center: map.getCenter(), zoom: zoom });
}
$scope.goToLandowner = function(url_id) {
window.url_param_2 = url_id;
$scope.state.page = "landowner";
}
$scope.goToPropertyOfLand = function(l) {
if (l.stamped && l.property_url && l.property_url.length>0)
if (confirm("This property is connected to a 360 virtual real estate, do you want to go there? Otherwise you will be brought to the vPark Land Map.")){
window.open(l.property_url);
return;
}
$scope.goToGroup(l.group_id);
}
$scope.copyToClipboard = function(text) {
navigator.clipboard.writeText(text).then(function() {
}, function(err) {
});
}
$scope.copyLandUrlToClipboard = function(groupId) {
$scope.copyToClipboard(`https://www.vpark.io/dashboard/${groupId}`);
}
$scope.state.goToCoord = function(x,y) {
if (x && y) map.setCenter($scope.sqr2nw(x,y)); map.setZoom(defaultZoom);
}
$scope.goToGroup = function(groupId, fly = true, mode) {
if (mode == 'news') { $scope.state.terraMode = 2; $scope.state.dashboard.page='terranews'; }
if (mode == 'land') { $scope.state.terraMode = 0; $scope.state.dashboard.page='buy-land'; }
$scope.state.page = "dashboard";
if ($scope.goToTimer) clearInterval($scope.goToTimer);
$scope.goToTimer = setInterval( () => {
if (!$scope.state.loggedIn) return;
clearInterval($scope.goToTimer);
$scope.vService.ajax.call("/api/land/" + groupId + "/position", "GET", true, null, (data) => {
if (fly) map.flyTo({ center: data, zoom: defaultZoom });
else { map.setCenter(data); map.setZoom(defaultZoom); }
$scope.vService.api.land.showPurchase(null, groupId);
});
}, 1000);
}
$scope.goToCoordInMap = (x,y) => {
let p = $scope.sqr2nw(x,y);
map.flyTo({center:[p.lon,p.lat], zoom: map.getZoom()});
}
$scope.state.goToCoordInMap = $scope.goToCoordInMap;
$scope.circum = (lat) => {
var sNumber = lat
var avar2 = 6378.137;
var bvar3 = 6356.752314;
var tnum1 = Math.pow(avar2, 2);
var cosv2 = Math.cos((sNumber / 180.0) * Math.PI);
cosv2 = cosv2 * avar2;
cosv2 = Math.pow(cosv2, 2);
var sinv3 = Math.sin((sNumber / 180.0) * Math.PI);
sinv3 = sinv3 * bvar3;
sinv3 = Math.pow(sinv3, 2);
var cNum3 = cosv2 + sinv3;
cNum3 = Math.pow(cNum3, 1 / 2);
cNum3 = tnum1 / cNum3;
return 2 * Math.PI * Math.cos((sNumber / 180.0) * Math.PI) * cNum3;
}
$scope.clearSelectedGroup = () => {
$scope.state.selectedGroup = null;
$scope.vService.api.land.clearPreparedLands();
// $scope.state.pages = ["map","land"];
}
$scope.cutdownTree = (s) => {
if (!$scope.isOccupied(s))
$scope.vService.api.land.cutdownTree(s.x,s.y, (d) => {
$scope.cutLands = Object.assign({}, $scope.cutLands, d.data);
});
}
$scope.buildRealEstate = (s) => {
let occ = $scope.getOccupiedSquareData(s);
if (!occ) return;
if (occ.address && occ.address.length>5 && !$scope.isMyBuilding(occ)) return;
if (!occ.address && !$scope.isMyLand(occ) && !$scope.isMyLease(occ)) return;
$scope.state.selectedToBuild = $scope.state.selectedToBuild === s ? null : s;
$scope.state.selectedREToBuild = $scope.state.selectedToBuild ? occ : null;
if ($scope.state.selectedREToBuild)
$scope.state.pages = occ.address && occ.address.length>5 /* assume good address is hash */ ? ["map","land",s.x+":"+s.y,"real-estate","setting"] : ["map","land",s.x+":"+s.y,"build"];
// if (!$scope.isOccupied(s))
// $scope.vService.api.land.cutdownTree(s.x,s.y);
}
$scope.tileMouseDown = (s) => {
if ($scope.isOccupied(s)) $scope.vService.api.land.showPurchase(s);
else { $scope.state.mousedown = true;
$scope.hovering(s.x, s.y); }
}
$scope.contentTileMouseDown = (s) => {
$scope.state.mousedown = true;
$scope.contentHovering(s.x, s.y);
}
$("#tiles").mousedown((e) => { $scope.state.mousedown = true; });
$("#tiles").mouseup((e) => { $scope.state.mousedown = false; });
$("#submission-tiles").mousedown((e) => { $scope.state.mousedown = true; });
$("#submission-tiles").mouseup((e) => { $scope.state.mousedown = false; });
$scope.state.curX = 0;
$scope.state.curY = 0;
$scope.hovering = (x, y) => {
$scope.state.curX = x;
$scope.state.curY = y;
if ($scope.state.action == 'add')
$scope.select(x, y);
if ($scope.state.action == 'remove')
$scope.deselect(x, y);
}
$scope.contentHovering = (x, y) => {
if ($scope.state.content.action == 'add')
$scope.contentSelect(x, y);
if ($scope.state.content.action == 'remove')
$scope.contentDeselect(x, y);
}
$scope.minX = -1;
$scope.minY = -1;
$scope.selected = (x, y) => {
return $scope.state.selectedSquares.indexOf(x + "-" + y) >= 0;
}
$scope.contentSelected = (x, y) => {
return $scope.state.content.selectedSquares.indexOf(x + "-" + y) >= 0;
}
$scope.calculateLandTaxArea = () => {
let sqrs = $scope.state.content.sqrs;
if (!sqrs || !$scope.state.currentContent) return;
$scope.state.content.landTax = new Array(sqrs.length).fill(0);
let lt = $scope.state.content.landTax;
var rad = parseInt($scope.state.currentContent.distribution_radius);
if ($scope.state.content.selectedSquares)
for (var i in sqrs) {
let x = sqrs[i].x;
let y = sqrs[i].y;
$scope.state.content.selectedSquares.forEach( (e) => {
let xy = e.split('-');
let j = parseInt(xy[0])-x;
let k = parseInt(xy[1])-y;
let dis = Math.sqrt(k*k+j*j);
if (dis <= rad) lt[i] = Math.max(lt[i], (rad+1-dis)/rad/2);
} );
}
}
$scope.contentLandTaxArea = (x, y, index) => {
if ($scope.state.content.selectedSquares.indexOf(x + "-" + y) >= 0) return 0;
if (!$scope.state.content.landTax) return 0;
return $scope.state.content.landTax[index];
}
$scope.state.countryPrices = {};
$scope.getCountryMarketValue = (country) => {
if ($scope.state.countryPrices[country]) return $scope.state.countryPrices[country].market_value;
return $scope.vService.api.land.getCountryPrice(country);
}
$scope.getUserTotalSquares = (l) => {
var x = 0; for (var i in $scope.state.user.lands) x += $scope.state.user.lands[i].volume; return x;
}
$scope.getUserTotalSquaresPaid = (l) => {
var x = 0; for (var i in $scope.state.user.lands) x += $scope.state.user.lands[i].volume*$scope.state.user.lands[i].price; return x;
}
$scope.getUserTotalSquares = (l) => {
var x = 0; for (var i in $scope.state.user.lands) x += $scope.state.user.lands[i].volume; return x;
}
$scope.getOccupiedSquareData = (s) => {
// return $scope.occupiedLands.find((e) => e.x == s.x && e.y == s.y);
return $scope.occupiedLands[s.x+"-"+s.y];
}
$scope.isOccupied = (s) => {
return $scope.getOccupiedSquareData(s) != undefined;
}
$scope.isCutArea = (s) => {
return $scope.state.action == 'cut' && (new THREE.Vector3(s.x,s.y,0)).sub(new THREE.Vector3($scope.state.curX,$scope.state.curY,0)).length() <= 5;
}
$scope.cutLands = {};
$scope.isCut = (s) => {
return $scope.cutLands[s.x+"-"+s.y];
}
$scope.isBuilt = (s) => {
if(s.x) s = $scope.getOccupiedSquareData(s);
return s && s.type > 1;
}
$scope.isMyBuilding = (s) => {
if(s.x) s = $scope.getOccupiedSquareData(s);
return s && s.builder == $scope.state.user.hash;
}
$scope.isMyLand = (s) => {
if(s.x) s = $scope.getOccupiedSquareData(s);
return s && s.owner == $scope.state.user.hash;
}
$scope.isMyLease = (s) => {
if(s.x) s = $scope.getOccupiedSquareData(s);
return s && s.renter == 2;
}
$scope.isForLease = (s) => {
if(s.x) s = $scope.getOccupiedSquareData(s);
return s && s.lease_price > 0;
}
$scope.isPathwaysOrBuilt = (s) => {
return $scope.properties.find((e) => e.x == s.x && e.y == s.y);
}
$scope.countryCodeToName = (c) => {
let a = {af:"Afghanistan",ax:"Åland Islands",al:"Albania",dz:"Algeria",as:"American Samoa",ad:"Andorra",ao:"Angola",ai:"Anguilla",aq:"Antarctica",ag:"Antigua and Barbuda",ar:"Argentina",am:"Armenia",aw:"Aruba",au:"Australia",at:"Austria",az:"Azerbaijan",bs:"Bahamas",bh:"Bahrain",bd:"Bangladesh",bb:"Barbados",by:"Belarus",be:"Belgium",bz:"Belize",bj:"Benin",bm:"Bermuda",bt:"Bhutan",bo:"Bolivia, Plurinational State of",bq:"Bonaire, Sint Eustatius and Saba",ba:"Bosnia and Herzegovina",bw:"Botswana",bv:"Bouvet Island",br:"Brazil",io:"British Indian Ocean Territory",bn:"Brunei Darussalam",bg:"Bulgaria",bf:"Burkina Faso",bi:"Burundi",kh:"Cambodia",cm:"Cameroon",ca:"Canada",cv:"Cape Verde",ky:"Cayman Islands",cf:"Central African Republic",td:"Chad",cl:"Chile",cn:"China",cx:"Christmas Island",cc:"Cocos (Keeling) Islands",co:"Colombia",km:"Comoros",cg:"Congo",cd:"Congo, the Democratic Republic of the",ck:"Cook Islands",cr:"Costa Rica",ci:"Côte d'Ivoire",hr:"Croatia",cu:"Cuba",cw:"Curaçao",cy:"Cyprus",cz:"Czech Republic",dk:"Denmark",dj:"Djibouti",dm:"Dominica",do:"Dominican Republic",ec:"Ecuador",eg:"Egypt",sv:"El Salvador",gq:"Equatorial Guinea",er:"Eritrea",ee:"Estonia",et:"Ethiopia",fk:"Falkland Islands (Malvinas)",fo:"Faroe Islands",fj:"Fiji",fi:"Finland",fr:"France",gf:"French Guiana",pf:"French Polynesia",tf:"French Southern Territories",ga:"Gabon",gm:"Gambia",ge:"Georgia",de:"Germany",gh:"Ghana",gi:"Gibraltar",gr:"Greece",gl:"Greenland",gd:"Grenada",gp:"Guadeloupe",gu:"Guam",gt:"Guatemala",gg:"Guernsey",gn:"Guinea",gw:"Guinea-Bissau",gy:"Guyana",ht:"Haiti",hm:"Heard Island and McDonald Islands",va:"Holy See (Vatican City State)",hn:"Honduras",hk:"Hong Kong",hu:"Hungary",is:"Iceland",in:"India",id:"Indonesia",ir:"Iran, Islamic Republic of",iq:"Iraq",ie:"Ireland",im:"Isle of Man",il:"Israel",it:"Italy",jm:"Jamaica",jp:"Japan",je:"Jersey",jo:"Jordan",kz:"Kazakhstan",ke:"Kenya",ki:"Kiribati",kp:"Korea, Democratic People's Republic of",kr:"Korea, Republic of",kw:"Kuwait",kg:"Kyrgyzstan",la:"Lao People's Democratic Republic",lv:"Latvia",lb:"Lebanon",ls:"Lesotho",lr:"Liberia",ly:"Libya",li:"Liechtenstein",lt:"Lithuania",lu:"Luxembourg",mo:"Macao",mk:"Macedonia, the former Yugoslav Republic of",mg:"Madagascar",mw:"Malawi",my:"Malaysia",mv:"Maldives",ml:"Mali",mt:"Malta",mh:"Marshall Islands",mq:"Martinique",mr:"Mauritania",mu:"Mauritius",yt:"Mayotte",mx:"Mexico",fm:"Micronesia, Federated States of",md:"Moldova, Republic of",mc:"Monaco",mn:"Mongolia",me:"Montenegro",ms:"Montserrat",ma:"Morocco",mz:"Mozambique",mm:"Myanmar",na:"Namibia",nr:"Nauru",np:"Nepal",nl:"Netherlands",nc:"New Caledonia",nz:"New Zealand",ni:"Nicaragua",ne:"Niger",ng:"Nigeria",nu:"Niue",nf:"Norfolk Island",mp:"Northern Mariana Islands",no:"Norway",om:"Oman",pk:"Pakistan",pw:"Palau",ps:"Palestinian Territory, Occupied",pa:"Panama",pg:"Papua New Guinea",py:"Paraguay",pe:"Peru",ph:"Philippines",pn:"Pitcairn",pl:"Poland",pt:"Portugal",pr:"Puerto Rico",qa:"Qatar",re:"Réunion",ro:"Romania",ru:"Russian Federation",rw:"Rwanda",bl:"Saint Barthélemy",sh:"Saint Helena, Ascension and Tristan da Cunha",kn:"Saint Kitts and Nevis",lc:"Saint Lucia",mf:"Saint Martin (French part)",pm:"Saint Pierre and Miquelon",vc:"Saint Vincent and the Grenadines",ws:"Samoa",sm:"San Marino",st:"Sao Tome and Principe",sa:"Saudi Arabia",sn:"Senegal",rs:"Serbia",sc:"Seychelles",sl:"Sierra Leone",sg:"Singapore",sx:"Sint Maarten (Dutch part)",sk:"Slovakia",si:"Slovenia",sb:"Solomon Islands",so:"Somalia",za:"South Africa",gs:"South Georgia and the South Sandwich Islands",ss:"South Sudan",es:"Spain",lk:"Sri Lanka",sd:"Sudan",sr:"Suriname",sj:"Svalbard and Jan Mayen",sz:"Swaziland",se:"Sweden",ch:"Switzerland",sy:"Syrian Arab Republic",tw:"Taiwan",tj:"Tajikistan",tz:"Tanzania, United Republic of",th:"Thailand",tl:"Timor-Leste",tg:"Togo",tk:"Tokelau",to:"Tonga",tt:"Trinidad and Tobago",tn:"Tunisia",tr:"Turkey",tm:"Turkmenistan",tc:"Turks and Caicos Islands",tv:"Tuvalu",ug:"Uganda",ua:"Ukraine",ae:"United Arab Emirates",gb:"United Kingdom",us:"United States",um:"United States Minor Outlying Islands",uy:"Uruguay",uz:"Uzbekistan",vu:"Vanuatu",ve:"Venezuela, Bolivarian Republic of",vn:"Viet Nam",vg:"Virgin Islands, British",vi:"Virgin Islands, U.S.",wf:"Wallis and Futuna",eh:"Western Sahara",ye:"Yemen",zm:"Zambia",zw:"Zimbabwe",unknown:"Unknown country"};
return a[c];
}
$scope.flagIcon = (c, size='24x18') => {
if (!c) return null;
return c == "unknown" ? "/images/icons/flag-black.svg" : `https://flagcdn.com/${size}/`+c+".png";//"https://www.countryflags.io/" + c + "/flat/24.png";
}
$scope.className = (c) => {
c = Math.floor(c);
let classes = ["Meteor", "Rhodium", "Platinum", "Gold", "Ruthenium", "Iridium", "Osmium", "Palladium", "Rhenium", "Silver", "Carbon"]
return c - 1 < classes.length ? classes[c - 1] : "Carbon";
}
$scope.select = (x, y) => {
if ($scope.state.mousedown) {
$scope.vService.api.land.clearPreparedLands()
$scope.deselect(x, y);
if ($scope.state.selectedSquares.length == 0) {
$scope.minX = x;
$scope.minY = y;
$scope.vService.api.land.getLandPrice(x, y);
} else if (Math.sqrt(Math.pow(x - $scope.minX, 2) + Math.pow(y - $scope.minY, 2)) > 500) {
console.log("You've selected a square too far from the other selected ones.");
return;
}
if ($scope.state.selectedSquares.length >= 500) return;
if ((x+"-"+y) in $scope.occupiedLands) return;
// if ($scope.occupiedLands.find((e) => e.x == x && e.y == y)) return;
$scope.state.selectedSquares.push(x + "-" + y);
$scope.clearSelectedGroup();
}
}
$scope.deselect = (x, y) => {
if ($scope.state.mousedown) {
$scope.state.selectedSquares = $scope.state.selectedSquares.filter(e => e !== x + "-" + y);
if ($scope.state.selectedSquares.length == 0) $scope.state.landInfo = null;
$scope.clearSelectedGroup();
}
}
$scope.contentSelect = (x, y) => {
if ($scope.state.mousedown) {
$scope.contentDeselect(x, y,false);
if ($scope.state.content.selectedSquares.length == 0) {
$scope.minX = x;
$scope.minY = y;
} else if (Math.sqrt(Math.pow(x - $scope.minX, 2) + Math.pow(y - $scope.minY, 2)) > 100) {
console.log("You've selected a square too far from the other selected ones.");
return;
}
if ($scope.state.content.selectedSquares.length >= 100) return;
$scope.state.content.selectedSquares.push(x + "-" + y);
$scope.calculateLandTaxArea();
// console.log("selected");
}
}
$scope.contentDeselect = (x, y, calc=true) => {
if ($scope.state.mousedown) {
$scope.state.content.selectedSquares = $scope.state.content.selectedSquares.filter(e => e !== x + "-" + y);
if ($scope.state.content.selectedSquares.length == 0) $scope.state.content.landInfo = null;
}
if (calc) $scope.calculateLandTaxArea();
}
$scope.clearSelected = () => {
$scope.state.selectedSquares = [];
$scope.state.landInfo = null;
$scope.clearSelectedGroup();
}
$scope.allowedPurchaseFromUser = (sellerHash) => {
if ($scope.state.user.hash == sellerHash) return false;
return !$scope.state.user.recentPurchases[sellerHash] || $scope.state.user.recentPurchases[sellerHash] < 5;
}
$scope.countUserOffers = () => { var c = 0; if ($scope.state.user) $scope.state.user.lands.forEach( e => c+= e.offers ); return c; }
$scope.buyLandTick = new Date();
$scope.lastRandomLandCoords = [];
$scope.state.profileUpdated = true;
$scope.savePaypal = () => {
$scope.state.profileUpdated = true;
$scope.$apply();
}
$scope.validate = (init=true) => {
$scope.vService.auth.validate( () => {
if ($scope.vService.loggedIn)
$scope.vService.ajax.call(`/api/user?${location.pathname.indexOf('/dashboard') >= 0 ? "" : "simple=true" }`, "GET", true, null, (data) => {
if (!data.email) return;
$scope.state.loggedIn = true;
$scope.state.user = data;
$scope.state.vParkUserConnected = data.vpark_id;
$scope.getVerificationStatus();
$scope.$apply();
if (init)
$scope.initializePage()
});
}, () => { $scope.state.page = "dashboard"; });
}
$scope.loadLandowner = (hash) => {
$scope.vService.ajax.call("/api/user/"+hash, "GET", true, null, (data) => {
$scope.state.landowner = data;
$scope.$apply();
});
}
$scope.validate();
$scope.login = (email, password) => {
if (!$scope.state.user.email || $scope.state.user.email.length < 6) { alert("Please enter a valid email"); return; }
if (!$scope.state.user.password || $scope.state.user.password.length < 8) { alert("Password must be at least 8 characters."); return; }
grecaptcha.execute('6LcRjFIaAAAAANdjiJ8hATjdcpBVdik9ITN6L8L8', {action: 'submit'}).then(function(token) {
$scope.state.user.capcha = token;
$scope.vService.auth.login( $scope.state.user, () => {
// $scope.vService.ajax.call("/api/user", "GET", true, null, (data) => {
// $scope.state.loggedIn = true;
// $scope.state.user = data;
// $scope.state.vParkUserConnected = data.vpark_id;
// // $scope.getVerificationStatus();
// $scope.$apply();
location.reload();
// });
});
});
}
$scope.signout = () => {
$scope.vService.auth.logout( () => {
$scope.state.loggedIn = false;
$scope.$apply();
location.href="/dashboard";
});
}
$scope.click2FAUrl = () => {
window.open($scope.state.qr.url);
}
$scope.userTotalLandOwned = () => {
var x = 0;
$scope.state.user.lands.forEach(e => x += e.volume);
return x;
}
$scope.state.marketSort = "date";
$scope.state.lastMarketSort = "date";
$scope.state.marketCountry = "unknown";
$scope.state.lastMarketCountry = "unknown";
$scope.state.marketOffer = null;
$scope.state.lastMarketOffer = null;
$scope.state.lastMarketMaxPrice = null;
$scope.state.lastMarketMinPrice = null;
$scope.state.lastMarketAsc = null;
$scope.state.lastMarketSearch = null;
$scope.state.initMarketFilter = (max = 100) => {
$scope.state.marketMinPrice = 1;
$scope.state.marketMaxPrice = max;
$( "#price-slider-range" ).slider({
range: true,
min: $scope.state.marketMinPrice,
max: $scope.state.marketMaxPrice,
values: [ $scope.state.marketMinPrice, $scope.state.marketMaxPrice ],
slide: function( event, ui ) {
$scope.state.marketMinPrice = ui.values[ 0 ];
$scope.state.marketMaxPrice = ui.values[ 1 ];
}
});
}
$scope.state.changeMarketMaxPrice = () => {
let x = parseInt(prompt("Set new max value..."));
if (isNaN(x)) return;
if (x < 10) return;
if (x > 10000000) return;
$( "#price-slider-range" ).slider("destroy");
$scope.initMarketFilter(x);
}
$scope.gotoMarketPage = () => {
var s = prompt("Enter a page number between 1 to "+Math.floor($scope.state.purchasableLands.available/$scope.state.purchasableLands.limit+1));
if (!s || isNaN(s) || !Number.isInteger(parseInt(s))) return;
s = parseInt(s)-1;
if (s < 0 || s > Math.floor($scope.state.purchasableLands.available/$scope.state.purchasableLands.limit)) return;
$scope.vService.api.land.loadMarket($scope.state.marketSort,$scope.state.purchasableLands.limit*s);
}
$scope.formatDate = (t) => {
return moment(t).format('MM/DD LT');
}
$scope.transactionType = (t) => {
let _ = {"land_tax": "Land Tax", "land_sale": "Land", "class_residual":"Residual", "referral": "Referral", "deposit": "Deposit", "withdrawal": "Withdraw", "refund": "Refund"};
return _[t] ? _[t] : t;
}
$scope.loadStats = () => {
// if (!$scope.vService.loggedIn) return;
$scope.vService.ajax.call("/api/land/stats/countries", "GET", false, null, (data) => {
$scope.state.countryStats = data["countries"];
$scope.state.userCountryStats = data["user_countries"];
$scope.state.big_supporters = data["big_supporters"];
$scope.state.gdp = Math.round(data["gdp"]);
$scope.state.population = data["population"];
$scope.state.crimes = data["crimes"];
for (var i in data) {
data[i].price = parseFloat(data[i].price);
}
});
}
$scope.verifyIdentity = () => {
if ($scope.state.page.indexOf("api/world") <0) ;
$scope.vService.ajax.call("/api/profile/verification/id/jumio", "POST", true, {}, (data) => {
location.href = data;
if ($scope.refreshVerifyStatusTimer) {
clearInterval($scope.refreshVerifyStatusTimer);
$scope.refreshVerifyStatusTimer = setInterval( () => {$scope.getVerificationStatus()}, 5000 );
}
});
}
$scope.state.verifyIdentity = $scope.verifyIdentity;
$scope.getVerificationStatus = () => {
if (!$scope.state.loggedIn) return;
if ($scope.state.page.indexOf("api/world") <0)
$scope.vService.ajax.call("/api/profile/verification/id/jumio", "GET", true, {}, (data) => {
$scope.state.verification = data.status;
$scope.state.prev_failure = data.prev_failure;
if (data.status == "na" || data.status == "verified") {
if ($scope.refreshVerifyStatusTimer) {
clearInterval($scope.refreshVerifyStatusTimer);
$scope.refreshVerifyStatusTimer = null;
}
if (data.status == "failed") location.reload();
}
$scope.$apply();
});
}
$scope.refreshVerifyStatusTimer = setInterval( () => {$scope.getVerificationStatus()}, 5000 );
$scope.nearIndex = (x) => {
return Math.abs(x) < 4;
};
$scope.getKeys = (v) => { return Object.keys(v); };
$scope.timeToLandChat = (cmas) => {
today = new Date();
// if (today.getMonth() >= 6 && today.getDate() > 25) {
// cmas.setFullYear(cmas.getFullYear()+1);
// }
var one_sec=1000;
var sec = Math.ceil((cmas.getTime()-today.getTime())/one_sec);
return [sec % 60, Math.ceil(sec / 60) % 60, Math.ceil(sec / 60/60) % 24, Math.ceil(sec / 60/60/24)];
}
var today = new Date();
$scope.state.landchats = {countdown: $scope.timeToLandChat(new Date(today.getFullYear(), 7, 0, 12, 0, 0)),contents: []};
$scope.state.terranews = {countdown: $scope.timeToLandChat(new Date(today.getFullYear(), 7, 5, 12, 0, 0)),contents: [], newContent: {tokens:1}};
$scope.state.digicards = {newCard: {royalty:10} };
$scope.state.shop = {};
$scope.state.dashboard = {
// prompt: {
// title:"title", question: "uwqbflublfq iqw fiu qiuf iqlw", button:"submit",
// callback: () => {},
// cancel: () => {},
// }
};
$scope.state.landfunds = {page: 0 };
$scope.LCScrollDown = () => {
$(".landchatContent").each( (i,c) => $(c).animate({scrollTop: c.scrollHeight-c.clientHeight}, 1000));
}
$scope.TNGetTotalCastedVotes = (poll) => {
if (!poll.results) return 0;
var c = 0;
poll.results.forEach(r => c += r.votes);
return c;
}
$scope.TNGetVotes = (poll, option) => {
if (!poll.results) return 0;
let r = poll.results.find(r => r.option_id == option.id);
return r ? r.votes : 0;
}
$scope.hasGlobalToken = () => {
let x = $scope.state.user.country_based_voken.find( !e.country );
if (x) return true; return false;
}
$scope.changeNewContentFile = () => {
let input = $('#tn_content_file')[0];
if (input.files[0].size > 5*1024*1024) {
alert("File is too big. 5 MB Max.");
input.value = '';
return;
}
$scope.state.terranews.newContent.file = input.files[0];
$("#new_content_thumbnail").css("background", "url("+URL.createObjectURL(input.files[0])+") no-repeat center center / cover");
$scope.$apply();
};
$scope.change3dstuff = () => {
let input = $('#nft-thumb')[0];
gl.currentState.call("uploadThumb",URL.createObjectURL(input.files[0]));
}
$scope.changeNewDigicardFile = () => {
let input = $('#digicard_file')[0];
if (input.files[0].size > 50*1024*1024) {
alert("Thumbnail is too big. 50 MB Max.");
input.value = '';
return;
}
$scope.state.digicards.newCard.file = input.files[0];
$("#new_digicard_thumbnail").css("background", "var(--vp-orange)");
// $("#new_digicard_thumbnail").css("background", "url("+URL.createObjectURL(input.files[0])+") no-repeat center center / cover");
$scope.$apply();
};
$scope.changeNewDigicardThumb = () => {
let input = $('#digicard_thumb')[0];
if (input.files[0].size > 50*1024*1024) {
alert("Original file is too big. 50MB Max.");
input.value = '';
return;
}
$scope.state.digicards.newCard.thumbnail = input.files[0];
// $("#new_digicard_thumb_prev").css("background", "var(--vp-orange)");
$("#new_digicard_thumb_prev").css("background", "url("+URL.createObjectURL(input.files[0])+") no-repeat center center / cover");
$scope.$apply();
};
$scope.changePostImage = () => {
let input = $('#post_image_thumb')[0];
if (input.files[0].size > 50*1024*1024) {
alert("Original file is too big. 50MB Max.");
input.value = '';
return;
}
$scope.state.social.new.data = input.files[0];
// $("#new_digicard_thumb_prev").css("background", "var(--vp-orange)");
$("#new_post_image").css("background", "url("+URL.createObjectURL(input.files[0])+") no-repeat center center / cover");
$scope.$apply();
};
$scope.cdtsInShop = (page) => {
if (!page) {
if ($scope.state.dashboard.subpage) {
page = $scope.state.dashboard.subpage;
if (page == 'mycards') return $scope.state.user.my_cdts;
return page == 'gallery' ? $scope.state.usableCDTs : $scope.state.user.cdts;
}
}
if (!page) page = $scope.state.shop.page;
if (page == 4) return $scope.state.user.my_cdts;
return page == 1 ? $scope.state.usableCDTs : $scope.state.user.cdts;
}
$scope.digicardsToList = (page) => {
if (!page) {
if ($scope.state.dashboard.subpage) {
page = $scope.state.dashboard.subpage;
if (page == 'mycards') return $scope.state.user.my_digicards;
return page == 'gallery' ? $scope.state.usableDigicards : $scope.state.user.digicards;
}
}
if (!page) page = $scope.state.digicards.page;
if (page == 4) return $scope.state.user.my_digicards;
return page == 1 ? $scope.state.usableDigicards : $scope.state.user.digicards;
}
$scope.investableLandToList = () => {
if ($scope.state.landfunds.page == 3) return $scope.state.user.lands.filter( e => e.fund );
return $scope.state.landfunds.page == 1 ? $scope.state.investableLands : $scope.state.myInvestedLands;
}
$scope.state.cardSort = 'date';
$scope.state.cardFilter = '';
$scope.state.cardSearch = '';
$scope.state.contents = [];
$scope.loadContents = () => {
if (!$scope.state.loggedIn) return;
$scope.vService.ajax.call("/api/contents", "GET", true, null, (data) => {
$scope.state.contents = data;
$scope.$apply();
});
};
$scope.state.creatingContent = false;
$scope.createContent = () => {
if ($scope.state.creatingContent) return;
if (!$scope.state.loggedIn) return;
$scope.state.creatingContent = true;
$scope.vService.ajax.call("/api/contents", "POST", true, null, (data) => {
$scope.state.contents.unshift(data);
$scope.selectContent($scope.state.contents[0]);
$scope.state.creatingContent = false;
},
(data) => {
$scope.state.creatingContent = false;
});
};
$scope.state.currentContent = null;
$scope.calculateLandTaxArea();
$scope.selectContent = (c) => {
$scope.state.currentContent = c;
$scope.state.content_page_index = 0;
$scope.state.content.selectedSquares = [];
if (c) {
c.distribution_radius = ""+c.distribution_radius;
$scope.state.content.selectedSquares = c.lands;
}
if (c && c.lands && c.lands.length){
let p = c.lands[0].split("-");
p = $scope.sqr2nw(p[0],p[1]);
contentMap.jumpTo({ center: p, zoom: defaultContentZoom });
}
$scope.calculateLandTaxArea();
$scope.$apply();
}
$scope.deleteContent = (c) => {
if ($scope.state.creatingContent) return;
if (!$scope.state.loggedIn) return;
if (!confirm("This action is final.")) return;
$scope.vService.ajax.call("/api/contents/"+c.hash, "DELETE", true, null, (data) => {
$scope.state.contents = data;
$scope.selectContent();
});
}
$scope.submitContent = (c) => {
if (!$scope.state.loggedIn) return;
if ($scope.state.updatingContent) return;
if (c.modified) return;
if (!c.description || c.description.length < 50) { alert("Please write more discription"); return; }
if (!c.address || c.address.length < 10) { alert("Please enter the address field."); return; }
if (!c.title || c.title.length < 10) { alert("Please write a longer title"); return; }
if (c.screened) {
if (!$scope.state.content.selectedSquares.length) { alert("Please select the landtax"); return; }
if (!c.exif_picture) { alert("Invalid document entry 1"); return; }
if (!c.thumbnail) { alert("Invalid document entry 2"); return; }
if (c.type=='static' && !c.wallpaper) { alert("Invalid document entry 3"); return; }
// if (c.type=='static' && !c.pano_lr) { alert("Invalid document entry 4"); return; }
if (c.type=='static' && !c.pano_vlr) { alert("Invalid document entry 5"); return; }
if (c.type=='static' && !c.pano_hr) { alert("Invalid document entry 6"); return; }
// if (!c.pano_vhr) { alert("Invalid document entry 7"); return; }
}
$scope.state.updatingContent = true;
$scope.vService.ajax.call("/api/contents/"+c.hash, "POST", true, null, (data) => {
c.submitted = true;
$scope.state.updatingContent = false;
$scope.$apply();
}, (data) => {
$scope.state.updatingContent = false;
});
}
$scope.updateContent = (c) => {
if (!$scope.state.loggedIn) return;
if ($scope.state.updatingContent) return;
$scope.state.updatingContent = true;
c.lands = $scope.state.content.selectedSquares;
$scope.vService.ajax.call("/api/contents/"+c.hash, "PUT", true, c, (data) => {
c.modified = false;
$scope.state.updatingContent = false;
$scope.$apply();
}, (data) => {
$scope.state.updatingContent = false;
});
}
$scope.loadContent = (hash) => {
if (!$scope.state.loggedIn) return;
$scope.vService.ajax.call("/api/contents/"+hash, "GET", true, null, (data) => {
$scope.state.content.viewMapOnly = true;
$scope.selectContent(data);
}, (data) => {
$scope.state.updatingContent = false;
});
}
$scope.state.newVote = {industry:"", entity:'consumer'};
$scope.postForVotes = () => {
alert('Posting is now closed.'); return;
if (!$scope.state.newVote.industry.length) { alert('Please select an idea type.'); return; }
if (!$scope.state.newVote.title || $scope.state.newVote.title.length < 5) { alert('Invalid title'); return; }
if (!$scope.state.newVote.story || $scope.state.newVote.story.length < 100) { alert('Please write more in the story field'); return; }
if (!$scope.state.newVote.agree) { alert('You must agree to the IP release statement.'); return; }
$scope.vService.ajax.call("/api/voting/posts", "POST", true, $scope.state.newVote, (data) => {
alert("Thanks for your submission, we will review it.");
$scope.state.newVote = {industry:"", entity:'consumer'};
$scope.$apply();
});
}
$scope.getVotes = () => {
$scope.vService.ajax.call("/api/voting/"+($scope.state.loggedIn ? 'posts' : 'public-posts'), "GET", true, null, (data) => {
$scope.state.voting.posts = data;
$scope.$apply();
});
}
$scope.makeVote = (p, vote) => {
if (!$scope.state.loggedIn) { alert("Please create an account to vote"); return; }
if (p.mine) return;
var d = {};
if (vote=='price') {
d[vote] = prompt("Enter a price you think it should be priced at. Please consider the value seriously.");
if (d[vote]=='' || isNaN(d[vote])) return;
}
p["my_"+vote] = p["my_"+vote] ? 0 : 1;
if (vote != 'price') d[vote] = p["my_"+vote];
else p["my_"+vote] = 1;
p["total_"+vote] += p["my_"+vote] ? 1 : -1;
$scope.vService.ajax.call("/api/voting/posts/"+p.id+"/vote", "POST", true, d, (data) => {
$scope.$apply();
});
}
$scope.encodePost = (p) => {
return "https://land.vpark.io/link/votes/"+encodeURIComponent(p.url_path);
};
$scope.showCreationTool = () => {
$scope.state.showingCreationTool = true;
gl.setState("VP_STATE_CREATION");
// $(window).trigger('resize');
window.dispatchEvent(new Event('resize'));
}
$scope.show360 = (x) => {
$scope.state.showing360 = x;
gl.setState("VP_STATE_360");
gl.currentState.call("set360Video", x);
$scope.state.pages=["home","360",x.hash+'__________________________________'];
// if (gl.camera) gl.camera.zoom = 1;
// if ($scope.state.zoom >= 5) map.setZoom(1);
// else map.flyTo({ center: map.getCenter(), zoom: 1.05 });
}
$scope.show360Store = (x) => {
$scope.state.showing360 = true;
gl.setState("VP_STATE_STORE");
$scope.vService.ajax.call("/api/land/"+x.group_id, "GET", true, null, (data) => {
gl.currentState.call("setLands", [$scope.state.user.lands,data]);
});
}
$scope.state.show360 = $scope.show360;
$scope.state.show360Store = $scope.show360Store;
$scope.showSpaceSafari = () => {
return;
$scope.state.showing360 = false;
$scope.state.showingCreationTool = false;
gl.setState("VP_STATE_MAIN");
}
$scope.showCreationProgress = (img) => {
if (!img) {
let input = $('#digicard_thumb')[0];
img = URL.createObjectURL(input.files[0]);
}
gl.currentState.call("uploadThumb", img);
}
$scope.hideCreationProgress = () => {
gl.currentState.call("hideDisplay", null);
}
$scope.state.myPropFilters = {};
$scope.myFilteredProperties = () => {
return $scope.state.user.lands.filter(l =>
(!$scope.state.myPropFilters.offers || l.offers>0) &&
(!$scope.state.myPropFilters.listed || l.listed>0) &&
(!$scope.state.myPropFilters.country || $scope.state.myPropFilters.country == "" || l.country==$scope.state.myPropFilters.country)
);
}
$scope.updateLeftPanel = () => {
if (!$scope.state.pages.length) return;
if ($scope.state.pages[0] != "map") return;
if ($scope.state.selectedToBuild) return;
// if ($scope.state.zoom <= 1.05) $scope.state.pages = ["mission"];
else if ($scope.state.zoom <= 5) $scope.state.pages = ["map", "news"];
else if ($scope.inRingZoomZone() && $scope.inCityZoomZone()) $scope.state.pages = ["map", "route"];
else
$scope.state.pages = ["map", "land"];
}
$scope.formatNewsDate = (d) => {
return moment(d).format("MMM DD, YYYY");
};
$scope.formatChatDate = (d) => {
return moment(d).format("MM/DD/YYYY");
};
$scope.updateSelectedRoute = (hash,dist) => {
if (hash.length > 10)
vService.api.route.get(hash,dist, (d) => {
$scope.state.world.route = d;
}, () => {$scope.state.world.route.price = 'invalid';} );
else $scope.state.world.route.price = 'invalid';
};
$scope.texts = {
"announcement": [
{title: 'Welcome to the new discussion board', shown:true, edited: new Date("July 23 2021"),
content: "
Test
",},
],
"news": [
{title: 'We are NOT a B2C Company', shown:true, edited: new Date("April 26 2021"),
content: "
We are a B2B product company to help business adopt 360 VR because we believe this will protect many businesses and open thousands business opportunities in the future. Our alpha platform for example is usable for some businesses with just clicking a few buttons. The app install section is just like an app store that allows different businesses get customized feature for their own particular needs. Our landtax will be given for those helping to use or share about our products where a 360 scan from their land location is required. This resembles a regional sales reps commission structure in an enterprise. This landtax is the very value of our virtual land. We are trying to provide VREaaS to companies to solve their issues or to bring their business to the next level. We helped tourism in the past to provide the best experience to their customers, not to sell the services to our landowners as the landowners mostly fall under a demographics where most non-fintech businesses won't go after anyway. Although it is not set yet, we probably will share decks to landowners so they can easily share our product to any businesses that require 360 videos around their lands. This might take time and require manpowers of many landowners to help spread the word when one product market fit occurs. If we decide to make a separate B2C product ourselves just like the vPark Land itself, it is most probably a go-to-market strategy, but our mission remains the same unless stated otherwise. Since there are rumors out there about our pricing, we want to clarify that the current pricing formula is exactly the same pricing formula when we launched vPark Land in Mid January, this happens because the algorithm simply takes quota for discount into account. To make sure landowners can receive an accurate information, we have decided to make an announcement ourselves on this site or through email. If you hear about vPark from others that we are trying to compete with Earth2, that is a false information and the source might mistakenly think we are making a game, please read our site or contact us via email or chat before buying our vPark Lands.
We've removed the suggestion system and replace it with a bidding system. Seller must accept a bid from the \"my properties\" page, then buyer must make a purchase with the new price.
I made a bold decision a few days after I launched vPark land to change the pricing system because there were immediate thousands of buyers in just a few days who bought our land uncontrollably. What confuses the current users is how we applied the price reduction for rebalancing in January, especially if you are new to vPark land and had no recollection of the event. Users who've been with us since January probably have a bit idea how we did it just by monitoring the price, but we will not disclose it to anyone. One thing I can disclose, the discount may run out in the next hours or days. Unlike metaverse games that encourage buyers to buy the pacific ocean, there is imminent scarcity of lands with economic value on vPark because economy is often created by real people in our earth's civilization. Please take time to read all our previous explanations on discord or ask from those who have understood it. The discord is for landowners to help one another.
After a long journey, we've received a greenlight from our payout partner, Tipalti. If you open their site, you can immediattely see the best companies they serve. Likewise, as a platform that serves international stakeholders, we are committed to give the best to our customers. Thank you for everyone's patience to make vPark safe.
As promised, nothing fancy, we just want things to get things rolling to serve some of our early content creators who have been wanting to submit videos! https://land.vpark.io/contents Show us all what you've got. And hopefully very soon we'll start seeing you and others using your own content across social media. We will list it on the site more clearly once we've got a few submissions.
Once you've updated your profile with a new username, others be able to see your properties in a dedicated page. We think this is important to enable collaboration between landowners around the same location.
",},
{title: 'What is the Settling/Aging Period?', shown:true, edited: new Date("February 13 2021"),
content: "
Some users are probably aware of the existence of this period. Just like Robinhood, Banks, and other Fintech, there is a period where our users have to a fund or a virtual property is settled. Currently, the two major settling periods are 7 days before you can resell your properties upon purchase and 7 days before any incomes, including your deposit, to be included in your withdrawable balance.
Hi everyone, in the preparation for our launch, we’ll share series of important updates to help all of you better transition. While vPark in itself doesn’t, vPark Land falls under a fintech. Because we are located in the California, one of the things we have to do as a US fintech company is to verify who our customers are. While we believe our early adopters are good people, we are very aware of fintech startups that did not do this and got into trouble, we simply do not want to take any chances with the trust you have given us. So, we have been partnering with Jumio, a reputable company in Palo Alto to be our verifier. You’ll be seeing a verification section under your account page, it is a very simple process.
We stated in the terms and conditions that you could not have multiple accounts. Not only is it unfair to other landowners, but our future identity verification might also reject your submission. At vPark, we take security and compliance very seriously because we believe it will protect all landowners in the long run as the US federal law is strict on money laundering & terrorist preventions. We will prohibit this from now on but will ignore all cases carried out before 12:00 AM PST, Feb 09, 2021. You can always buy your own properties from the other accounts while you still can in these next few days, but we also offer assistance to merge them as long as you used the same PayPal email address on both accounts. If you need this assistance, feel free to email us with the account IDs you have, the common paypal email, and the final ID you want. You cannot change your decision on which final account you prefer after you ask for this assistance. As of tomorrow, any properties purchased from an account you don't choose for the final account will not be transferred to you when we provide this assistance. A side note: We believe all our early adopters are great people, there is nothing to worry about.
",},
content: "
We stated in the terms and conditions that you could not have multiple accounts. Not only is it unfair to other landowners, but our future identity verification might also reject your submission. At vPark, we take security and compliance very seriously because we believe it will protect all landowners in the long run as the US federal law is strict on money laundering & terrorist preventions. We will prohibit this from now on but will ignore all cases carried out before 12:00 AM PST, Feb 09, 2021.
We have made a suggestion system to let a listed land's owner know a price you think is reasonable. This way, a seller can make an educated decision of pricing their land. You can see the # of suggestions you have on your land card. This suggestion system is not a bidding system.
You can do different things, based on what you want to do with your stamped land.
You can list it in a Virtual Property, our 360 rooms where you can earn 9% in royalties from transactions where you weren’t the buyer or seller. However, this is dependent on someone purchasing a Virtual Property.
You can just have it listed in the Marketplace, share the link with other people, and have them bid on your land. You will earn 5% in royalties, again, from transactions where you weren’t the buyer or seller.
Or, you could have just created this stamped land to display in your Virtual Property as a personal gallery.
How to Stamp Land
Ensure your vPark land and vPark.io accounts are connected
Go to the my properties tab Click “Stamp” on the property that you’d like to stamp.
Confirm that you understand the purchase details.
Congrats! You now own Stamped Land.
How to List Stamped Land in a 360 Room
Go to your vPark account page: vpark.io/account
Under Real Estate, go to Properties and create a new Virtual Property by selecting the background first.
Under assets, you can add your stamped lands along with other 3D assets like furniture.
Activate your property
Scroll up and click on the URL to check out your new Virtual Property.
Double click at the land frame and use WASD+Up+Down keys to move, rotate or scale the frame.
You will earn 9% in royalties whenever transactions occur where you weren’t the buyer or seller. vPark’s commission fee is 5% for this option.
How to List Stamped Land in the Market
You can list your stamped land the same way for unstamped land. You will earn 5% in royalties whenever transactions occur where you weren’t the buyer or seller. vPark’s commission fee is 10% for this option.
",},
{title: 'Making an account', shown:false,
content: "
Please read all our notes & terms very carefully, we don't want any users to get into future issues during payout. To make an account, simply fill out the email and password fields on top, and click register. You need to verify your email before logging in, so, open your mailbox.
We often receive a question about not being able to login after registering, please look for a confirmation email we send to activate your account. Junk mails, over quota, domain whitelisting, and typos are the usual culprits of not finding the email.
There are multiple ways to generate income from owning our vPark Land. Currently there are three main methods. Other incomes such as royalties from captured data and others will be announced when we move on to the next stage.
vPark Land Value
Just like a stock or other currencies, vPark Land has a limited supply, so you want to buy low and sell high. The earlier you buy, most probably the land squares you want are still unclaimed, making it purchasable at a very low market value. The longer you delay, the higher the land value will be as the supply decreases. Each land price in different countries is different, so you can focus on the fluctuation in each country or simply claim the unclaimed. You can sell your lands to others using our listing system.
5% Referral Bonus
This is the easiest way to make money, your account comes with a referral code that will give you 5% commission of any land bought by other users that use your code. So, share the code with your friends asap, before the referral code market become saturated. You cannot use your own referral code and the referral value diminishes over time when used by the same user.
Early Adopter Residual Income
For early buyers in each country, split into different classes, we give them a bit amount for new land purchases (unclaimed) in that country. Depending on the land class, you own, the amount distributed are different. We currently distribute the these residuals weekly. Only the last owner a week before the distribution will get the residuals of the following week. We only do this to thank early adopters, it is not related to our business in general.
Land Tax
This is the true value of vPark Land, to give commission to those who help build a market economy through our 360 rooms. Eligible transactions within 360 rooms that use a 360 video taken from your land will earn you landtax. Landtax is distributed weekly and only the last owner a week before the distribution will get the portion of the following week.
In the Land Tab, make sure you are in the navigate mode (one of the buttons below), search for the land you want to buy, zoom in until the bulls-eye target is gone. Alternatively, you can click the \"Zoom to Grid\" button and wait until the squares appear.
Make sure the \"Select\" button is selected, and simply choose the squares you want to purchase. You can hold your mouse click while selecting multiple squares. The deselect button is available to remove any squares
Once you are satisfied with the squares, click the \"Buy XXX Squares - $XXX\" button next to the select and deselect buttons.
",},
{title: 'Selling Land', shown:false,
content: "In the account's properties page, you can name each land square you own, and set a price for it. That's it. You'll get notified if someone buys your squares.",},
{title: 'Adding Cash', shown:false,
content: "To buy in a smaller amount, you need to use your balance. You can go to your account page -> finance -> deposit some fund.",},
{title: 'Withdrawing Cash', shown:false,
content: "Go to the Profile tab and follow the instruction to withdraw your money. Just like Robinhood, Banks, and other Fintech, there is a period where our users have to a fund or a virtual property is settled. Currently, the two major settling periods are 7 days before you can resell your properties upon purchase and 7 days before any incomes, including your deposit, to be included in your withdrawable balance. This is part of our Anti Money Laundering process.",},
// {title: 'Seeing Issues', shown:false,
// content: " Please email us your ID and shortly tell us what has happened. Please don't use PayPal refund as a mean of communication. We will refund your money if we see issues on our part.",},
],
"faqs": [
{title: 'Will non-Americans have to pay taxes in the US? I will not just randomly get a bill from the US Embassy?', shown:false, edited: (new Date() - new Date("March 25 2021")),
content: "We are required to withhold in some scenarios. As long as your W8 is not a fraud and you are not a US residence, probably not. But since we are neither US government nor tax advisor, please check with your local authority or experts.",},
{title: 'What cant I do without getting verified?', shown:false, edited: (new Date() - new Date("February 17 2021")),
content: "Buying from other users, withdrawal, and selling lands. You can still buy as many new lands as you want without getting verified.",},
{title: 'How does vPark Land benefit landowners in the real world and 360 content creators?',edited: (new Date() - new Date("February 7 2021")),
content: "Backed by the community to make this happen, when we provide captured data we do not own, the party who owns the data or the real owner in the physical world might also be given royalties. Helping experience-based businesses earn extra revenue by having a presence in the captured world is one of our main goals. The captured space can easily evolve into a counseling space, recreation, corporate event, education, and others just like how we use a physical real estate.",},
{title: 'How can I help?',edited: (new Date() - new Date("January 27 2021")),
content: "Spreading the word is main key element to help. We also encourage individual leaders to build their own communitiy. If you are a professional, you can drop your linkedin to our support email. Otherwise, if you have a crucial feedback, let us know.",},
{title: 'Will the building be removed on the map?',edited: (new Date() - new Date("January 27 2021")),
content: "We get this question a lot. It seems like users are equating our vision to that of earth2. We are not making a game in a virtual world. We are about capturing physical spaces and moving interactions and businesses into them. Please read our plan carefully.",},
{title: 'What can I do with the land?',
content: "Please read the 3 different stages very carefully. We are not making a game with lands you can build on top. This virtual land ownership simply determines who will get the credit, nothing more than that. In the next stages, some incomes from solutions or sales created through our 360 pano and future technology out of the stage 3 will be treated as a source of royalties. Some users are wondering how much the royalties will be, that's not how an economy works. We want to ensure the value of your land keeps increasing by stabilizing the growth. Different vendors or partners might price things differently.",},
{title: 'Zero residuals?', edited: (new Date() - new Date("January 30 2021")),
content: "In case you only own a few squares or not many people are buying in your country, your residual income may be very small. Still, we record it as a transaction.",},
{title: 'Why is my account balance reduced?',
content: "This is a rare case, but it could happen. Someone's probably used your coupon code, then, for a legitimate reason between us and that user we've decided to cancel the transaction, your referral income went away. It may go negative if you've used up your balance through withdrawal or spending the referral for other lands. We do not take away the land you've purchased for this reason though, so please remain calm. However, you need to add more balance to cover the remaining before buying new squares.",},
{title: 'Why did my valuation decrease?', edited: (new Date() - new Date("January 27 2021")),
content: "Our initial valuation method was a mix of commonly known 'comparable' & 'cost' approaches in the real estate industry. Just like in the real world, there will not be a 'right' valuation approach. It could be that users had paid a huge amount (spike) in the past days and has stabilized, like a candlestick in the stock market graph. It could also mean some users are selling low to gain immediate profits. Another factor of change is if no one sells anything for awhile. After getting feedbacks from users, we've decided to temporarily show the valuation in your account using the \"cost approach\" instead. The trading average in the My Square is still showing the approximate valuation by mixing the cost & comparable approaches. We did not deduct any amount from your account.",},
{title: 'Are we supposed to capture 360 videos?', edited: (new Date() - new Date("January 27 2021")),
content: "No. Unless you want to be a content creator, which we hope can help you generate extra income. Landowners are there to build the market, not to do 360 capture, but some landowners are already offering to do that.",},
{title: 'Market Hour',
content: "We are not planning to provide transactional activities during a weekend, this includes but not limited to adding balance and buying lands. At the moment, however, we are still letting the market run on Saturday until further notice. Our timezone is pacific standard time (PST).",},
{title: 'How much should I buy?',
content: "We cannot act as a financial advisor but one thing to remember: there is no economy in the island of one's owner. Likewise, in this new virtual lands, more owners is merrier, you will have co-owners to collaborate to get more income in the future. Buying an entire city might not be a good investment in the long run.",},
{title: 'Buying 100 Squares?',
content: "Although 100 squares are the limit to buy, we recommend buying in smaller chunks. We currently do not provide any tool to split your lands.",},
{title: 'How can I buy a property that is already owned?',
content: "You need to wait until the owner of the property list it on the market.",},
{title: 'Can I sell the land squares to vPark?',
content: "Not right now, but as Rowan Atkinson said, never say never. There could be a case that we want them back and give you a good deal.",},
{title: 'How much should I sell my property?',
content: "There are multiple reasons why you might want to sell them, depending on the reason pricing strategy should be different. Long-term investors might want to wait until the price has matured or just sleep on it while receiving future royalties if they have a good property that would be probably be used by the public. Otherwise, feel free to check the current market rate and how much others are selling.",},
{title: 'I purchased a land, now what?',
content: "Each stage will take time to mature. Once we get enough land-owners, we will launch producthunt-like forum for our users to participate in a new product ideation or request on how to use 360 video/image with monetary value. In the mean time, spreading the word is the most effective way to bring all landowners to potential profitability asap, since we own this economy together. ",},
{title: 'Is there any hidden fee anywhere?',
content: "Our goal is to help everyone, so we will always try to keep everyone up to date if we have a decision. So far all potential fees are notified as part of the experience or in the terms for tax-related stuff. We keep updating the additional T&C too if there is more users should know. But no, we want to be transparent here. We are doing this in a journey of making the world more equal, not to hurt anyone.",},
]};
$scope.state.helps = {
"General": [
{title: 'Making an account', shown:true,
content: "
Please read all our notes & terms very carefully, we don't want any users to get into future issues during payout. To make an account, simply fill out the email and password fields on top, and click register. You need to verify your email before logging in, so, open your mailbox.
We often receive a question about not being able to login after registering, please look for a confirmation email we send to activate your account. Junk mails, over quota, domain whitelisting, and typos are the usual culprits of not finding the email.
",},
{title: 'Adding Cash', shown:false,
content: "To buy in a smaller amount, you need to use your balance. You can go to your Profile Tab and to deposit some fund.",},
{title: 'Withdrawing Cash', shown:false,
content: "Go to the Profile tab and follow the instruction to withdraw your money. Just like Robinhood, Banks, and other Fintech, there is a period where our users have to a fund or a virtual property is settled. Currently, the two major settling periods are 7 days before you can resell your properties upon purchase and 7 days before any incomes, including your deposit, to be included in your withdrawable balance. This is part of our Anti Money Laundering process.",},
],
// "Token": [
// {title: `What is it?`, shown:true,
// content: "
The economy on vPark circles around DigiCards prepared for various businesses. At the same time, we are planning to create liquidity as digital currency against USD coins. DigiCard can only be purchased from another user using tokens. vPark tokens are currently on presale. It can also be earned from other users or completing certain tasks.
",},
// {title: `How will the token value increase?`, shown:true,
// content: "
We are allowing users to mine Tokens to gather 360 or captured media around the world, however, it is not like crypto mining you know. The new technology we create is similar to ethereum, but instead of digitizing a process, we are humanizing them. You can think of the lands as a node, the gas fees as payment to land owners for validating 360s in the neighborhood, 360 submissions as the mining process.
",},
// {title: `What can I use token for?`, shown:true,
// content: "
Currently, you can invest your tokens to other user's Land Token. The collected tokens in the Land Token can be used to purchase DigiCard captions, which is what can appear on your land. In the future, we plan to use the token to activate land and make the land a consensus participant.
",},
// {title: `Does it cost anything to open a Land Token?`, shown:true,
// content: "
No, you can look Land Token like an investment fund (Special purpose vehicle in real-life fundraising).
",},
// {title: `How you can earn more tokens?`, shown:true,
// content: "
By checking and completing what are specified under virtual missions.
",},
// ],
// "LandChat": [
// {title: `Why can't I join some groups?`, shown:true,
// content: "
Each group has its own requirement, for example, you must have enough lands for country-based groups.
",},
// {title: `Why do I need to be verified?`, shown:true,
// content: "
The chat is meant to help create sales for businesses, this is a platform for businesses, therefore verification is necessary to earn income and to prevent unwanted discussions that hurt our partners.
",},
// {title: `How many channels are there?`, shown:true,
// content: "
There is one channel per country worldwide accessible if you zoom in the map to a specific country while opening the LandChat. Each open LandToken has also a channel accessible from your LandToken section to collaborate with fellow investors to get the best captions. When you zoom out in the map there is a closed group called vInt accessible for those who joined the LandChat waiting list. On top of these, you may find more LandChat groups from the map news section.
",},
// {title: `How can I create a new group?`, shown:true,
// content: "
If you have 5 or more users wanting to open a public group, you can pick a location, a name for the group, and make a suggestion to us via email or HelpDesk group.
",},
// ],
// "DigiCard": [
// {title: `Why can different lands resell Artwork with different prices?`, shown:true,
// content: "
The DigiCard aimed for resell might not appear as often as the higher price and might be difficult to find, if you are a Merchant in Virtual Mission #1, you can tell people where they are at, the goal is to help resell Artwork with the highest price.
",},
// {title: `What's the fee?`, shown:true,
// content: "
DigiCard itself is free, but one that carries an Artwork as specified in Virtual Mission #1 has 5% transaction fee just like the Artwork pricing structure.
",},
// {title: 'How is DigiCard different from Artwork', shown:true,
// content: "
DigiCard is a collaboration or inquiry tool for landowners to earn income, the first DigiCard you see on vPARK is to resell an artwork as specified in Virtual Mission #1.
A DigiCard may or may not contain an Artwork. A DigiCard can be captioned and sold to earn diamonds. An Artwork on the map is actually a Digicard that contains an Artwork.
",},
// {title: 'Why are there Artwork duplicates for each DigiCard', shown:true,
// content: "
Each artwork requires proof of sales for it to be worth our landowner's efforts for resale, making an Artwork copy allows the creator to spread their own card alone before landowners gain massive profits.
Go to account/verification/ID and click start button
Wait until it is approved, could be a few mins or hours. You can read our mission page for more info in the meantime if interested
Go to account/Artwork and click create Artwork
Fill and upload the content, and click submit
Go back to account/Artwork again, and set the price for the Artwork and the minimum resale price for DigiCard there.
",},
// {title: `What's the fee?`, shown:true,
// content: "
5% per sale transaction.
",},
// {title: 'How can we submit a non-artwork contract?', shown:true,
// content: "
Please contact our support and we will assist you.
",},
// // {title: 'Is the Artwork on blockchain?', shown:true,
// // content: "
The Artwork and its DigiCard version were on Hyperledger blockchain, one that does not cause global warming. Due to reprioritization, we have shutdown the blockchain until further need.
",},
// {title: 'Can we pay with Ethereum?', shown:true,
// content: "
We only accept USD.
",},
// {title: 'Can we import other Artwork created on another site?', shown:true,
// content: "
We do not accommodate migration at the moment.
",},
// // {title: 'Can I detach an Artwork from a DigiCard that has one?', shown:true,
// // content: "
No, when such DigiCard was made, there is a new Artwork created that resides within the card.
",},
// // {title: 'Can I have a different contract?', shown:true,
// // content: "
Yes, please contact us right after creating the Artwork before you list it or its DigiCard twin.
",},
// {title: 'How can I upload different art sizes?', shown:true,
// content: "
We suggest you upload a zip of those files.
",},
// ],
"Land How To": [
// {title: 'Stamping your land', shown:true,
// content: "
Overview
You can do different things, based on what you want to do with your stamped land.
You can list it in a Virtual Property, our 360 rooms where you can earn 9% in royalties from transactions where you weren’t the buyer or seller. However, this is dependent on someone purchasing a Virtual Property.
You can just have it listed in the Marketplace, share the link with other people, and have them bid on your land. You will earn 5% in royalties, again, from transactions where you weren’t the buyer or seller.
Or, you could have just created this stamped land to display in your Virtual Property as a personal gallery.
How to Stamp Land
Ensure your vPark land and vPark.io accounts are connected
Go to the my properties tab Click “Stamp” on the property that you’d like to stamp.
Confirm that you understand the purchase details.
Congrats! You now own Stamped Land.
How to List Stamped Land in a 360 Room
Go to your vPark account page: vpark.io/account
Under Real Estate, go to Properties and create a new Virtual Property by selecting the background first.
Under assets, you can add your stamped lands along with other 3D assets like furniture.
Activate your property
Scroll up and click on the URL to check out your new Virtual Property.
Double click at the land frame and use WASD+Up+Down keys to move, rotate or scale the frame.
You will earn 9% in royalties whenever transactions occur where you weren’t the buyer or seller. vPark’s commission fee is 5% for this option.
How to List Stamped Land in the Market
You can list your stamped land the same way for unstamped land. You will earn 5% in royalties whenever transactions occur where you weren’t the buyer or seller. vPark’s commission fee is 10% for this option.
There are multiple ways to generate income from owning our vPark Land. Currently there are three main methods. Other incomes such as royalties from business activities.
vPark Land Value
vPark Land has a limited supply, so you want to buy low and sell high. The earlier you buy, most probably the land squares you want are still unclaimed, making it purchasable at a very low market value. The longer you delay, the higher the land value will be as the supply decreases. Each land price in different countries is different, so you can focus on the fluctuation in each country or simply claim the unclaimed. You can sell your lands to others using our listing system.
5% Referral Bonus
This is the easiest way to make money, your account comes with a referral code that will give you 5% commission of any land bought by other users that use your code. So, share the code with your friends asap, before the referral code market become saturated. You cannot use your own referral code and the referral value diminishes over time when used by the same user.
Land Tax
This is the true value of vPark Land, to give commission to those who help build a market economy through our various programs.
In the Map page, make sure you are in the navigate mode (one of the buttons below), search for the land you want to buy, zoom in you see the grid. Alternatively, you can click the \"Zoom to Grid\" button and wait until the squares appear.
Make sure the \"Select\" button is selected, and simply choose the squares you want to purchase. You can hold your mouse click while selecting multiple squares. The deselect button is available to remove any squares
Once you are satisfied with the squares, click the \"Buy XXX Squares - $XXX\" button next to the select and deselect buttons.
Buy the land.
",},
{title: 'Selling Land', shown:false,
content: "In your account -> my properties page, you can name each land square you own, and set a price for it. That's it. You'll get notified if someone buys your squares.",},
],
"Land FAQs": [
{title: 'Will non-Americans have to pay taxes in the US? I will not just randomly get a bill from the US Embassy?', shown:false, edited: (new Date() - new Date("March 25 2021")),
content: "We are required to withhold in some scenarios. As long as your W8 is not a fraud and you are not a US residence, probably not. But since we are neither US government nor tax advisor, please check with your local authority or experts.",},
{title: 'What cant I do without getting verified?', shown:false, edited: (new Date() - new Date("February 17 2021")),
content: "Buying from other users, withdrawal, and selling lands. You can still buy as many new lands as you want without getting verified.",},
{title: 'How can I help?',edited: (new Date() - new Date("January 27 2021")),
content: "Spreading the word is main key element to help. We also encourage individual leaders to build their own communitiy. If you are a professional, you can drop your linkedin to our support email. Otherwise, if you have a crucial feedback, let us know.",},
{title: 'What can I do with the land?',
content: "Please read the 3 different stages very carefully. We are not making a game with lands you can build on top. This virtual land ownership simply determines who will get the credit, nothing more than that. In the next stages, some incomes from solutions or sales created through our 360 pano and future technology out of the stage 3 will be treated as a source of royalties. Some users are wondering how much the royalties will be, that's not how an economy works. We want to ensure the value of your land keeps increasing by stabilizing the growth. Different vendors or partners might price things differently.",},
// {title: 'Zero residuals?', edited: (new Date() - new Date("January 30 2021")),
// content: "In case you only own a few squares or not many people are buying in your country, your residual income may be very small. Still, we record it as a transaction.",},
{title: 'Why is my account balance reduced?',
content: "This is a rare case, but it could happen. Someone's probably used your coupon code, then, for a legitimate reason between us and that user we've decided to cancel the transaction, your referral income went away. It may go negative if you've used up your balance through withdrawal or spending the referral for other lands. We do not take away the land you've purchased for this reason though, so please remain calm. However, you need to add more balance to cover the remaining before buying new squares.",},
{title: 'Market Hour',
content: "We are not planning to provide transactional activities during a weekend, this includes but not limited to adding balance and buying lands. At the moment, however, we are still letting the market run on Saturday until further notice. Our timezone is pacific standard time (PST).",},
{title: 'How much should I buy?',
content: "We cannot act as a financial advisor but one thing to remember: there is no economy in the island of one's owner. Likewise, in this new virtual lands, more owners is merrier, you will have co-owners to collaborate to get more income in the future. Buying an entire city might not be a good investment in the long run.",},
{title: 'Buying 100 Squares?',
content: "Although 100 squares are the limit to buy, we recommend buying in smaller chunks. We currently do not provide any tool to split your lands.",},
{title: 'How can I buy a property that is already owned?',
content: "You need to wait until the owner of the property list it on the market.",},
{title: 'Can I sell the land squares to vPark?',
content: "Not right now, but as Rowan Atkinson said, never say never. There could be a case that we want them back and give you a good deal.",},
{title: 'How much should I sell my property?',
content: "There are multiple reasons why you might want to sell them, depending on the reason pricing strategy should be different. Long-term investors might want to wait until the price has matured or just sleep on it while receiving future royalties if they have a good property that would be probably be used by the public. Otherwise, feel free to check the current market rate and how much others are selling.",},
{title: 'Is there any hidden fee anywhere?',
content: "Our goal is to help everyone, so we will always try to keep everyone up to date if we have a decision. So far all potential fees are notified as part of the experience or in the terms for tax-related stuff. We keep updating the additional T&C too if there is more users should know. But no, we want to be transparent here. We are doing this in a journey of making the world more equal, not to hurt anyone.",},
],
}
$scope.countryPriceIncreasePercentage = (c) => {
if (!$scope.state.countryStats) return 0;
let minprice = 0.1;
return (($scope.state.countryStats.find( e => e.country==c ).price-minprice)/minprice*100).toFixed(2);
}
var stripe = window.Stripe ? Stripe("pk_live_51IOms1IzTSMtOr1ynr0ewLhbeTXXWUYQu5bYVKEWq90eBEeuXX1JQWWeg8pgoVNhBzcLMri6AMHU4Qnl1LpClKuF00rGfYBjFi") : null;
$scope.stripePurchase = () => {
if ($scope.state.amountToDeposit < 3 || $scope.state.requestingDeposit) return;
if (isNaN($scope.state.amountToDeposit)) return;
if (!$scope.state.user.tax_verified) alert("This is a friendly reminder that your account isn't fully verified yet. Your account is limited, but you can still buy lands from vPark.");
$scope.state.requestingDeposit = true;
$scope.vService.ajax.call( "/api/land/order", "POST", true, {action: "deposit",total: $scope.state.amountToDeposit}, (data) => {
$scope.state.requestingDeposit = false;
$scope.state.showStripe = true;
$scope.$apply();
if (!$scope.stripeSetup) {
$scope.stripeSetup = true;
var elements = stripe.elements();
var style = {
base: {
color: "#32325d",
fontFamily: 'Arial, sans-serif',
fontSmoothing: "antialiased",
fontSize: "16px",
"::placeholder": {
color: "#32325d"
}
},
invalid: {
fontFamily: 'Arial, sans-serif',
color: "#fa755a",
iconColor: "#fa755a"
}
};
var card = elements.create("card", { style: style });
// Stripe injects an iframe into the DOM
// $("#card-element").empty();
card.mount("#card-element");
card.on("change", function (event) {
// Disable the Pay button if there are no card details in the Element
$scope.state.stripeButtonEnabled = !event.empty;
document.querySelector("#card-error").textContent = event.error ? event.error.message : "";
$scope.$apply();
});
}
var form = document.getElementById("payment-form");
if (!$scope.stripeSubmit)
$scope.stripeSubmit = function(event) {
event.preventDefault();
// Complete payment when the submit button is clicked
$scope.payWithCard(stripe, card, data.clientSecret);
};
else
form.removeEventListener("submit", $scope.stripeSubmit);
form.addEventListener("submit", $scope.stripeSubmit);
},(data) => {
$scope.state.requestingDeposit = false;
$scope.$apply();
alert(data.responseJSON.message);
});
}
// Calls stripe.confirmCardPayment
// If the card requires authentication Stripe shows a pop-up modal to
// prompt the user to enter authentication details without leaving your page.
$scope.payWithCard = function(stripe, card, clientSecret) {
$scope.loading(true);
stripe
.confirmCardPayment(clientSecret, {
payment_method: {
card: card
}
})
.then(function(result) {
if (result.error) {
// Show error to your customer
$scope.showError(result.error.message);
} else {
// The payment succeeded!
$scope.orderComplete(result.paymentIntent.id);
}
});
};
/* ------- UI helpers ------- */
// Shows a success message when the payment is complete
$scope.orderComplete = function(paymentIntentId) {
$scope.loading(false);
document.querySelector(".result-message").classList.remove("hidden");
$scope.state.stripeButtonEnabled = false;
$scope.state.showStripe = false;
$scope.$apply();
location.reload();
};
// Show the customer the error from Stripe if their card fails to charge
$scope.showError = function(errorMsgText) {
$scope.loading(false);
var errorMsg = document.querySelector("#card-error");
errorMsg.textContent = errorMsgText;
setTimeout(function() {
errorMsg.textContent = "";
$scope.state.showStripe = false;
$scope.$apply();
}, 4000);
};
// Show a spinner on payment submission
$scope.loading = function(isLoading) {
};
$scope.state.setupStripe = (idx = "", purpose = "deposit") => {
$scope.state.stripePurpose = purpose;
$scope.registerElements=(elements, exampleName) => {
var formClass = '#' + exampleName;
var example = document.querySelector(formClass);
var form = example.querySelector('form');
var resetButton = example.querySelector('a.reset');
var error = form.querySelector('.error');
var errorMessage = error.querySelector('.message');
function enableInputs() {
Array.prototype.forEach.call(
form.querySelectorAll(
"input[type='text'], input[type='email'], input[type='tel']"
),
function(input) {
input.removeAttribute('disabled');
}
);
}
function disableInputs() {
Array.prototype.forEach.call(
form.querySelectorAll(
"input[type='text'], input[type='email'], input[type='tel']"
),
function(input) {
input.setAttribute('disabled', 'true');
}
);
}
function triggerBrowserValidation(form) {
// The only way to trigger HTML5 form validation UI is to fake a user submit
// event.
var submit = document.createElement('input');
submit.type = 'submit';
submit.style.display = 'none';
form.appendChild(submit);
submit.click();
submit.remove();
}
// Listen for errors from each Element, and show error messages in the UI.
var savedErrors = {};
elements.forEach(function(element, idx) {
element.on('change', function(event) {
if (event.error) {
error.classList.add('visible');
savedErrors[idx] = event.error.message;
errorMessage.innerText = event.error.message;
} else {
savedErrors[idx] = null;
// Loop over the saved errors and find the first one, if any.
var nextError = Object.keys(savedErrors)
.sort()
.reduce(function(maybeFoundError, key) {
return maybeFoundError || savedErrors[key];
}, null);
if (nextError) {
// Now that they've fixed the current error, show another one.
errorMessage.innerText = nextError;
} else {
// The user fixed the last error; no more errors.
error.classList.remove('visible');
}
}
});
});
// Listen on the form's 'submit' handler...
form.addEventListener('submit', function(e) {
e.preventDefault();
// Trigger HTML5 validation UI on the form if any of the inputs fail
// validation.
var plainInputsValid = true;
Array.prototype.forEach.call(form.querySelectorAll('input'), function(
input
) {
if (input.checkValidity && !input.checkValidity()) {
plainInputsValid = false;
return;
}
});
if (!plainInputsValid) {
triggerBrowserValidation(form);
return;
}
// if ($scope.state.amountToDeposit < 3 || $scope.state.requestingDeposit) return;
if (isNaN($scope.state.amountToDeposit)) return;
if ($scope.state.loggedIn && !$scope.state.user.tax_verified) alert("This is a friendly reminder that your account isn't fully verified yet. Your account is limited, but you can still buy lands from vPark.");
$scope.state.requestingDeposit = true;
$scope.vService.ajax.call("/api/land/order","POST", $scope.state.loggedIn, {action: $scope.state.stripePurpose,total: $scope.state.amountToDeposit}, (data) => {
$scope.state.requestingDeposit = false;
$scope.$apply();
// Show a loading screen...
example.classList.add('submitting');
// Disable all inputs.
disableInputs();
// Gather additional customer data we may have collected in our form.
var name = form.querySelector('#' + exampleName + '-name');
// var address1 = form.querySelector('#' + exampleName + '-address');
// var city = form.querySelector('#' + exampleName + '-city');
// var state = form.querySelector('#' + exampleName + '-state');
var zip = form.querySelector('#' + exampleName + '-zip');
var additionalData = {
name: name ? name.value : undefined,
// address_line1: address1 ? address1.value : undefined,
// address_city: city ? city.value : undefined,
// address_state: state ? state.value : undefined,
address_zip: zip ? zip.value : undefined,
};
stripe.confirmCardPayment(data.clientSecret, {
payment_method: {
card: elements[0],
billing_details: {
name: additionalData.name,
address: {postal_code: additionalData.address_zip}
},
}
})
.then(function(result) {
example.classList.remove('submitting');
if (result.error) {
// Show error to your customer
enableInputs();
} else {
// The payment succeeded!
example.classList.add('submitted');
if ($scope.state.loggedIn)
$scope.vService.ajax.call("/api/user", "GET", true, null, (data) => {
$scope.state.user = data;
$scope.$apply();
});
}
});
});
});
resetButton.addEventListener('click', function(e) {
e.preventDefault();
// Resetting the form (instead of setting the value to `''` for each input)
// helps us clear webkit autofill styles.
form.reset();
// Clear each Element.
elements.forEach(function(element) {
element.clear();
});
// Reset error state as well.
error.classList.remove('visible');
// Resetting the form does not un-disable inputs, so we need to do it separately:
enableInputs();
example.classList.remove('submitted');
});
}
var elements = stripe.elements({
// Stripe's examples are localized to specific languages, but if
// you wish to have Elements automatically detect your user's locale,
// use `locale: 'auto'` instead.
locale: window.__exampleLocale
});
var elementStyles = {
base: {
color: '#fff',
fontWeight: 500,
// fontFamily: 'Quicksand, Open Sans, Segoe UI, sans-serif',
fontSize: '14px',
fontSmoothing: 'antialiased',
':focus': {
color: '#444',
},
'::placeholder': {
color: '#aaa',
},
':focus::placeholder': {
color: '#ccc',
},
},
invalid: {
color: '#fff',
':focus': {
color: '#FA755A',
},
'::placeholder': {
color: '#FFCCA5',
},
},
};
var elementClasses = {focus: 'focus',empty: 'empty',invalid: 'invalid',};
var cardNumber = elements.create('cardNumber', {style: elementStyles,classes: elementClasses,});
cardNumber.mount('#deposit-stripe-card-number'+idx);
var cardExpiry = elements.create('cardExpiry', {style: elementStyles,classes: elementClasses,});
cardExpiry.mount('#deposit-stripe-card-expiry'+idx);
var cardCvc = elements.create('cardCvc', {style: elementStyles,classes: elementClasses,});
cardCvc.mount('#deposit-stripe-card-cvc'+idx);
$scope.registerElements([cardNumber, cardExpiry, cardCvc], "deposit-stripe"+idx);
};
$scope.presentPDF = (property) => {
selectLocalPDF( () => {
setTimeout( () => {
$scope.updatePresentedPDFPage(property);
$scope.state.presentingPDF = true;
}, 1000);
});
}
$scope.updatePresentedPDFPage = (property) => {
$.ajax({
type: "POST",
url: "/api/world/properties/"+property+"/session/present/pdf",
headers: {
Authorization: $scope.vService.bearer,
},
data: {
image: $("#pdf-canvas").get()[0].toDataURL(),
}
}).done(function(o) {
console.log("Presentation updated");
});
}
$scope.getPresentedPDFPage = (property) => {
$scope._getPresentedPDFPage(property);
var a = setInterval( () => {
$scope._getPresentedPDFPage(property);
}, 5000);
}
$scope._getPresentedPDFPage = (property) => {
if (!$scope.state.presentingPDF)
$.ajax({
type: "GET",
// url: "/api/rooms/"+$scope.property.hash+"/present/pdf?timestamp="+$scope.liveplayer.getVideoTimestamp()+"&uuid="+$scope.uuid+"&key="+$scope.state.attendeeKey,
url: "/api/world/properties/"+property+"/session/present/pdf",
headers: {
Authorization: $scope.vService.bearer,
},
}).done(function(data) {
var img = new Image();
img.onload = function() {
let canvas = $("#pdf-canvas").get()[0];
var context = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0);
}
img.crossOrigin='anonymous';
$scope.state.presentingPDFUrl = data.presentation=='' ? 'https://images.unsplash.com/photo-1528731708534-816fe59f90cb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8d2hpdGUlMjBjb2xvcnxlbnwwfHwwfHw%3D&auto=format&fit=crop&w=800&q=60' : data.presentation;
img.src = data.presentation=='' ? 'https://images.unsplash.com/photo-1528731708534-816fe59f90cb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8d2hpdGUlMjBjb2xvcnxlbnwwfHwwfHw%3D&auto=format&fit=crop&w=800&q=60' : data.presentation;
// if (data.presentation=='') clearInterval(a);
});
}
$scope.stopPresentingPDF = (property) => {
$.ajax({
type: "DELETE",
url: "/api/world/properties/"+property+"/session/present/pdf",
headers: {
Authorization: $scope.vService.bearer,
},
data: {
}
}).done(function(o) {
$scope.state.presentingPDF = false;
console.log("Presentation updated");
});
}
$scope.prevPDFPage = async (property) => {
// if ($scope.state.presentingPDF)
if(_CURRENT_PAGE != 1) {
await showPage(--_CURRENT_PAGE);
$scope.updatePresentedPDFPage(property);
}
}
$scope.nextPDFPage = async (property) => {
// if ($scope.state.presentingPDF)
if(_CURRENT_PAGE != _TOTAL_PAGES) {
await showPage(++_CURRENT_PAGE);
$scope.updatePresentedPDFPage(property);
}
}
$scope.startRecording = () => {
if (!$scope.state.social.new) $scope.state.social.new = {};
$scope.state.social.new.active=false;
$scope.state.social.new.encodeAfterRecord = true; // when to encode
var constraints = { audio: true, video:false }
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
$scope.state.social.new.audioContext = new AudioContext();
//assign to gumStream for later use
$scope.state.social.new.gumStream = stream;
/* use the stream */
$scope.state.social.new.input = $scope.state.social.new.audioContext.createMediaStreamSource(stream);
//stop the input from playing back through the speakers
//input.connect(audioContext.destination)
//get the encoding
$scope.state.social.new.encodingType = "mp3";
//disable the encoding selector
// $scope.state.social.encodingTypeSelect.disabled = true;
$scope.state.social.new.recorder = new WebAudioRecorder($scope.state.social.new.input, {
workerDir: "js/libs/", // must end with slash
encoding: $scope.state.social.new.encodingType,
numChannels:2, //2 is the default, mp3 encoding supports only 2
onEncoderLoading: function(recorder, encoding) {
console.log("loading encoder");
},
onEncoderLoaded: function(recorder, encoding) {
console.log("enconder loaded");
}
});
$scope.state.social.new.recorder.onComplete = function(recorder, blob) {
console.log("complete");
$scope.createDownloadLink(blob,recorder.encoding);
$scope.state.social.new.active = false;
}
$scope.state.social.new.recorder.onTimeout = function(recorder) {
console.log("timeout");
}
$scope.state.social.new.recorder.onEncodingProgress = function(recorder, progress) {
console.log("progressing "+progress);
}
$scope.state.social.new.recorder.onEncodingCanceled = function(recorder) {
console.log("something is wrong");
}
$scope.state.social.new.recorder.onError = function(recorder, message) {
console.log(message);
}
$scope.state.social.new.recorder.setOptions({
timeLimit:300,
encodeAfterRecord:$scope.state.social.new.encodeAfterRecord,
ogg: {quality: 1},
mp3: {bitRate: 320}
});
//start the recording process
$scope.state.social.new.recorder.startRecording();
// __log("Recording started");
}).catch(function(err) {
//enable the record button if getUSerMedia() fails
$scope.state.social.new.active = false;
console.log(err);
});
$scope.state.social.new.active = true;
}
$scope.stopRecording = () => {
//stop microphone access
$scope.state.social.new.gumStream.getAudioTracks()[0].stop();
$scope.state.social.new.active = false;
//tell the recorder to finish the recording (stop recording + encode the recorded audio)
$scope.state.social.new.recorder.finishRecording();
console.log("stopping");
}
$scope.recordingIsValid = () => {
return (!$scope.state.social.new.type && !$scope.state.social.new.active && $scope.state.social.new.data && $scope.state.social.new.player && Math.min(Math.max($scope.state.social.new.player.duration,5),300) == $scope.state.social.new.player.duration)
|| ($scope.state.social.new.type==3 && $scope.state.social.new.data);
}
$scope.createDownloadLink = (blob,encoding) => {
var url = URL.createObjectURL(blob);
var au = $("#recorder-player").get()[0];
//add controls to the