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 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'){ 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'){ return 'tree'; }else if(t=='water_park', t=='theme_park'){ return 'dice-five'; }else if(t=='?'){ 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_id : window.location.pathname.split('/').pop() || gen_id(16), journey_step: window.location.hash.slice(1)==""?((['view','short'].indexOf(window.location.pathname.split('/')[1])!=-1)?0:-1):parseInt(window.location.hash.slice(1)), journey_step_data: {day:-1, section:-1}, journey_data : { name: "New Journey", main:[], step_title:[], }, vtp: ['view','short'].indexOf(window.location.pathname.split('/')[1]), query:{hotel:[],flight:[],nominatim:[]}, querying:{hotel:false,flight:false,place:false,food:false}, impexp:"", lang: { 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({step_title:"?",map:{zoom:2}, hotel:{latlon:[0,0]},places:{restaurants:[],places:[]}}); }, next_step: function(){ this.journey_step+=1; this.journey_step_data = this.step_convert(this.journey_step); if(this.journey_step_data.section==-1) this.prev_step(); window.location.hash = '#' + this.journey_step; }, prev_step: function(){ this.journey_step-=1; if(this.journey_step<-1) this.journey_step=-1; if(this.journey_step==-1 && this.view) this.journey_step=0; this.journey_step_data = this.step_convert(this.journey_step); window.location.hash = '#' + this.journey_step; }, nextnext_step: function(){ let nd = this.journey_step_data; let ns = this.journey_step; while((nd.section==this.journey_step_data.section || nd.day>1) && ns>=0){ ns+=1; nd = this.step_convert(ns) if(nd.section==-1){ ns-=1; nd = this.step_convert(ns) break; } } this.journey_step = ns; this.journey_step_data = nd; window.location.hash = '#' + this.journey_step; }, prevprev_step: function(){ let nd = this.journey_step_data; let ns = this.journey_step; while((nd.section==this.journey_step_data.section || nd.day>1) && ns>=0){ ns-=1; nd = this.step_convert(ns) if(ns==-1){ ns+=1; nd = this.step_convert(ns) break; } } this.journey_step = ns; this.journey_step_data = nd; window.location.hash = '#' + this.journey_step; }, first_step: function(){ this.journey_step=-1; this.journey_step_data = {day:-1, section:-1}; window.location.hash = ''; }, step_convert: function(step){ let steps_left = step+1; for(let e in this.journey_data.main){ if(this.journey_data.main[e].dateRange && (this.journey_data.main[e].dateRange[0]-(new Date(0))) != 0){ let cd = ((this.journey_data.main[e].dateRange[1]-this.journey_data.main[e].dateRange[0])/(1000*60*60*24))+1; if(cd>=steps_left){ return {day: steps_left, section:e,start: (steps_left==1), end: (steps_left==cd)}; }else{ steps_left -= cd; } }else{ steps_left -= 1; } if(steps_left==0){ return {day:0,section:e, start:true,end:true}; } } return {day:-1, section:-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){ var dt = d.toJSONLocal().slice(0, 10); return dt.slice(8, 10) + '/' + dt.slice(5, 7) + '/' + dt.slice(0, 4) + ' (' + ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][d.getDay()] +')'; }, rm_section: function(idx){ this.journey_data.main.splice(idx,1); }, toggle_section_vis: function(idx){ this.journey_data.main[idx].hidden = !this.journey_data.main[idx].hidden; }, get_section_vis: function(idx){ return !this.journey_data.main[idx].hidden }, 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)); console.log(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))); }, 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):(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)}, }, created: function () { axios.get('/api/'+this.journey_id).then(response =>{ console.log(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.length===2){ e.dateRange[0]= new Date(e.dateRange[0]); e.dateRange[1]= new Date(e.dateRange[1]); } } this.journey_step_data = this.step_convert(this.journey_step); }); 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() if(ndata.main.length == odata.main.length){ for(let d in ndata.main){ if(ndata.main[d].hotel != odata.main[d].hotel){ if(ndata.main[d].hotel.latlon){ this.journey_data.main[d].map.center=ndata.main[d].hotel.latlon; } } } } }, deep: true, }, }, })