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.journey_data.main[s].dateRange? this.step_len(s):0; 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 = 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.journey_data.main[s].dateRange? this.step_len(s):0; 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()]; }, 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{ 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=>{ 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)}, }, created: function () { 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.length===2){ e.dateRange[0]= new Date(e.dateRange[0]); e.dateRange[1]= new Date(e.dateRange[1]); } e.flight = undefined; 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, }, }, })