379 lines
15 KiB
JavaScript
379 lines
15 KiB
JavaScript
const gen_id = (length)=>{
|
|
var result = '';
|
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
const len = characters.length;
|
|
for (let i = 0; i < length; i++ ) {
|
|
result += characters.charAt(Math.floor(Math.random() * len));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Date.prototype.toJSONLocal = (function() {
|
|
function addZ(n) {
|
|
return (n<10? '0' : '') + n;
|
|
}
|
|
return function() {
|
|
return this.getFullYear() + '-' +
|
|
addZ(this.getMonth() + 1) + '-' +
|
|
addZ(this.getDate());
|
|
};
|
|
}())
|
|
|
|
function toEncoded(string) {
|
|
const codeUnits = Uint16Array.from(
|
|
{ length: string.length },
|
|
(element, index) => string.charCodeAt(index)
|
|
);
|
|
const charCodes = new Uint8Array(codeUnits.buffer);
|
|
|
|
let result = "";
|
|
charCodes.forEach((char) => {
|
|
result += String.fromCharCode(char);
|
|
});
|
|
return window.btoa(result);
|
|
}
|
|
|
|
function toDecoded(string) {
|
|
let binary = window.atob(string)
|
|
const bytes = Uint8Array.from({ length: binary.length }, (element, index) =>
|
|
binary.charCodeAt(index)
|
|
);
|
|
const charCodes = new Uint16Array(bytes.buffer);
|
|
|
|
let result = "";
|
|
charCodes.forEach((char) => {
|
|
result += String.fromCharCode(char);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
|
|
const query_nominatim = (q,f) => axios.get('/api/place/'+q).then(res=>res.data).then(res=>res.filter(f))
|
|
const query_flight = (q) => axios.get('/api/flight/'+q).then(res=>res.data)
|
|
|
|
const is_restauration_type = e => ["restaurant", "cafe", "pub", "bar", "fast_food", "food_court"].indexOf(e.type)!=-1;
|
|
|
|
const is_attraction_type = e => (["tourism", "leisure", "place", "amenity", "highway", "historic", "natural", "waterway"].indexOf(e.category)!=-1 ||
|
|
["place_of_worship", "national_park", "nature_reserve", "protected_area"].indexOf(e.type!=-1));
|
|
|
|
const icon_type = (item)=>{
|
|
let t = item.type
|
|
let c = item.category
|
|
const arr = ["restaurant", "cafe", "pub", "bar", "fast_food", "food_court"];
|
|
if(arr.indexOf(t)!=-1){
|
|
return 'utensils';
|
|
}else if(t=='hotel' || t=='hostel'){
|
|
return 'bed';
|
|
}else if(t=='museum' || c=='historic' || t=='place_of_worship'){
|
|
return 'landmark';
|
|
}else if(t=='peak' || t=='viewpoint'){
|
|
return 'mountain';
|
|
}else if(t=='parking'){
|
|
return 'parking';
|
|
}else if(t=='water' || t=='river' || t=='lake' || t=='torrent' || t=='aquarium'){
|
|
return 'water';
|
|
}else if(t=='community_centre' || t=='locality'){
|
|
return 'building';
|
|
}else if(t=='attraction'){
|
|
return 'landmark';
|
|
}else if(t=='information' || t=='university'){
|
|
return 'landmark';
|
|
}else if(t=='bridge'){
|
|
return 'archway';
|
|
}else if(t=='woodland'|| t=='shieling' || t=='national_park' || t=='zoo' || t=='park' || t=='garden' || 0){
|
|
return 'tree';
|
|
}else if(t=='water_park', t=='theme_park'){
|
|
return 'dice-five';
|
|
}else if(t=='?' || t=='neighbourhood' || t=='quarter' || c=='highway'){
|
|
return '';
|
|
}else{
|
|
console.log(item.display_name, item.category, item.type);
|
|
return 'question';
|
|
}
|
|
}
|
|
|
|
Vue.component('l-map', window.Vue2Leaflet.LMap)
|
|
Vue.component('l-tile-layer', window.Vue2Leaflet.LTileLayer)
|
|
Vue.component('l-marker', window.Vue2Leaflet.LMarker)
|
|
Vue.component('l-icon', window.Vue2Leaflet.LIcon)
|
|
Vue.component('l-popup', window.Vue2Leaflet.LPopup)
|
|
Vue.component('l-tooltip', window.Vue2Leaflet.LTooltip)
|
|
Vue.component('l-control-scale', window.Vue2Leaflet.LControlScale)
|
|
Vue.component('multiselect', window.VueMultiselect.default)
|
|
Vue.use(window.VueTextareaAutosize);
|
|
|
|
const app = new Vue({
|
|
el: '#app',
|
|
data: {
|
|
journey_edit: (['view','short'].indexOf(window.location.pathname.split('/')[1])==-1),
|
|
journey_id : window.location.pathname.split('/').pop() || gen_id(16),
|
|
|
|
journey_step_data: {day:1, section:0},
|
|
journey_data : {
|
|
name: "New Journey",
|
|
main:[],
|
|
},
|
|
|
|
query:{hotel:[],flight:[],nominatim:[]},
|
|
querying:{hotel:false,flight:false,place:false,food:false},
|
|
impexp:"",
|
|
lang: {
|
|
format: 'ddd D MMM',
|
|
formatLocale: {
|
|
firstDayOfWeek: 1,
|
|
},
|
|
monthBeforeYear: true,
|
|
},
|
|
},
|
|
methods: {
|
|
start_journey: function(event){
|
|
window.location.href = '/'+this.journey_id;
|
|
},
|
|
add_section: function(event){
|
|
if(this.journey_data.main==undefined) this.journey_data.main=[];
|
|
this.journey_data.main.push({title:"?",step_title:[],map:{zoom:2}, hotel:{latlon:[0,0]},places:{restaurants:[],places:[]}});
|
|
},
|
|
step_len: function(idx){
|
|
return this.journey_data.main[idx].dateRange?
|
|
((this.journey_data.main[idx].dateRange[1]-this.journey_data.main[idx].dateRange[0])/(1000*60*60*24))+1
|
|
: 1;
|
|
},
|
|
next_step: function(){
|
|
this.journey_step_data.day += 1;
|
|
let s = this.journey_step_data.section;
|
|
let cd = this.step_len(s);
|
|
|
|
if(this.journey_step_data.day>cd){
|
|
this.journey_step_data.section +=1;
|
|
if(this.journey_step_data.section>=this.journey_data.main.length){
|
|
this.journey_step_data.section = this.journey_data.main.length-1;
|
|
this.journey_step_data.day = cd
|
|
}else{
|
|
this.journey_step_data.day = 1;
|
|
}
|
|
}
|
|
|
|
},
|
|
prev_step: function(){
|
|
this.journey_step_data.day -=1;
|
|
if(this.journey_step_data.day<=0){
|
|
this.journey_step_data.section -=1;
|
|
if(this.journey_step_data.section <0){
|
|
this.first_step();
|
|
}else{
|
|
let s = this.journey_step_data.section;
|
|
|
|
let cd = this.step_len(s);
|
|
this.journey_step_data.day = cd ;
|
|
}
|
|
}
|
|
},
|
|
nextnext_step: function(){
|
|
this.journey_step_data.section += 1;
|
|
this.journey_step_data.day = 1;
|
|
if(this.journey_step_data.section>=this.journey_data.main.length)
|
|
this.first_step();
|
|
},
|
|
prevprev_step: function(){
|
|
this.journey_step_data.section -= 1;
|
|
this.journey_step_data.day = 1;
|
|
if(this.journey_step_data.section<0)
|
|
this.first_step();
|
|
},
|
|
first_step: function(){
|
|
this.journey_step_data.section =0;
|
|
this.journey_step_data.day = 1;
|
|
},
|
|
|
|
active_date: function(){
|
|
if(this.journey_step_data.day < 0) return "?";
|
|
if(!this.journey_data.main[this.journey_step_data.section].dateRange) return "?";
|
|
var date = new Date(this.journey_data.main[this.journey_step_data.section].dateRange[0]);
|
|
date.setDate(date.getDate() + this.journey_step_data.day-1);
|
|
return this.format_date(date)
|
|
},
|
|
format_date: function(d){
|
|
return ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][d.getDay()] + ' '
|
|
+ d.getDate() + ' '
|
|
+ ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][d.getMonth()];
|
|
},
|
|
|
|
total_days: function(){
|
|
if(this.journey_data.main.length==0) return 0;
|
|
try{
|
|
return (this.journey_data.main[this.journey_data.main.length-1].dateRange[1]-this.journey_data.main[0].dateRange[0])/(1000*60*60*24)
|
|
}catch{
|
|
return '?'
|
|
}
|
|
},
|
|
total_date: function(){
|
|
if(this.journey_data.main.length==0) return '';
|
|
try{
|
|
return `${this.format_date(this.journey_data.main[0].dateRange[0])} - ${this.format_date(this.journey_data.main[this.journey_data.main.length-1].dateRange[1])}`;
|
|
}catch{
|
|
return '?';
|
|
}
|
|
},
|
|
update_date: function(idx){
|
|
let dateRange = this.journey_data.main[idx].dateRange;
|
|
let start_end = [0,0];
|
|
let step_len = 0;
|
|
|
|
let last_start = dateRange[0];
|
|
for(let i = idx-1; i >=0; --i){
|
|
step_len = this.step_len(i)-1;
|
|
if (this.journey_data.main[i].dateRange){
|
|
start_end = [last_start.getDate() - step_len, last_start.getDate()];
|
|
}else{
|
|
this.journey_data.main[i].dateRange= [new Date(), new Date()]
|
|
start_end = [last_start.getDate() - step_len, last_start.getDate()];
|
|
}
|
|
this.journey_data.main[i].dateRange[0].setTime(last_start.getTime())
|
|
this.journey_data.main[i].dateRange[0].setDate(start_end[0]);
|
|
this.journey_data.main[i].dateRange[1].setTime(last_start.getTime())
|
|
this.journey_data.main[i].dateRange[1].setDate(start_end[1]);
|
|
last_start = this.journey_data.main[i].dateRange[0];
|
|
}
|
|
|
|
let last_end = dateRange[1];
|
|
for(let i = idx+1; i<this.journey_data.main.length; ++i){
|
|
step_len = this.step_len(i)-1;
|
|
if (this.journey_data.main[i].dateRange){
|
|
start_end = [last_end.getDate(), last_end.getDate() + step_len];
|
|
}else{
|
|
this.journey_data.main[i].dateRange= [new Date(), new Date()]
|
|
start_end = [last_end.getDate(), last_end.getDate() + step_len];
|
|
}
|
|
this.journey_data.main[i].dateRange[0].setTime(last_end.getTime())
|
|
this.journey_data.main[i].dateRange[0].setDate(start_end[0]);
|
|
this.journey_data.main[i].dateRange[1].setTime(last_end.getTime())
|
|
this.journey_data.main[i].dateRange[1].setDate(start_end[1]);
|
|
last_end = this.journey_data.main[i].dateRange[1];
|
|
}
|
|
},
|
|
|
|
rm_section: function(idx){
|
|
this.journey_data.main.splice(idx,1);
|
|
if(this.journey_step_data.section==idx){
|
|
this.prevprev_step();
|
|
}
|
|
},
|
|
sel_section: function(idx){
|
|
this.journey_step_data.section = idx;
|
|
this.journey_step_data.day = 1;
|
|
},
|
|
search_nominatim: function(txt,f){
|
|
if(txt==""){
|
|
this.query.nominatim=[];
|
|
return Promise.resolve([]);
|
|
}
|
|
return query_nominatim(txt,f)
|
|
.then((results) =>{
|
|
results.forEach(r=>{
|
|
r.latlon=[parseFloat(r.lat),parseFloat(r.lon)];
|
|
r.sname=r.display_name.split(',')[0];
|
|
})
|
|
this.query.nominatim=results;
|
|
});
|
|
},
|
|
search_flight: function(txt){
|
|
if(txt=="") return;
|
|
this.querying.flight=true;
|
|
query_flight(txt.replace(" ", ""))
|
|
.then((results) =>{
|
|
if(results.results==""){
|
|
this.query.flight=[];this.querying.flight=false;return;
|
|
}
|
|
this.query.flight=results.results;
|
|
this.querying.flight=false;
|
|
});
|
|
|
|
},
|
|
generate_icon: function(item,fcolor){
|
|
return L.AwesomeMarkers.icon({
|
|
icon: icon_type(item) || 'star', prefix: 'fa',
|
|
markerColor: fcolor || item.color || 'blue'}
|
|
).createIcon().outerHTML
|
|
},
|
|
|
|
save_data: function(){
|
|
this.impexp = toEncoded(JSON.stringify(this.journey_data));
|
|
axios.post('/api/'+this.journey_id, this.journey_data).then(response => {
|
|
console.log("Saved...")
|
|
}).catch(error => {
|
|
console.warn('Error! Could not reach the API.')
|
|
})
|
|
},
|
|
import_data:function(){
|
|
this.journey_data = Object.assign({}, JSON.parse(toDecoded(this.impexp)));
|
|
this.journey_data.main.forEach(e=>{
|
|
if(e.dateRange){
|
|
e.dateRange[0] = new Date(e.dateRange[0]);
|
|
e.dateRange[1] = new Date(e.dateRange[1]);
|
|
}
|
|
})
|
|
},
|
|
export_data:function(){
|
|
this.impexp = toEncoded(JSON.stringify(this.journey_data));
|
|
},
|
|
filter_selected:function(list,step){
|
|
return list.filter(e=>(step?(e.step==this.journey_step_data.day):(e.step>=0)))
|
|
},
|
|
filter_unselected:function(list){
|
|
return list.filter(e=>(e.step==undefined || e.step<0))
|
|
},
|
|
remove_item:function(list,idx){
|
|
list[idx].step=-1;
|
|
list.splice(idx, 1);
|
|
},
|
|
log:function(e){console.log(e)},
|
|
|
|
keyboardEvent (e) {
|
|
if (e.which === 13) {
|
|
}
|
|
},
|
|
},
|
|
created: function () {
|
|
|
|
window.addEventListener('keydown', (e) => {
|
|
switch(e.key){
|
|
case 'ArrowLeft': this.prev_step(); break;
|
|
case 'ArrowRight': this.next_step(); break;
|
|
default: console.log(e.key);
|
|
}
|
|
});
|
|
|
|
axios.get('/api/'+this.journey_id).then(response =>{
|
|
if(response.data=='') throw "Invalid Journey Data Received";
|
|
app.journey_data = response.data;
|
|
|
|
for(let e of app.journey_data.main){
|
|
if(e.dateRange){
|
|
e.dateRange[0]= new Date(e.dateRange[0]);
|
|
e.dateRange[1]= new Date(e.dateRange[1]);
|
|
}
|
|
e.step_title = e.step_title || [];
|
|
}
|
|
});
|
|
|
|
|
|
this.debounceSave = _.debounce(this.save_data, 500)
|
|
this.debounceSearch = {"hotel":_.debounce((q)=>{this.querying.hotel=true;this.search_nominatim(q,(r)=>(r.type=="hotel" || r.type=="hostel")).then((r)=>{this.querying.hotel=false});}, 500),
|
|
"restaurants":_.debounce((q)=>{this.querying.food=true;this.search_nominatim(q,(r)=>(is_restauration_type(r))).then((r)=>{this.querying.food=false});}, 500),
|
|
"places":_.debounce((q)=>{this.querying.place=true;this.search_nominatim(q,(r)=>(is_attraction_type(r))).then((r)=>{this.querying.place=false});}, 500),
|
|
"other":_.debounce((q)=>{this.querying.any=true;this.search_nominatim(q,(r)=>(true)).then((r)=>{this.querying.any=false});}, 500),
|
|
"flight":_.debounce((q)=>this.search_flight(q), 500)
|
|
}
|
|
},
|
|
watch: {
|
|
journey_data: {
|
|
handler:function (ndata, odata){
|
|
this.debounceSave();
|
|
},
|
|
deep: true,
|
|
},
|
|
},
|
|
})
|
|
|
|
|