This commit is contained in:
Andy Bunce 2017-09-02 22:35:31 +01:00
parent 38ead82e9c
commit a9b2bff043
27 changed files with 1430 additions and 373 deletions

View file

@ -38,7 +38,7 @@ var settings = {
}); });
}); });
}, },
setItem (key,value,callback) { setItem (key,value) {
if (this.debug) console.log('setItem',key,value); if (this.debug) console.log('setItem',key,value);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
localforage.setItem(key, value) localforage.setItem(key, value)
@ -50,16 +50,23 @@ var settings = {
return new Promise((resolve, reject) => {reject(err);}) return new Promise((resolve, reject) => {reject(err);})
}); });
}) })
},
length(){
return new Promise((resolve, reject) => {
localforage.keys() // returns array of keys
.then((value) => {
console.log('length ',value);
return new Promise((resolve, reject) => {resolve(value);})
}).catch((err) => {
console.log('length');
return new Promise((resolve, reject) => {reject(err);})
});
})
} }
};
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` is a Vue-specific error info, e.g. which lifecycle hook
console.error(err, vm, info);
alert("vue error");
}; };
//Returns a function, that, as long as it continues to be invoked, will not //Returns a function, that, as long as it continues to be invoked, will not
//be triggered. The function will be called after it stops being called for //be triggered. The function will be called after it stops being called for
//N milliseconds. If `immediate` is passed, trigger the function on the //N milliseconds. If `immediate` is passed, trigger the function on the
@ -77,19 +84,50 @@ function debounce(func, wait, immediate) {
}; };
}; };
// https://stackoverflow.com/questions/36672561/how-to-exit-fullscreen-onclick-using-javascript
function fullscreen() {
var isInFullScreen = (document.fullscreenElement && document.fullscreenElement !== null) ||
(document.webkitFullscreenElement && document.webkitFullscreenElement !== null) ||
(document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
(document.msFullscreenElement && document.msFullscreenElement !== null);
var docElm = document.documentElement;
if (!isInFullScreen) {
if (docElm.requestFullscreen) {
docElm.requestFullscreen();
} else if (docElm.mozRequestFullScreen) {
docElm.mozRequestFullScreen();
} else if (docElm.webkitRequestFullScreen) {
docElm.webkitRequestFullScreen();
} else if (docElm.msRequestFullscreen) {
docElm.msRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
};
const router = new VueRouter({ const router = new VueRouter({
base:"/vue-poc/ui/", base:"/vue-poc/ui/",
mode: 'history', mode: 'history',
routes: [ routes: [
{ path: '/', component: Home,meta:{title:"Home"} }, { path: '/', component: Home, meta:{title:"Home"} },
{ path: '/session', component: Session ,meta:{title:"Session"}}, { path: '/session', component: Session ,meta: {title:"Session"}},
{ path: '/images/item', component: Images, meta:{title:"Images"} }, {path: '/images', redirect: '/images/item' },
{ path: '/images/report', name:"image-reports",component: Report, props: true, meta:{title: "Image report"}}, { path: '/images/item', name:'images', component: Images, meta:{title: "Images"} },
{ path: '/images/report', name:"image-reports", component: Report, props: true, meta:{title: "Image report"}},
{ path: '/images/item/:id', name:"image",component: Image, props: true, meta:{title: "Image details"}}, { path: '/images/item/:id', name:"image",component: Image, props: true, meta:{title: "Image details"}},
{ path: '/images/thumbnail', component: Thumbnail, meta:{title:"Thumbnail generator"} }, { path: '/images/thumbnail', component: Thumbnail, meta:{title:"Thumbnail generator"} },
{ path: '/images/keywords', component: Keywords, meta:{title:"Thumbnail keywords"} }, { path: '/images/keywords', component: Keywords, meta:{title:"Image keywords"} },
{ path: '/images/dates', component: Dates, meta:{title:"Thumbnail dates"} }, { path: '/images/dates', component: Dates, meta:{title:"Image dates"} },
{ path: '/select', component: Select, meta:{title:"Select"} }, { path: '/select', component: Select, meta:{title:"Select"} },
{ path: '/search', component: Search, meta:{title:"Search"} }, { path: '/search', component: Search, meta:{title:"Search"} },
{ path: '/tabs', component: Tabs,meta:{title:"tab test",requiresAuth: true} }, { path: '/tabs', component: Tabs,meta:{title:"tab test",requiresAuth: true} },
@ -100,6 +138,7 @@ const router = new VueRouter({
{ path: '/database', component: Files,meta:{title:"Databases"},props:{protocol:"basexdb"} }, { path: '/database', component: Files,meta:{title:"Databases"},props:{protocol:"basexdb"} },
{ path: '/ping', component: Ping,meta:{title:"Ping"} }, { path: '/ping', component: Ping,meta:{title:"Ping"} },
{ path: '/settings', component: Settings, meta:{title:"Settings"} }, { path: '/settings', component: Settings, meta:{title:"Settings"} },
{ path: '/acesettings', component: Acesettings, meta:{title:"Editor settings"} },
{ path: '/history', component: History, meta:{title:"File History"} }, { path: '/history', component: History, meta:{title:"File History"} },
{ path: '/puzzle', component: Puzzle, meta:{title:"Jigsaw"} }, { path: '/puzzle', component: Puzzle, meta:{title:"Jigsaw"} },
{ path: '/eval', component: Eval, meta:{title:"Evaluate XQuery"} }, { path: '/eval', component: Eval, meta:{title:"Evaluate XQuery"} },
@ -137,6 +176,7 @@ router.beforeEach((to, from, next) => {
}); });
Vue.use(Vuetify); Vue.use(Vuetify);
const app = new Vue({ const app = new Vue({
router, router,
data:function(){return { data:function(){return {
@ -217,10 +257,58 @@ const app = new Vue({
showAlert(msg){ showAlert(msg){
this.alert.msg=moment().format()+" "+ msg this.alert.msg=moment().format()+" "+ msg
this.alert.show=true this.alert.show=true
},
fullscreenEnabled(){
return document.fullscreenEnabled
},
isInFullScreen(){
return (document.fullscreenElement && document.fullscreenElement !== null) ||
(document.webkitFullscreenElement && document.webkitFullscreenElement !== null) ||
(document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
(document.msFullscreenElement && document.msFullscreenElement !== null)
},
fullscreen(){
// https://stackoverflow.com/questions/36672561/how-to-exit-fullscreen-onclick-using-javascript
var isInFullScreen = this.isInFullScreen();
alert(isInFullScreen);
var docElm = document.documentElement;
if (!isInFullScreen) {
if (docElm.requestFullscreen) {
docElm.requestFullscreen();
} else if (docElm.mozRequestFullScreen) {
docElm.mozRequestFullScreen();
} else if (docElm.webkitRequestFullScreen) {
docElm.webkitRequestFullScreen();
} else if (docElm.msRequestFullscreen) {
docElm.msRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
} }
}, },
computed:{
fullscreenIcon(){ return this.isInFullScreen()?'fullscreen_exit':'fullscreen'}
},
created(){ created(){
console.log("create-----------") console.log("create-----------")
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` is a Vue-specific error info, e.g. which lifecycle hook
console.error(err, vm, info);
this.showAlert("vue error:\n"+err)
alert("vue error");
};
// Add a response interceptor // Add a response interceptor
HTTP.interceptors.response.use( HTTP.interceptors.response.use(
@ -229,8 +317,8 @@ const app = new Vue({
return response; return response;
}, },
(error) =>{ (error) =>{
// Do something with response error // interupt restxq single
this.showAlert("http error:\n"+error.response.data) if(460 != error.response.status)this.showAlert("http error:\n"+error.response.data)
return Promise.reject(error); return Promise.reject(error);
}); });

View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<!--
simple full screen
-->
<template id="qd-fullscreen">
<a onclick="fullscreen()" href="javascript:void(0);">fS</a>
</template>
<script>{
created:function(){
console.log("qd-fullscreen");
}
}
</script>

View file

@ -1,9 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<template id="about"> <v-layout class="ma-5"> <v-flex <template id="about">
xs4> <v-card hover raised> <v-card-title <v-container>
height="200px" class="pa-5 indigo accent-3"> <v-parallax src="/vue-poc/ui/vue-poc.png" >
<div class="display-1 white--text text-xs-center">VUE-POC</div> </v-parallax>
v0.0.3 </v-card-title> </v-card> </v-flex> <v-flex xs4> <v-card>
<v-card-text>
<p> <p>
This is a experiment in using This is a experiment in using
<code>vue.js</code> <code>vue.js</code>
@ -22,7 +25,11 @@ v0.0.3 </v-card-title> </v-card> </v-flex> <v-flex xs4>
<li> <router-link to="database?url=%2Fvue-poc%2F">DB</router-link></li> <li> <router-link to="database?url=%2Fvue-poc%2F">DB</router-link></li>
</ul> </ul>
</v-flex> <v-btn floating="floating"> <v-icon>add</v-icon> </v-btn> <qd-link </v-flex> <v-btn floating="floating"> <v-icon>add</v-icon> </v-btn> <qd-link
href="/dba">REPLACED</qd-link> </v-layout> </template> href="/dba">REPLACED</qd-link>
</v-card-text>
</v-card>
</v-container>
</template>
<script> <script>
{ {
} }

View file

@ -43,7 +43,7 @@
</v-btn> </v-btn>
<v-btn icon @click="acecmd('foldall')" title="fold all"> <v-btn icon @click="togglefold" title="fold toggle">
<v-icon>vertical_align_center</v-icon> <v-icon>vertical_align_center</v-icon>
</v-btn> </v-btn>
@ -140,6 +140,7 @@ v-on:annotation="annotation"></vue-ace>
snackbar: false, snackbar: false,
message: "Cant do that", message: "Cant do that",
events: new Vue({}), events: new Vue({}),
folded: false, // toggle fold/unfold action
mimemap:{ mimemap:{
"text/xml":"xml", "text/xml":"xml",
"application/xml":"xml", "application/xml":"xml",
@ -192,6 +193,10 @@ v-on:annotation="annotation"></vue-ace>
}); });
}, },
togglefold(){
this.folded=!this.folded
this.acecmd(this.folded?"foldall":"unfoldall")
},
acecmd(cmd){ acecmd(cmd){
//alert("acecmd: "+cmd) //alert("acecmd: "+cmd)
this.events.$emit('eventFired',cmd); this.events.$emit('eventFired',cmd);

View file

@ -9,20 +9,63 @@
<v-toolbar class="orange darken-1"> <v-toolbar class="orange darken-1">
<v-btn icon to="./"><v-icon>arrow_back</v-icon></v-btn> <v-btn icon to="./"><v-icon>arrow_back</v-icon></v-btn>
<v-card-title > <v-card-title >
<v-chip >todo</v-chip> <v-chip >Images by year and month</v-chip>
</v-card-title> </v-card-title>
<v-spacer></v-spacer> <v-spacer></v-spacer>
</v-toolbar> </v-toolbar>
<v-card-text> <v-card-text>
dates todo <v-progress-linear v-if="busy" v-bind:indeterminate="true" ></v-progress-linear>
<v-container v-if="!busy" fluid>
<v-layout v-for="year in items"
:key="year.year">
<v-flex v-text="year.year"></v-flex>
<v-flex v-for="(m,i) in year.months"
:key="i"><v-btn icon primary :disabled="0==m" @click="go(year.year,i)">{{m}}</v-btn></v-flex>
</v-layout>
</v-container>
<v-layout>
</v-card-text> </v-card-text>
</v-card> </v-card>
</v-container> </v-container>
</template> </template>
<script>{ <script>{
data: ()=>({
busy: false,
total: 0,
items: [],
elapsed: null,
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
}),
methods:{
getDatetaken(){
this.busy=true
var t0 = performance.now();
HTTP.get("images/datetaken")
.then(r=>{
this.busy=false
this.total=r.data.total
this.items=r.data.items
var t1 = performance.now();
this.elapsed= 0.001 *(t1 - t0)
})
},
go(year,month){
month=("0" + (1+month)).slice(-2)
// alert("year: "+year+" "+month)
var from=year + "-" + month + "-01"
var lastday=new Date(year, month, 0).getDate()
var until=year + "-" + month + "-"+ lastday
this.$router.push({ name: 'images', query: { from:from, until:until }})
}
},
created:function(){
console.log("create datetaken")
this.getDatetaken()
}
} }
</script> </script>

View file

@ -20,24 +20,24 @@
<v-chip class="primary white--text">{{ total }} in {{ elapsed | round(2) }} secs </v-chip> <v-chip class="primary white--text">{{ total }} in {{ elapsed | round(2) }} secs </v-chip>
Page:{{ query.page+1 }} Page:{{ query.page+1 }}
<v-btn @click.stop="query.page=Math.min(0,query.page-1)" :disabled="query.page==0" icon primary> <v-btn @click.stop="pageBack()" :disabled="query.page==0" icon primary>
<v-icon>arrow_back</v-icon> <v-icon>arrow_back</v-icon>
</v-btn> </v-btn>
<v-btn @click.stop="query.page+=1" icon primary> <v-btn @click.stop="pageNext()" icon primary>
<v-icon>arrow_forward</v-icon> <v-icon>arrow_forward</v-icon>
</v-btn> </v-btn>
</span> </span>
</v-toolbar> </v-toolbar>
<v-progress-linear v-if="busy" v-bind:indeterminate="true" ></v-progress-linear> <v-progress-linear v-if="busy" v-bind:indeterminate="true" ></v-progress-linear>
<v-container v-if="!busy" fluid grid-list-md> <v-container v-if="!busy" fluid grid-list-md>
<v-layout row wrap> <v-layout row wrap v-touch="{ left: () => pageNext(), right: () => pageBack()}">
<v-flex height="80px" <v-flex height="80px"
xs2 xs2
v-for="image in images" v-for="image in images"
:key="image.name" :key="image.name"
> >
<v-card class="grey lighten-2 pt-1"> <v-card class="grey lighten-2 pt-1">
<v-card-media :src="src(image)" @click="go(image)" <v-card-media :src="src(image)" @dblclick="go(image)"
height="80px" contain></v-card-media> height="80px" contain></v-card-media>
<v-card-actions v-tooltip:top="{ html: ' '+image.path }"> <v-card-actions v-tooltip:top="{ html: ' '+image.path }">
@ -69,9 +69,17 @@
<v-select <v-select
v-bind:items="keywords" v-bind:items="keywords"
v-model="query.keyword" v-model="query.keyword"
label="Keyword" label="Keyword" item-value="text" item-text="text"
autocomplete autocomplete
></v-select> >
<template slot="item" scope="data">
<v-list-tile-content>
<v-list-tile-title v-html="data.item.text"></v-list-tile-title>
<v-list-tile-sub-title v-html="data.item.count"></v-list-tile-sub-title>
</v-list-tile-content>
</template>
</v-select>
<v-btn @click="query.keyword=null" :disabled="!query.keyword"> <v-btn @click="query.keyword=null" :disabled="!query.keyword">
<v-icon>close</v-icon>Clear keyword <v-icon>close</v-icon>Clear keyword
</v-btn> </v-btn>
@ -208,6 +216,12 @@
}, },
go(image){ go(image){
this.$router.push({ name: 'image', params: { id: image.id }}) this.$router.push({ name: 'image', params: { id: image.id }})
},
pageBack(){
this.query.page=Math.min(0,this.query.page-1)
},
pageNext(){
this.query.page+=1
} }
}, },
@ -235,7 +249,7 @@
showFilter(){ showFilter(){
if(this.keywords.length==0){ if(this.keywords.length==0){
HTTP.get("images/keywords") HTTP.get("images/keywords2")
.then(r=>{ .then(r=>{
this.keywords=r.data.items this.keywords=r.data.items
}) })

View file

@ -26,7 +26,7 @@ function vue-api:id( $id as xs:integer)
</json> </json>
}; };
(:~ (:~
: do a thumbnail : get set of thumbnails matching search
:) :)
declare declare
%rest:single %rest:single
@ -62,22 +62,52 @@ $keyword
</json> </json>
}; };
(:~ (:~
: keywords : keywords
:) :)
declare declare
%rest:GET %rest:path("/vue-poc/api/images/keywords") %rest:GET %rest:path("/vue-poc/api/images/keywords2")
%rest:produces("application/json") %rest:produces("application/json")
%output:method("json") %output:method("json")
function vue-api:keywords() function vue-api:keywords2()
{ {
let $keys:= let $keys:=db:open($cfg:DB-IMAGE,"keywords.xml")/keywords/keyword
collection($cfg:DB-IMAGE || "/image")/image/keywords/keyword
=>distinct-values()
=>sort("http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive")
return <json type="object" > return <json type="object" >
<items type="array">{ <items type="array">{
$keys!<_ >{.}</_> $keys!<_ type="object">
<text>{@name/string()}</text>
<count>{@count/string()}</count>
</_>
}</items>
</json>
};
(:~
: keywords
:)
declare
%rest:GET %rest:path("/vue-poc/api/images/datetaken")
%rest:produces("application/json")
%output:method("json")
function vue-api:datetaken()
{
let $years:=db:open($cfg:DB-IMAGE,"datetaken.xml")/dates/year
return <json type="object" >
<items type="array">{
for $year in $years
return <_ type="object">
<year>{$year/@value/string()}</year>
<count type="number">{$year/@count/string()}</count>
<months type="array">{
for $m in 1 to 12
let $c:= $year/month[@value=format-integer($m,"00")]/@count
return <_ type="number">{if($c)then string($c) else 0}</_>
}</months>
</_>
}</items> }</items>
</json> </json>
}; };

View file

@ -9,20 +9,63 @@
<v-toolbar class="orange darken-1"> <v-toolbar class="orange darken-1">
<v-btn icon to="./"><v-icon>arrow_back</v-icon></v-btn> <v-btn icon to="./"><v-icon>arrow_back</v-icon></v-btn>
<v-card-title > <v-card-title >
<v-chip >todo</v-chip> <v-chip >click to show</v-chip>
</v-card-title> </v-card-title>
<v-spacer></v-spacer> <v-spacer></v-spacer>
</v-toolbar> </v-toolbar>
<v-card-text> <v-card-text>
keywords todo <v-progress-linear v-if="busy" v-bind:indeterminate="true" ></v-progress-linear>
<v-container v-if="!busy" fluid grid-list-md>
<v-layout row wrap v-touch="{ left: () => pageNext(), right: () => pageBack()}">
<v-flex height="80px"
xs3
v-for="keyword in items"
:key="keyword.text"
>
<v-card class="grey lighten-2 pt-1" @click="show(keyword)">
<v-toolbar>
<v-card-title v-text="keyword.text"></v-card-title>
<v-spacer></v-spacer>
<v-chip>{{keyword.count}}</v-chip>
</v-toolbar>
</v-card>
</v-flex>
</v-layout>
</v-container>
</v-card-text> </v-card-text>
</v-card> </v-card>
</v-container> </v-container>
</template> </template>
<script>{ <script>{
data: ()=>({
busy: false,
total: 0,
items: [],
elapsed: null
}),
methods:{
getKeywords(){
this.busy=true
var t0 = performance.now();
HTTP.get("images/keywords2")
.then(r=>{
this.busy=false
this.total=r.data.total
this.items=r.data.items
var t1 = performance.now();
this.elapsed= 0.001 *(t1 - t0)
})
},
show(keyword){
this.$router.push({ name: 'images', query: { keyword: keyword.text }})
} }
</script> },
created:function(){
console.log("create keywords")
this.getKeywords()
}
}</script>

View file

@ -0,0 +1,27 @@
(:~ get keywords
: <keyword name="2 orlop" count="31">
: <dates earliest="2010-08-05T15:40:54" latest="2011-03-06T18:04:28"/>
: <idref>14569796 14569818 </idref>
: </keyword>
:)
import module namespace cfg = "quodatum:media.image.configure" at "config.xqm";
declare %updating function local:put($data,$path){
db:replace($cfg:DB-IMAGE,$path,$data)
};
declare variable $DEST:="/keywords.xml";
let $images:=collection($cfg:DB-IMAGE || "/image")/image
let $keywords:= $images/keywords/keyword=>distinct-values()
let $kd:=<keywords date="{current-dateTime()}">{
for $k in $keywords
order by lower-case($k)
let $i:=$images[keywords/keyword = $k]
let $i:=sort($i,(),function($x){$x/datetaken})
let $earliest:=head($i)
let $latest:=$i[last()]
return <keyword name="{$k}" count="{count($i)}">
<dates earliest="{$earliest/datetaken}" latest="{$latest/datetaken}"/>
<idref >{db:node-id($i)=>string-join(" ")}</idref>
</keyword>
}</keywords>
return $kd=>local:put($DEST)

View file

@ -0,0 +1,28 @@
(:~ get datetaken
: <keyword name="2 orlop" count="31">
: <dates earliest="2010-08-05T15:40:54" latest="2011-03-06T18:04:28"/>
: <idref>14569796 14569818 </idref>
: </keyword>
:)
import module namespace cfg = "quodatum:media.image.configure" at "config.xqm";
declare %updating function local:put($data,$path){
db:replace($cfg:DB-IMAGE,$path,$data)
};
declare variable $DEST:="/datetaken.xml";
let $dates:=<dates date="{current-dateTime()}">{
for $image in collection($cfg:DB-IMAGE || "/image")/image[not(@original)]
let $year:=substring($image/datetaken,1,4)
group by $year
order by $year descending
return <year value="{$year}" count="{count($image)}">
{for $image in $image
let $month:=substring($image/datetaken,6,2)
group by $month
order by $month
return <month value="{$month}" count="{count($image)}"/>
}
</year>
}</dates>
return $dates =>local:put($DEST)

View file

@ -7,10 +7,29 @@ import module namespace session = "http://basex.org/modules/session";
(:~ Session key. :) (:~ Session key. :)
declare variable $vue-login:SESSION-KEY := "vue-poc"; declare variable $vue-login:SESSION-KEY := "id";
(:~ Current session. :) (:~ Current session. :)
declare variable $vue-login:SESSION-VALUE := session:get($vue-login:SESSION-KEY); declare variable $vue-login:SESSION-VALUE := session:get($vue-login:SESSION-KEY);
(:~
: get status
:)
declare
%rest:GET %rest:path("/vue-poc/api/status")
%rest:produces("application/json")
%output:method("json")
function vue-login:status( )
{
let $user:=session:get("id","")
let $role:=if($user and user:exists($user)) then user:list-details($user)/@permission/string() else ""
return <json type="object" >
<user>{ if($user) then $user else "guest" }</user>
<permission>{$role}</permission>
<session>{session:id()}</session>
<created>{session:created()}</created>
</json>
};
(:~ (:~
: Checks the user input and redirects to the main page, or back to the login page. : Checks the user input and redirects to the main page, or back to the login page.
: @param $name user name : @param $name user name

View file

@ -13,10 +13,8 @@
<br/> <br/>
<table> <table>
<tr v-for="(item, row) in grid"> <tr v-for="(item, row) in grid">
<td v-for="(cell,col) in item" style="width:50px;height:50px;" > <td v-for="(cell,col) in item" style="width:50px;height:50px;" @click="click(row,col)">
<v-btn @click="click(row,col)" :disabled="disabled(row,col)">
<img :src="src(row,col)" style="width:50px;height:50px;"/> <img :src="src(row,col)" style="width:50px;height:50px;"/>
</v-btn>
</td> </td>
</tr> </tr>
</table> </table>
@ -39,6 +37,7 @@
}, },
methods: { methods: {
click(row,col) { click(row,col) {
if(this.disabled(row,col))return;
var g=this.grid var g=this.grid
var h=g[row][col] var h=g[row][col]
g[row][col]=null g[row][col]=null

View file

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<template id="settings"> <template id="acesettings">
<v-layout row> <v-layout row>
<v-flex xs12 sm6 offset-sm3> <v-flex xs12 sm6 offset-sm3>
<v-card> <v-card>
@ -97,7 +97,7 @@
fontsize: "14px" fontsize: "14px"
}, },
keybindings:[ 'Ace', 'Vim', 'Emacs' ] keybindings:[ 'ace', 'vim', 'emacs', 'textarea', 'sublime' ]
} }
}, },

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<template id="settings">
<v-container fluid>
<v-card>
<v-card-text>
<router-link to="/acesettings">Editor settings</router-link>
</v-card-text>
</v-card>
<v-card>
<v-card-title>System information</v-card-title>
<v-card-text></v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn @click="wipe" error>Wipe</v-btn></v-card-actions>
</v-card>
</v-container>
</template>
<script>{
data:function(){return {
keys:[]
}
},
methods:{
wipe(){
alert("wipe")
}
},
created:function(){
console.log("settings")
settings.length()
.then(k=>{
console.log("length:",k)
this.keys=k;
})
}
}
</script>

View file

@ -7,6 +7,7 @@
<v-icon>menu</v-icon> <v-icon>menu</v-icon>
</v-btn> </v-btn>
<v-card-title>Page Title</v-card-title> <v-card-title>Page Title</v-card-title>
<v-spacer></v-spacer>
<v-btn icon light> <v-btn icon light>
<v-icon>search</v-icon> <v-icon>search</v-icon>
</v-btn> </v-btn>
@ -15,7 +16,7 @@
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
<v-tabs-bar slot="activators"> <v-tabs-bar slot="activators" class="green">
<v-tabs-slider></v-tabs-slider> <v-tabs-slider></v-tabs-slider>
<v-tabs-item <v-tabs-item
v-for="i in 13" v-for="i in 13"

View file

@ -1,215 +0,0 @@
(:~
: generate xquery access code for entity definitions
:)
module namespace bf = 'quodatum.tools.buildfields';
declare default function namespace 'quodatum.tools.buildfields';
declare namespace ent="https://github.com/Quodatum/app-doc/entity";
(:~
: write generated xquery module from entity xml
: @param efolder full path to folder with entities e.g. fn:resolve-uri("./data/models")
: @param dest full name of xqm to create e.g. fn:resolve-uri("models.xqm")
:)
declare %updating function write($efolder as xs:string,$dest as xs:string)
{
let $src:=bf:module(bf:sources($efolder))
return file:write-text($dest,$src)
};
(:~
: generate xquery module for given entities as a string
:)
declare function module($entities as element(ent:entity)*) as xs:string
{
let $src:= <text>(: entity access maps
: auto generated from xml files in entities folder at: {fn:current-dateTime()}
:)
module namespace entity = 'quodatum.models.generated';
{bf:build-modules($entities)}
{bf:build-namespaces($entities)}
{( bf:build-describe($entities))}
(:~ map of access functions for entity :)
declare function entity:fields($entity as xs:string)
as map(*){{
$entity:list($entity)("access")
}};
</text>
return $src
};
(:~
: generate xquery for to return field value in the format: "name":function($_){}
:)
declare function accessfn($f as element(ent:field)) as xs:string
{
let $type:=$f/@type/fn:string()
return <field>
"{$f/@name/fn:string()}": function($_ as element()) as {$type} {{$_/{$f/ent:xpath } }}</field>
};
declare function generate($e as element(ent:entity)) as xs:string
{
let $fields:=for $field in $e/ent:fields/ent:field
order by $field/@name
return $field
let $filter:=$e/ent:views/ent:view[@name="filter"]=>fn:tokenize()
let $filter:= $e/ent:fields/ent:field[@name=$filter]/ent:xpath/fn:concat("$item/",.)
return <field>
"{$e/@name/fn:string()}": map{{
"name": "{ $e/@name/fn:string()}",
"description": "{ escape($e/ent:description)}",
"access": map{{ {$fields!accessfn(.)=>fn:string-join(",")} }},
"filter": function($item,$q) as xs:boolean{{
some $e in ( {fn:string-join($filter,", ")}) satisfies
fn:contains($e,$q, 'http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive')
}},
"json": map{{ {$fields!jsonfn(.)=>fn:string-join(",")} }},
"data": function() as {$e/ent:data/@type/fn:string(.)}*
{{ {let $a:=$e/ent:data/fn:string() return if($a)then $a else "()"} }},
"views": map{{
{$e/ent:views/ent:view!("'" || @name || "': '" ||. || "'")=>fn:string-join(',')}
}}
}}</field>
};
(:~
: @return sequence of element(entity) items for definitions at path
:)
declare function sources($path as xs:string) as element(ent:entity)*
{
let $_:=fn:trace($path,"DD")
let $p:=fn:resolve-uri($path) || "/"
return for $f in file:list($p)
order by $f
return fn:doc(fn:concat($p,$f))/ent:entity
};
(:map for entity :)
declare function build-map($entity as element(ent:entity)) as xs:string
{
let $m:=for $field in $entity/ent:fields/ent:field
order by $field/@name
return accessfn($field)
return <text>
declare variable $entity:{$entity/@name/fn:string()}: map{{ {fn:string-join($m,",")}
}};
</text>
};
(:~
: return xml for suitable json serialization for field
:)
declare function jsonfn($f as element(ent:field))
as xs:string
{
let $name:=$f/@name/fn:string()
let $type:=$f/@type/fn:string()
let $opt:=fn:contains($type,"?")
let $repeat:=fn:contains($type,"*")
let $json-type:=json-type($type)
let $mult:=if($repeat) then "*" else "?"
let $at:=if($json-type ne "string")
then "attribute type {'" || $json-type || "'},"
else ""
(: generate json xml :)
let $simple:=function() as xs:string{
<field>(: {$type} :)
fn:data($_/{$f/ent:xpath })!element {$name} {{ {$at} .}}
</field>
}
let $array:=function() as xs:string{
<field>(: array of strings :)
element {$name} {{
attribute type {{"array"}},
$_/{$f/ent:xpath }!element _ {{ attribute type {{"string"}}, .}}
}}
</field>
}
(: serialize when element :)
let $element:=function() as xs:string{
<field>element {$name} {{
attribute type {{"string"}},
fn:serialize($_/{$f/ent:xpath})}}</field>
}
return <field>
"{$name}": function($_ as element()) as element({$name}){$mult} {{
{if($repeat)then
$array()
else if($type="element()") then
$element()
else $simple()} }}</field>
};
(:~ convert xs type to json
:)
declare function json-type($xsd as xs:string) as xs:string{
switch ($xsd)
case "element()" return "string"
case "xs:boolean" return "boolean"
case "xs:integer" return "number"
case "xs:float" return "number"
case "xs:double" return "number"
case "xs:string*" return "array"
default return "string"
};
(:~ declare any namespaces found :)
declare function build-namespaces($entities as element()*){
for $n in distinct-deep($entities/ent:namespace)
return
<text>declare namespace {$n/@prefix/fn:string()}='{$n/@uri/fn:string()}';
</text>
};
(:~ declare any namespaces found :)
declare function build-modules($entities as element()*){
for $n in distinct-deep($entities/ent:module)
return
<text>import module namespace {$n/@prefix/fn:string()}='{$n/@namespace/fn:string()}';
</text>
};
declare function build-describe($entities){
let $m:=for $e in $entities
return generate($e)
return <text>
declare variable $entity:list:=map {{ {fn:string-join($m,",")}
}};
</text>
};
declare function escape($str as xs:string)
as xs:string{
fn:replace(
fn:replace($str,'"','""'),
"'","''")
};
(:-----from functx-------------------:)
declare function distinct-deep
( $nodes as node()* ) as node()* {
for $seq in (1 to fn:count($nodes))
return $nodes[$seq][fn:not(is-node-in-sequence-deep-equal(
.,$nodes[fn:position() < $seq]))]
};
declare function is-node-in-sequence-deep-equal
( $node as node()? ,
$seq as node()* ) as xs:boolean {
some $nodeInSeq in $seq satisfies fn:deep-equal($nodeInSeq,$node)
} ;

View file

@ -6,7 +6,7 @@
:) :)
module namespace vue-api = 'quodatum:vue.api'; module namespace vue-api = 'quodatum:vue.api';
import module namespace bf = 'quodatum.tools.buildfields' at "entity-gen.xqm"; import module namespace bf = 'quodatum.tools.buildfields' at "./../../../lib/entity-gen.xqm";
(:~ (:~
: Returns a file content. : Returns a file content.
@ -19,9 +19,13 @@ declare
%output:method("json") %output:method("json")
%updating %updating
function vue-api:model($efolder ,$target ) function vue-api:model($efolder ,$target )
{( {
let $config:='import module namespace cfg = "quodatum:media.image.configure" at "features/images/config.xqm";'
let $src:=bf:module(bf:entities($efolder),$config)
return (
prof:variables(), prof:variables(),
bf:write($efolder,$target), file:write-text($target,$src),
db:output(<json type="object"><msg>Updated: {$target}</msg></json>) db:output(<json type="object"><msg>Updated: {$target}</msg></json>)
)}; )
};

View file

@ -72,6 +72,7 @@
this.waiting=false this.waiting=false
this.alert={msg:error.response.data,success:false,error:true} this.alert={msg:error.response.data,success:false,error:true}
console.log(error); console.log(error);
alert("bad")
}); });
} }
}, },

View file

@ -0,0 +1,224 @@
<!DOCTYPE html>
<template id="vuepoc">
<v-app >
<v-navigation-drawer persistent light :mini-variant.sync="mini" v-model="drawer"
:disable-route-watcher="true" height="100%" class="grey lighten-4 pb-0">
<v-list class="pa-0">
<v-list-tile avatar tag="div">
<v-list-tile-avatar >
<v-btn icon @click="session">
<img src="/vue-poc/ui/quodatum.gif" />
</v-btn>
</v-list-tile-avatar>
<v-list-tile-content>
<v-list-tile-title>Vue PoC</v-list-tile-title>
</v-list-tile-content>
<v-list-tile-action>
<v-btn icon @click.stop="mini = !mini">
<v-icon>chevron_left</v-icon>
</v-btn>
</v-list-tile-action>
</v-list-tile>
</v-list>
<qd-navlist :items="items"></qd-navlist>
</v-navigation-drawer>
<v-toolbar class="indigo" dark >
<v-toolbar-side-icon @click.stop="drawer = !drawer" ></v-toolbar-side-icon>
<v-toolbar-title class="hidden-sm-and-down" >{{$route.meta.title}}</v-toolbar-title>
<v-spacer></v-spacer>
<v-text-field prepend-icon="search" label="Search..." v-model="q"
hide-details single-line dark @keyup.enter="search"></v-text-field>
<v-menu left transition="v-fade-transition">
<v-btn dark icon slot="activator">
{{$auth.user}}
</v-btn>
<v-list>
<v-list-tile @click="logout()">
<v-list-tile-title >logout</v-list-tile-title>
</v-list-tile>
<v-list-tile>
<v-list-tile-title >permission: {{$auth.permission}}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
<v-btn icon @click="fullscreen" :disabled="!fullscreenEnabled">
<v-icon>{{ fullscreenIcon }}</v-icon>
</v-btn>
<qd-fullscreen></qd-fullscreen>
</v-toolbar>
<main>
<v-alert error value="true" dismissible v-model="alert.show">
<pre style="overflow:auto;">{{ alert.msg }}</pre>
</v-alert>
<transition name="fade" mode="out-in">
<router-view class="view ma-3"></router-view>
</transition>
</main>
</v-app>
</template>
<script>{
// router,
data:function(){return {
q:"",
status:{},
drawer:true,
mini: false,
alert:{show:false,msg:"Hello"},
items:[
{href: '/',text: 'Home', icon: 'home' },
{
icon: 'folder_open',
text: 'Collections' ,
model: false,
children: [
{href: '/database', text: 'Databases',icon: 'developer_mode' },
{href: '/files', text: 'File system',icon: 'folder' },
{href: '/edit',text: 'Edit',icon: 'mode_edit'},
{href: '/history',text: 'history',icon: 'history'}
]},
{
icon: 'directions_run',
text: 'Actions' ,
model: false,
children: [
{href: '/eval',text: 'Query',icon: 'play_circle_outline'},
{href: '/tasks',text: 'Tasks',icon: 'history'}
]},
{
icon: 'cast_connected',
text: 'Server' ,
model: false,
children: [
{href: '/jobs',text: 'Running jobs',icon: 'dashboard'},
{href: '/logs',text: 'Server logs',icon: 'dns'},
{href: '/ping',text: 'Ping',icon: 'update'}
]},
{
icon: 'camera_roll',
text: 'Images' ,
model: false,
children: [
{href: '/images/item',text: 'Collection',icon: 'photo_camera'},
{href: '/images/keywords',text: 'Keywords',icon: 'label'},
{href: '/images/dates',text: 'Date taken',icon: 'date_range'},
{href: '/images/thumbnail',text: 'Thumbnail',icon: 'touch_app'},
{href: '/images/report',text: 'Reports',icon: 'report'}
]},
{
icon: 'more_horiz',
text: 'More' ,
model: false,
children: [
{href: '/session',text: 'Session',icon: 'person'},
{href: '/select',text: 'Select',icon: 'extension'},
{href: '/puzzle',text: 'Puzzle',icon: 'extension'},
{href: '/tabs',text: 'Tabs',icon: 'switch_camera'},
{href: '/timeline',text: 'Time line',icon: 'timelapse'}
]},
{href: '/settings',text: 'Settings',icon: 'settings' },
{href: '/about',text: 'About', icon: 'help' },
]
}},
methods: {
session(){
this.$router.push({path: '/session'})
},
search(){
this.$router.push({path: '/search',query: { q: this.q }})
},
logout(){
HTTP.get("logout").then(r=>{
alert("logout")
})
},
showAlert(msg){
this.alert.msg=moment().format()+" "+ msg
this.alert.show=true
},
fullscreenEnabled(){
return document.fullscreenEnabled
},
isInFullScreen(){
return (document.fullscreenElement && document.fullscreenElement !== null) ||
(document.webkitFullscreenElement && document.webkitFullscreenElement !== null) ||
(document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
(document.msFullscreenElement && document.msFullscreenElement !== null)
},
fullscreen(){
// https://stackoverflow.com/questions/36672561/how-to-exit-fullscreen-onclick-using-javascript
var isInFullScreen = this.isInFullScreen();
alert(isInFullScreen);
var docElm = document.documentElement;
if (!isInFullScreen) {
if (docElm.requestFullscreen) {
docElm.requestFullscreen();
} else if (docElm.mozRequestFullScreen) {
docElm.mozRequestFullScreen();
} else if (docElm.webkitRequestFullScreen) {
docElm.webkitRequestFullScreen();
} else if (docElm.msRequestFullscreen) {
docElm.msRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
}
},
computed:{
fullscreenIcon(){ return this.isInFullScreen()?'fullscreen_exit':'fullscreen'}
},
created(){
console.log("create-----------")
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` is a Vue-specific error info, e.g. which lifecycle hook
console.error(err, vm, info);
this.showAlert("vue error:\n"+err)
alert("vue error");
};
// Add a response interceptor
HTTP.interceptors.response.use(
(response)=> {
// Do something with response data
return response;
},
(error) =>{
// interupt restxq single
if(460 != error.response.status)this.showAlert("http error:\n"+error.response.data)
return Promise.reject(error);
});
HTTP.get("status")
.then(r=>{
console.log("status",r.data)
Object.assign(Auth,r.data)
this.$forceUpdate()
})
},
beforeDestroy(){
console.log("destory-----------")
}
}
</script>

View file

@ -1,5 +1,5 @@
(: entity access maps (: entity access maps
: auto generated from xml files in entities folder at: 2017-08-25T21:59:02.677+01:00 : auto generated from xml files in entities folder at: 2017-08-31T11:04:08.727+01:00
:) :)
module namespace entity = 'quodatum.models.generated'; module namespace entity = 'quodatum.models.generated';
@ -102,20 +102,36 @@ declare variable $entity:list:=map {
"name": "thumbnail", "name": "thumbnail",
"description": "an image.", "description": "an image.",
"access": map{ "access": map{
"geo": function($_ as element()) as xs:boolean {$_/geo },
"height": function($_ as element()) as xs:integer {$_/height },
"id": function($_ as element()) as xs:integer {$_/db:node-id(.) }, "id": function($_ as element()) as xs:integer {$_/db:node-id(.) },
"keywords": function($_ as element()) as xs:integer {$_/0 },
"name": function($_ as element()) as xs:string {$_/file/@name }, "name": function($_ as element()) as xs:string {$_/file/@name },
"path": function($_ as element()) as xs:string {$_/file/@path }, "path": function($_ as element()) as xs:string {$_/file/@path },
"size": function($_ as element()) as xs:integer {$_/0 } }, "size": function($_ as element()) as xs:integer {$_/0 },
"width": function($_ as element()) as xs:integer {$_/width } },
"filter": function($item,$q) as xs:boolean{ "filter": function($item,$q) as xs:boolean{
some $e in ( $item/file/@name) satisfies some $e in ( $item/file/@name) satisfies
fn:contains($e,$q, 'http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive') fn:contains($e,$q, 'http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive')
}, },
"json": map{ "json": map{
"geo": function($_ as element()) as element(geo)? {
(: xs:boolean :)
fn:data($_/geo)!element geo { attribute type {'boolean'}, .}
},
"height": function($_ as element()) as element(height)? {
(: xs:integer :)
fn:data($_/height)!element height { attribute type {'number'}, .}
},
"id": function($_ as element()) as element(id)? { "id": function($_ as element()) as element(id)? {
(: xs:integer :) (: xs:integer :)
fn:data($_/db:node-id(.))!element id { attribute type {'number'}, .} fn:data($_/db:node-id(.))!element id { attribute type {'number'}, .}
}, },
"keywords": function($_ as element()) as element(keywords)? {
(: xs:integer :)
fn:data($_/0)!element keywords { attribute type {'number'}, .}
},
"name": function($_ as element()) as element(name)? { "name": function($_ as element()) as element(name)? {
(: xs:string :) (: xs:string :)
fn:data($_/file/@name)!element name { .} fn:data($_/file/@name)!element name { .}
@ -127,6 +143,10 @@ declare variable $entity:list:=map {
"size": function($_ as element()) as element(size)? { "size": function($_ as element()) as element(size)? {
(: xs:integer :) (: xs:integer :)
fn:data($_/0)!element size { attribute type {'number'}, .} fn:data($_/0)!element size { attribute type {'number'}, .}
},
"width": function($_ as element()) as element(width)? {
(: xs:integer :)
fn:data($_/width)!element width { attribute type {'number'}, .}
} }, } },
"data": function() as element(image)* "data": function() as element(image)*

View file

@ -19,6 +19,22 @@
<field name="size" type="xs:integer"> <field name="size" type="xs:integer">
<description>file size.</description> <description>file size.</description>
<xpath>0</xpath> <xpath>0</xpath>
</field>
<field name="keywords" type="xs:integer">
<description>Number of keywords</description>
<xpath>0</xpath>
</field>
<field name="width" type="xs:integer">
<description>width in pixels</description>
<xpath>width</xpath>
</field>
<field name="height" type="xs:integer">
<description>height in pixels</description>
<xpath>height</xpath>
</field>
<field name="geo" type="xs:boolean">
<description>has geo</description>
<xpath>geo</xpath>
</field> </field>
</fields> </fields>

View file

@ -1,4 +1,14 @@
// generated 2017-08-27T21:21:39.471+01:00 // generated 2017-09-02T22:16:55.583+01:00
Vue.component('qd-fullscreen',{template:`
<a onclick="fullscreen()" href="javascript:void(0);">fS</a>
`,
created:function(){
console.log("qd-fullscreen");
}
}
);
Vue.component('qd-link',{template:` Vue.component('qd-link',{template:`
<a :href="href" :target="href"> {{href}}<v-icon>link</v-icon></a> <a :href="href" :target="href"> {{href}}<v-icon>link</v-icon></a>
`, `,
@ -172,9 +182,14 @@ Vue.filter('round', function(value, decimals) {
} }
); );
const About=Vue.extend({template:` <v-layout class="ma-5"> <v-flex xs4=""> <v-card hover="" raised=""> <v-card-title height="200px" class="pa-5 indigo accent-3"> const About=Vue.extend({template:`
<div class="display-1 white--text text-xs-center">VUE-POC</div> <v-container>
v0.0.3 </v-card-title> </v-card> </v-flex> <v-flex xs4=""> <v-parallax src="/vue-poc/ui/vue-poc.png">
</v-parallax>
<v-card>
<v-card-text>
<p> <p>
This is a experiment in using This is a experiment in using
<code>vue.js</code> <code>vue.js</code>
@ -189,7 +204,11 @@ v0.0.3 </v-card-title> </v-card> </v-flex> <v-flex xs4="">
<li><a href="/dba" target="new">DBA app</a></li> <li><a href="/dba" target="new">DBA app</a></li>
<li> <router-link to="database?url=%2Fvue-poc%2F">DB</router-link></li> <li> <router-link to="database?url=%2Fvue-poc%2F">DB</router-link></li>
</ul> </ul>
</v-flex> <v-btn floating="floating"> <v-icon>add</v-icon> </v-btn> <qd-link href="/dba">REPLACED</qd-link> </v-layout> `, <v-btn floating="floating"> <v-icon>add</v-icon> </v-btn> <qd-link href="/dba">REPLACED</qd-link>
</v-card-text>
</v-card>
</v-container>
`,
} }
@ -520,7 +539,7 @@ v0.0.3 </v-card-title> </v-card> </v-flex> <v-flex xs4="">
</v-btn> </v-btn>
<v-btn icon="" @click="acecmd('foldall')" title="fold all"> <v-btn icon="" @click="togglefold" title="fold toggle">
<v-icon>vertical_align_center</v-icon> <v-icon>vertical_align_center</v-icon>
</v-btn> </v-btn>
@ -614,6 +633,7 @@ v0.0.3 </v-card-title> </v-card> </v-flex> <v-flex xs4="">
snackbar: false, snackbar: false,
message: "Cant do that", message: "Cant do that",
events: new Vue({}), events: new Vue({}),
folded: false, // toggle fold/unfold action
mimemap:{ mimemap:{
"text/xml":"xml", "text/xml":"xml",
"application/xml":"xml", "application/xml":"xml",
@ -666,6 +686,10 @@ v0.0.3 </v-card-title> </v-card> </v-flex> <v-flex xs4="">
}); });
}, },
togglefold(){
this.folded=!this.folded
this.acecmd(this.folded?"foldall":"unfoldall")
},
acecmd(cmd){ acecmd(cmd){
//alert("acecmd: "+cmd) //alert("acecmd: "+cmd)
this.events.$emit('eventFired',cmd); this.events.$emit('eventFired',cmd);
@ -993,20 +1017,61 @@ v0.0.3 </v-card-title> </v-card> </v-flex> <v-flex xs4="">
<v-toolbar class="orange darken-1"> <v-toolbar class="orange darken-1">
<v-btn icon="" to="./"><v-icon>arrow_back</v-icon></v-btn> <v-btn icon="" to="./"><v-icon>arrow_back</v-icon></v-btn>
<v-card-title> <v-card-title>
<v-chip>todo</v-chip> <v-chip>Images by year and month</v-chip>
</v-card-title> </v-card-title>
<v-spacer></v-spacer> <v-spacer></v-spacer>
</v-toolbar> </v-toolbar>
<v-card-text> <v-card-text>
dates todo <v-progress-linear v-if="busy" v-bind:indeterminate="true"></v-progress-linear>
</v-card-text> <v-container v-if="!busy" fluid="">
<v-layout v-for="year in items" :key="year.year">
<v-flex v-text="year.year"></v-flex>
<v-flex v-for="(m,i) in year.months" :key="i"><v-btn icon="" primary="" :disabled="0==m" @click="go(year.year,i)">{{m}}</v-btn></v-flex>
</v-layout>
</v-container>
<v-layout>
</v-layout></v-card-text>
</v-card> </v-card>
</v-container> </v-container>
`, `,
data: ()=>({
busy: false,
total: 0,
items: [],
elapsed: null,
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
}),
methods:{
getDatetaken(){
this.busy=true
var t0 = performance.now();
HTTP.get("images/datetaken")
.then(r=>{
this.busy=false
this.total=r.data.total
this.items=r.data.items
var t1 = performance.now();
this.elapsed= 0.001 *(t1 - t0)
})
},
go(year,month){
month=("0" + (1+month)).slice(-2)
// alert("year: "+year+" "+month)
var from=year + "-" + month + "-01"
var lastday=new Date(year, month, 0).getDate()
var until=year + "-" + month + "-"+ lastday
this.$router.push({ name: 'images', query: { from:from, until:until }})
}
},
created:function(){
console.log("create datetaken")
this.getDatetaken()
}
} }
); );
@ -1079,20 +1144,20 @@ dates todo
<v-chip class="primary white--text">{{ total }} in {{ elapsed | round(2) }} secs </v-chip> <v-chip class="primary white--text">{{ total }} in {{ elapsed | round(2) }} secs </v-chip>
Page:{{ query.page+1 }} Page:{{ query.page+1 }}
<v-btn @click.stop="query.page=Math.min(0,query.page-1)" :disabled="query.page==0" icon="" primary=""> <v-btn @click.stop="pageBack()" :disabled="query.page==0" icon="" primary="">
<v-icon>arrow_back</v-icon> <v-icon>arrow_back</v-icon>
</v-btn> </v-btn>
<v-btn @click.stop="query.page+=1" icon="" primary=""> <v-btn @click.stop="pageNext()" icon="" primary="">
<v-icon>arrow_forward</v-icon> <v-icon>arrow_forward</v-icon>
</v-btn> </v-btn>
</span> </span>
</v-toolbar> </v-toolbar>
<v-progress-linear v-if="busy" v-bind:indeterminate="true"></v-progress-linear> <v-progress-linear v-if="busy" v-bind:indeterminate="true"></v-progress-linear>
<v-container v-if="!busy" fluid="" grid-list-md=""> <v-container v-if="!busy" fluid="" grid-list-md="">
<v-layout row="" wrap=""> <v-layout row="" wrap="" v-touch="{ left: () => pageNext(), right: () => pageBack()}">
<v-flex height="80px" xs2="" v-for="image in images" :key="image.name"> <v-flex height="80px" xs2="" v-for="image in images" :key="image.name">
<v-card class="grey lighten-2 pt-1"> <v-card class="grey lighten-2 pt-1">
<v-card-media :src="src(image)" @click="go(image)" height="80px" contain=""></v-card-media> <v-card-media :src="src(image)" @dblclick="go(image)" height="80px" contain=""></v-card-media>
<v-card-actions v-tooltip:top="{ html: ' '+image.path }"> <v-card-actions v-tooltip:top="{ html: ' '+image.path }">
<v-btn icon="" small=""> <v-btn icon="" small="">
@ -1120,7 +1185,15 @@ dates todo
</v-toolbar> </v-toolbar>
<v-card-text> <v-card-text>
<v-select v-bind:items="keywords" v-model="query.keyword" label="Keyword" autocomplete=""></v-select> <v-select v-bind:items="keywords" v-model="query.keyword" label="Keyword" item-value="text" item-text="text" autocomplete="">
<template slot="item" scope="data">
<v-list-tile-content>
<v-list-tile-title v-html="data.item.text"></v-list-tile-title>
<v-list-tile-sub-title v-html="data.item.count"></v-list-tile-sub-title>
</v-list-tile-content>
</template>
</v-select>
<v-btn @click="query.keyword=null" :disabled="!query.keyword"> <v-btn @click="query.keyword=null" :disabled="!query.keyword">
<v-icon>close</v-icon>Clear keyword <v-icon>close</v-icon>Clear keyword
</v-btn> </v-btn>
@ -1226,6 +1299,12 @@ dates todo
}, },
go(image){ go(image){
this.$router.push({ name: 'image', params: { id: image.id }}) this.$router.push({ name: 'image', params: { id: image.id }})
},
pageBack(){
this.query.page=Math.min(0,this.query.page-1)
},
pageNext(){
this.query.page+=1
} }
}, },
@ -1253,7 +1332,7 @@ dates todo
showFilter(){ showFilter(){
if(this.keywords.length==0){ if(this.keywords.length==0){
HTTP.get("images/keywords") HTTP.get("images/keywords2")
.then(r=>{ .then(r=>{
this.keywords=r.data.items this.keywords=r.data.items
}) })
@ -1305,22 +1384,61 @@ body
<v-toolbar class="orange darken-1"> <v-toolbar class="orange darken-1">
<v-btn icon="" to="./"><v-icon>arrow_back</v-icon></v-btn> <v-btn icon="" to="./"><v-icon>arrow_back</v-icon></v-btn>
<v-card-title> <v-card-title>
<v-chip>todo</v-chip> <v-chip>click to show</v-chip>
</v-card-title> </v-card-title>
<v-spacer></v-spacer> <v-spacer></v-spacer>
</v-toolbar> </v-toolbar>
<v-card-text> <v-card-text>
keywords todo <v-progress-linear v-if="busy" v-bind:indeterminate="true"></v-progress-linear>
<v-container v-if="!busy" fluid="" grid-list-md="">
<v-layout row="" wrap="" v-touch="{ left: () => pageNext(), right: () => pageBack()}">
<v-flex height="80px" xs3="" v-for="keyword in items" :key="keyword.text">
<v-card class="grey lighten-2 pt-1" @click="show(keyword)">
<v-toolbar>
<v-card-title v-text="keyword.text"></v-card-title>
<v-spacer></v-spacer>
<v-chip>{{keyword.count}}</v-chip>
</v-toolbar>
</v-card>
</v-flex>
</v-layout>
</v-container>
</v-card-text> </v-card-text>
</v-card> </v-card>
</v-container> </v-container>
`, `,
data: ()=>({
busy: false,
total: 0,
items: [],
elapsed: null
}),
methods:{
getKeywords(){
this.busy=true
var t0 = performance.now();
HTTP.get("images/keywords2")
.then(r=>{
this.busy=false
this.total=r.data.total
this.items=r.data.items
var t1 = performance.now();
this.elapsed= 0.001 *(t1 - t0)
})
},
show(keyword){
this.$router.push({ name: 'images', query: { keyword: keyword.text }})
} }
},
created:function(){
console.log("create keywords")
this.getKeywords()
}
}
); );
const Job=Vue.extend({template:` const Job=Vue.extend({template:`
<v-card> <v-card>
@ -1705,10 +1823,8 @@ keywords todo
<br> <br>
<table> <table>
<tbody><tr v-for="(item, row) in grid"> <tbody><tr v-for="(item, row) in grid">
<td v-for="(cell,col) in item" style="width:50px;height:50px;"> <td v-for="(cell,col) in item" style="width:50px;height:50px;" @click="click(row,col)">
<v-btn @click="click(row,col)" :disabled="disabled(row,col)">
<img :src="src(row,col)" style="width:50px;height:50px;"> <img :src="src(row,col)" style="width:50px;height:50px;">
</v-btn>
</td> </td>
</tr> </tr>
</tbody></table> </tbody></table>
@ -1730,6 +1846,7 @@ keywords todo
}, },
methods: { methods: {
click(row,col) { click(row,col) {
if(this.disabled(row,col))return;
var g=this.grid var g=this.grid
var h=g[row][col] var h=g[row][col]
g[row][col]=null g[row][col]=null
@ -1933,7 +2050,7 @@ keywords todo
); );
const Settings=Vue.extend({template:` const Acesettings=Vue.extend({template:`
<v-layout row=""> <v-layout row="">
<v-flex xs12="" sm6="" offset-sm3=""> <v-flex xs12="" sm6="" offset-sm3="">
<v-card> <v-card>
@ -2020,7 +2137,7 @@ keywords todo
fontsize: "14px" fontsize: "14px"
}, },
keybindings:[ 'Ace', 'Vim', 'Emacs' ] keybindings:[ 'ace', 'vim', 'emacs', 'textarea', 'sublime' ]
} }
}, },
@ -2043,6 +2160,43 @@ keywords todo
} }
);
const Settings=Vue.extend({template:`
<v-container fluid="">
<v-card>
<v-card-text>
<router-link to="/acesettings">Editor settings</router-link>
</v-card-text>
</v-card>
<v-card>
<v-card-title>System information</v-card-title>
<v-card-text></v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn @click="wipe" error="">Wipe</v-btn></v-card-actions>
</v-card>
</v-container>
`,
data:function(){return {
keys:[]
}
},
methods:{
wipe(){
alert("wipe")
}
},
created:function(){
console.log("settings")
settings.length()
.then(k=>{
console.log("length:",k)
this.keys=k;
})
}
}
); );
const Tabs=Vue.extend({template:` const Tabs=Vue.extend({template:`
<v-tabs id="mobile-tabs-6" scroll-bars="" light=""> <v-tabs id="mobile-tabs-6" scroll-bars="" light="">
@ -2052,6 +2206,7 @@ keywords todo
<v-icon>menu</v-icon> <v-icon>menu</v-icon>
</v-btn> </v-btn>
<v-card-title>Page Title</v-card-title> <v-card-title>Page Title</v-card-title>
<v-spacer></v-spacer>
<v-btn icon="" light=""> <v-btn icon="" light="">
<v-icon>search</v-icon> <v-icon>search</v-icon>
</v-btn> </v-btn>
@ -2060,7 +2215,7 @@ keywords todo
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
<v-tabs-bar slot="activators"> <v-tabs-bar slot="activators" class="green">
<v-tabs-slider></v-tabs-slider> <v-tabs-slider></v-tabs-slider>
<v-tabs-item v-for="i in 13" :key="i" :href="'#mobile-tabs-6-' + i"> <v-tabs-item v-for="i in 13" :key="i" :href="'#mobile-tabs-6-' + i">
Item {{ i }} Item {{ i }}
@ -2318,6 +2473,7 @@ keywords todo
this.waiting=false this.waiting=false
this.alert={msg:error.response.data,success:false,error:true} this.alert={msg:error.response.data,success:false,error:true}
console.log(error); console.log(error);
alert("bad")
}); });
} }
}, },
@ -2451,6 +2607,227 @@ created(){
console.log("timeline") console.log("timeline")
} }
} }
);
const Vuepoc=Vue.extend({template:`
<v-app>
<v-navigation-drawer persistent="" light="" :mini-variant.sync="mini" v-model="drawer" :disable-route-watcher="true" height="100%" class="grey lighten-4 pb-0">
<v-list class="pa-0">
<v-list-tile avatar="" tag="div">
<v-list-tile-avatar>
<v-btn icon="" @click="session">
<img src="/vue-poc/ui/quodatum.gif">
</v-btn>
</v-list-tile-avatar>
<v-list-tile-content>
<v-list-tile-title>Vue PoC</v-list-tile-title>
</v-list-tile-content>
<v-list-tile-action>
<v-btn icon="" @click.stop="mini = !mini">
<v-icon>chevron_left</v-icon>
</v-btn>
</v-list-tile-action>
</v-list-tile>
</v-list>
<qd-navlist :items="items"></qd-navlist>
</v-navigation-drawer>
<v-toolbar class="indigo" dark="">
<v-toolbar-side-icon @click.stop="drawer = !drawer"></v-toolbar-side-icon>
<v-toolbar-title class="hidden-sm-and-down">{{$route.meta.title}}</v-toolbar-title>
<v-spacer></v-spacer>
<v-text-field prepend-icon="search" label="Search..." v-model="q" hide-details="" single-line="" dark="" @keyup.enter="search"></v-text-field>
<v-menu left="" transition="v-fade-transition">
<v-btn dark="" icon="" slot="activator">
{{$auth.user}}
</v-btn>
<v-list>
<v-list-tile @click="logout()">
<v-list-tile-title>logout</v-list-tile-title>
</v-list-tile>
<v-list-tile>
<v-list-tile-title>permission: {{$auth.permission}}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
<v-btn icon="" @click="fullscreen" :disabled="!fullscreenEnabled">
<v-icon>{{ fullscreenIcon }}</v-icon>
</v-btn>
<qd-fullscreen></qd-fullscreen>
</v-toolbar>
<main>
<v-alert error="" value="true" dismissible="" v-model="alert.show">
<pre style="overflow:auto;">{{ alert.msg }}</pre>
</v-alert>
<transition name="fade" mode="out-in">
<router-view class="view ma-3"></router-view>
</transition>
</main>
</v-app>
`,
// router,
data:function(){return {
q:"",
status:{},
drawer:true,
mini: false,
alert:{show:false,msg:"Hello"},
items:[
{href: '/',text: 'Home', icon: 'home' },
{
icon: 'folder_open',
text: 'Collections' ,
model: false,
children: [
{href: '/database', text: 'Databases',icon: 'developer_mode' },
{href: '/files', text: 'File system',icon: 'folder' },
{href: '/edit',text: 'Edit',icon: 'mode_edit'},
{href: '/history',text: 'history',icon: 'history'}
]},
{
icon: 'directions_run',
text: 'Actions' ,
model: false,
children: [
{href: '/eval',text: 'Query',icon: 'play_circle_outline'},
{href: '/tasks',text: 'Tasks',icon: 'history'}
]},
{
icon: 'cast_connected',
text: 'Server' ,
model: false,
children: [
{href: '/jobs',text: 'Running jobs',icon: 'dashboard'},
{href: '/logs',text: 'Server logs',icon: 'dns'},
{href: '/ping',text: 'Ping',icon: 'update'}
]},
{
icon: 'camera_roll',
text: 'Images' ,
model: false,
children: [
{href: '/images/item',text: 'Collection',icon: 'photo_camera'},
{href: '/images/keywords',text: 'Keywords',icon: 'label'},
{href: '/images/dates',text: 'Date taken',icon: 'date_range'},
{href: '/images/thumbnail',text: 'Thumbnail',icon: 'touch_app'},
{href: '/images/report',text: 'Reports',icon: 'report'}
]},
{
icon: 'more_horiz',
text: 'More' ,
model: false,
children: [
{href: '/session',text: 'Session',icon: 'person'},
{href: '/select',text: 'Select',icon: 'extension'},
{href: '/puzzle',text: 'Puzzle',icon: 'extension'},
{href: '/tabs',text: 'Tabs',icon: 'switch_camera'},
{href: '/timeline',text: 'Time line',icon: 'timelapse'}
]},
{href: '/settings',text: 'Settings',icon: 'settings' },
{href: '/about',text: 'About', icon: 'help' },
]
}},
methods: {
session(){
this.$router.push({path: '/session'})
},
search(){
this.$router.push({path: '/search',query: { q: this.q }})
},
logout(){
HTTP.get("logout").then(r=>{
alert("logout")
})
},
showAlert(msg){
this.alert.msg=moment().format()+" "+ msg
this.alert.show=true
},
fullscreenEnabled(){
return document.fullscreenEnabled
},
isInFullScreen(){
return (document.fullscreenElement && document.fullscreenElement !== null) ||
(document.webkitFullscreenElement && document.webkitFullscreenElement !== null) ||
(document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
(document.msFullscreenElement && document.msFullscreenElement !== null)
},
fullscreen(){
// https://stackoverflow.com/questions/36672561/how-to-exit-fullscreen-onclick-using-javascript
var isInFullScreen = this.isInFullScreen();
alert(isInFullScreen);
var docElm = document.documentElement;
if (!isInFullScreen) {
if (docElm.requestFullscreen) {
docElm.requestFullscreen();
} else if (docElm.mozRequestFullScreen) {
docElm.mozRequestFullScreen();
} else if (docElm.webkitRequestFullScreen) {
docElm.webkitRequestFullScreen();
} else if (docElm.msRequestFullscreen) {
docElm.msRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
}
},
computed:{
fullscreenIcon(){ return this.isInFullScreen()?'fullscreen_exit':'fullscreen'}
},
created(){
console.log("create-----------")
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` is a Vue-specific error info, e.g. which lifecycle hook
console.error(err, vm, info);
this.showAlert("vue error:\n"+err)
alert("vue error");
};
// Add a response interceptor
HTTP.interceptors.response.use(
(response)=> {
// Do something with response data
return response;
},
(error) =>{
// interupt restxq single
if(460 != error.response.status)this.showAlert("http error:\n"+error.response.data)
return Promise.reject(error);
});
HTTP.get("status")
.then(r=>{
console.log("status",r.data)
Object.assign(Auth,r.data)
this.$forceUpdate()
})
},
beforeDestroy(){
console.log("destory-----------")
}
}
); );
// base ----------------------- // base -----------------------
localforage.config({ localforage.config({
@ -2492,7 +2869,7 @@ var settings = {
}); });
}); });
}, },
setItem (key,value,callback) { setItem (key,value) {
if (this.debug) console.log('setItem',key,value); if (this.debug) console.log('setItem',key,value);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
localforage.setItem(key, value) localforage.setItem(key, value)
@ -2504,16 +2881,23 @@ var settings = {
return new Promise((resolve, reject) => {reject(err);}) return new Promise((resolve, reject) => {reject(err);})
}); });
}) })
},
length(){
return new Promise((resolve, reject) => {
localforage.keys() // returns array of keys
.then((value) => {
console.log('length ',value);
return new Promise((resolve, reject) => {resolve(value);})
}).catch((err) => {
console.log('length');
return new Promise((resolve, reject) => {reject(err);})
});
})
} }
};
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` is a Vue-specific error info, e.g. which lifecycle hook
console.error(err, vm, info);
alert("vue error");
}; };
//Returns a function, that, as long as it continues to be invoked, will not //Returns a function, that, as long as it continues to be invoked, will not
//be triggered. The function will be called after it stops being called for //be triggered. The function will be called after it stops being called for
//N milliseconds. If `immediate` is passed, trigger the function on the //N milliseconds. If `immediate` is passed, trigger the function on the
@ -2531,19 +2915,50 @@ function debounce(func, wait, immediate) {
}; };
}; };
// https://stackoverflow.com/questions/36672561/how-to-exit-fullscreen-onclick-using-javascript
function fullscreen() {
var isInFullScreen = (document.fullscreenElement && document.fullscreenElement !== null) ||
(document.webkitFullscreenElement && document.webkitFullscreenElement !== null) ||
(document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
(document.msFullscreenElement && document.msFullscreenElement !== null);
var docElm = document.documentElement;
if (!isInFullScreen) {
if (docElm.requestFullscreen) {
docElm.requestFullscreen();
} else if (docElm.mozRequestFullScreen) {
docElm.mozRequestFullScreen();
} else if (docElm.webkitRequestFullScreen) {
docElm.webkitRequestFullScreen();
} else if (docElm.msRequestFullscreen) {
docElm.msRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
};
const router = new VueRouter({ const router = new VueRouter({
base:"/vue-poc/ui/", base:"/vue-poc/ui/",
mode: 'history', mode: 'history',
routes: [ routes: [
{ path: '/', component: Home,meta:{title:"Home"} }, { path: '/', component: Home, meta:{title:"Home"} },
{ path: '/session', component: Session ,meta:{title:"Session"}}, { path: '/session', component: Session ,meta: {title:"Session"}},
{ path: '/images/item', component: Images, meta:{title:"Images"} }, {path: '/images', redirect: '/images/item' },
{ path: '/images/report', name:"image-reports",component: Report, props: true, meta:{title: "Image report"}}, { path: '/images/item', name:'images', component: Images, meta:{title: "Images"} },
{ path: '/images/report', name:"image-reports", component: Report, props: true, meta:{title: "Image report"}},
{ path: '/images/item/:id', name:"image",component: Image, props: true, meta:{title: "Image details"}}, { path: '/images/item/:id', name:"image",component: Image, props: true, meta:{title: "Image details"}},
{ path: '/images/thumbnail', component: Thumbnail, meta:{title:"Thumbnail generator"} }, { path: '/images/thumbnail', component: Thumbnail, meta:{title:"Thumbnail generator"} },
{ path: '/images/keywords', component: Keywords, meta:{title:"Thumbnail keywords"} }, { path: '/images/keywords', component: Keywords, meta:{title:"Image keywords"} },
{ path: '/images/dates', component: Dates, meta:{title:"Thumbnail dates"} }, { path: '/images/dates', component: Dates, meta:{title:"Image dates"} },
{ path: '/select', component: Select, meta:{title:"Select"} }, { path: '/select', component: Select, meta:{title:"Select"} },
{ path: '/search', component: Search, meta:{title:"Search"} }, { path: '/search', component: Search, meta:{title:"Search"} },
{ path: '/tabs', component: Tabs,meta:{title:"tab test",requiresAuth: true} }, { path: '/tabs', component: Tabs,meta:{title:"tab test",requiresAuth: true} },
@ -2554,6 +2969,7 @@ const router = new VueRouter({
{ path: '/database', component: Files,meta:{title:"Databases"},props:{protocol:"basexdb"} }, { path: '/database', component: Files,meta:{title:"Databases"},props:{protocol:"basexdb"} },
{ path: '/ping', component: Ping,meta:{title:"Ping"} }, { path: '/ping', component: Ping,meta:{title:"Ping"} },
{ path: '/settings', component: Settings, meta:{title:"Settings"} }, { path: '/settings', component: Settings, meta:{title:"Settings"} },
{ path: '/acesettings', component: Acesettings, meta:{title:"Editor settings"} },
{ path: '/history', component: History, meta:{title:"File History"} }, { path: '/history', component: History, meta:{title:"File History"} },
{ path: '/puzzle', component: Puzzle, meta:{title:"Jigsaw"} }, { path: '/puzzle', component: Puzzle, meta:{title:"Jigsaw"} },
{ path: '/eval', component: Eval, meta:{title:"Evaluate XQuery"} }, { path: '/eval', component: Eval, meta:{title:"Evaluate XQuery"} },
@ -2591,6 +3007,7 @@ router.beforeEach((to, from, next) => {
}); });
Vue.use(Vuetify); Vue.use(Vuetify);
const app = new Vue({ const app = new Vue({
router, router,
data:function(){return { data:function(){return {
@ -2671,10 +3088,58 @@ const app = new Vue({
showAlert(msg){ showAlert(msg){
this.alert.msg=moment().format()+" "+ msg this.alert.msg=moment().format()+" "+ msg
this.alert.show=true this.alert.show=true
},
fullscreenEnabled(){
return document.fullscreenEnabled
},
isInFullScreen(){
return (document.fullscreenElement && document.fullscreenElement !== null) ||
(document.webkitFullscreenElement && document.webkitFullscreenElement !== null) ||
(document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
(document.msFullscreenElement && document.msFullscreenElement !== null)
},
fullscreen(){
// https://stackoverflow.com/questions/36672561/how-to-exit-fullscreen-onclick-using-javascript
var isInFullScreen = this.isInFullScreen();
alert(isInFullScreen);
var docElm = document.documentElement;
if (!isInFullScreen) {
if (docElm.requestFullscreen) {
docElm.requestFullscreen();
} else if (docElm.mozRequestFullScreen) {
docElm.mozRequestFullScreen();
} else if (docElm.webkitRequestFullScreen) {
docElm.webkitRequestFullScreen();
} else if (docElm.msRequestFullscreen) {
docElm.msRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
} }
}, },
computed:{
fullscreenIcon(){ return this.isInFullScreen()?'fullscreen_exit':'fullscreen'}
},
created(){ created(){
console.log("create-----------") console.log("create-----------")
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` is a Vue-specific error info, e.g. which lifecycle hook
console.error(err, vm, info);
this.showAlert("vue error:\n"+err)
alert("vue error");
};
// Add a response interceptor // Add a response interceptor
HTTP.interceptors.response.use( HTTP.interceptors.response.use(
@ -2683,8 +3148,8 @@ const app = new Vue({
return response; return response;
}, },
(error) =>{ (error) =>{
// Do something with response error // interupt restxq single
this.showAlert("http error:\n"+error.response.data) if(460 != error.response.status)this.showAlert("http error:\n"+error.response.data)
return Promise.reject(error); return Promise.reject(error);
}); });

View file

@ -83,6 +83,10 @@
</v-list> </v-list>
</v-menu> </v-menu>
<v-btn icon @click="fullscreen" :disabled="!fullscreenEnabled">
<v-icon>{{ fullscreenIcon }}</v-icon>
</v-btn>
<qd-fullscreen></qd-fullscreen>
</v-toolbar> </v-toolbar>
<main> <main>
<v-alert error value="true" dismissible v-model="alert.show"> <v-alert error value="true" dismissible v-model="alert.show">

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View file

@ -0,0 +1,200 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1000px"
height="700px"
viewBox="0 0 214.95504 163.40485"
version="1.1"
id="svg8"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="vue-poc.svg">
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient6340">
<stop
style="stop-color:#aae418;stop-opacity:1;"
offset="0"
id="stop6336" />
<stop
style="stop-color:#aae418;stop-opacity:0;"
offset="1"
id="stop6338" />
</linearGradient>
<linearGradient
id="linearGradient6306"
osb:paint="solid">
<stop
style="stop-color:#aae418;stop-opacity:1;"
offset="0"
id="stop6304" />
</linearGradient>
<style
id="style4792">.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient6340"
id="linearGradient6342"
x1="-1.6441965"
y1="90.247025"
x2="213.31087"
y2="90.247025"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0000254,0,0,0.959177,-0.00268836,0.20964985)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.49497475"
inkscape:cx="256.09065"
inkscape:cy="432.63847"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1366"
inkscape:window-height="715"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(1.6441965,-5.0700892)"
style="display:inline">
<g
id="g10"
transform="matrix(0.34330717,-0.00308857,-0.00306839,-0.34556479,4.721666,171.7052)">
<g
id="g12"
transform="translate(178.0626,235.0086)">
<path
d="M 0,0 -22.669,-39.264 -45.338,0 h -75.491 L -22.669,-170.017 75.491,0 Z"
style="fill:#4dba87;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path14"
inkscape:connector-curvature="0" />
</g>
<g
id="g16"
transform="translate(178.0626,235.0086)">
<path
d="M 0,0 -22.669,-39.264 -45.338,0 H -81.565 L -22.669,-102.01 36.227,0 Z"
style="fill:#435466;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path18"
inkscape:connector-curvature="0" />
</g>
</g>
<rect
style="opacity:1;fill:url(#linearGradient6342);fill-opacity:1;fill-rule:nonzero;stroke:#004a00;stroke-width:0.25912979;stroke-opacity:1"
id="rect4844"
width="214.69592"
height="163.14572"
x="-1.5146316"
y="5.1996541"
rx="2.4568441"
ry="2.4568441" />
<g
id="g4816"
transform="matrix(0.26458333,0,0,0.26458333,5.5302908,20.580775)">
<polyline
id="polyline4798"
points="131.67 3 71.55 3 131.67 145.1"
class="cls-1"
style="fill:#1697f6" />
<polyline
id="polyline4800"
points="131.67 186.81 131.67 297.1 3 45.89 70.4 45.89"
class="cls-2"
style="fill:#7bc6ff" />
<polyline
id="polyline4802"
points="131.67 3 191.79 3 131.67 145.1"
class="cls-3"
style="fill:#1867c0" />
<polyline
id="polyline4804"
points="131.67 186.81 131.67 297.1 260.34 45.89 192.94 45.89"
class="cls-4"
style="fill:#aeddff" />
</g>
<g
id="g10-9"
transform="matrix(1.9498076,0,0,-2.4535824,77.384423,332.0942)">
<g
id="g12-4"
transform="scale(0.1)">
<path
inkscape:connector-curvature="0"
d="m 493.23291,1111.6167 c 3.47269,-0.9434 8.24049,-2.2912 12.31024,-4.7334 0,0 61.05511,66.7922 74.71754,80.5117 9.59708,9.6366 20.31366,18.0527 32.04878,25.0463 8.9078,5.3123 18.18878,9.4303 28.91852,9.1185 7.12976,-0.2106 20.70878,-0.1755 27.84731,-0.1844 5.58879,-0.01 13.06098,7.5338 13.06537,13.162 0.0176,16.1517 0.0132,32.3034 0,48.4595 -0.005,5.7337 -7.19122,12.9469 -12.9161,12.9513 H 418.50089 c -5.70732,0 -13.48112,-7.2615 -13.49473,-12.7317 -0.0413,-16.4459 -0.0294,-32.8961 0,-49.342 -0.0207,-6.8576 3.6079,-12.4156 11.59112,-12.4507 l 44.10482,-0.04 c 13.16985,0.07 13.92497,-10.8351 9.06058,-16.279 l -95.97553,-98.7581 -77.37804,94.6664 c -8.29843,11.1117 -2.19511,20.3707 4.93903,20.5771 l 39.32384,-0.08 c 9.07683,0 13.69361,6.3614 13.72785,13.2717 0.009,15.9585 0.007,31.9169 0.002,47.8755 -0.004,6.1376 -7.07926,13.2936 -13.17424,13.2849 -13.01794,-0.014 -26.03765,-0.08 -39.05779,-0.075 -80.17857,0.017 -166.10703,0.3599 -246.287348,0.3863 -6.124037,0 -14.37462,-7.1605 -14.378045,-13.263 -0.0069,-15.5678 0.133771,-31.456 0,-47.0238 0,-8.7234 5.60959,-14.2551 13.856704,-14.2595 8.61414,0 22.976554,-0.3951 31.591089,-0.4434 11.260094,-0.061 21.92707,-2.5859 32.50975,-6.5371 14.83945,-5.5405 22.95789,-14.9005 31.92716,-25.2966 10.53658,-12.2136 85.00081,-105.019 90.98251,-112.4666 l 5.82234,14.3144 c 0,0 -89.29534,111.898 -99.007,120.5073 -9.01185,7.9903 -20.59945,12.6395 -32.17915,16.3318 -11.93752,3.8062 -24.315807,4.7678 -36.764779,4.9697 -4.30595,0.07 -14.364171,0.2985 -18.672097,0.3205 -4.539467,0.017 -7.197627,2.6956 -7.202765,7.2746 -0.0137,12.2356 -0.01198,24.4757 0,36.7113 0.0034,4.7765 2.639283,7.4326 7.394838,7.4238 8.015442,-0.014 21.77973,-0.4127 29.793679,-0.4127 75.578904,-0.01 151.158244,0 226.737164,-0.014 5.87018,0 11.7439,-0.2854 17.60706,-0.1186 4.19664,0.1186 6.68503,-2.8756 6.71224,-6.0585 0.11503,-12.9205 0.0878,-25.8453 0.0224,-38.7659 -0.0189,-3.477 -1.79737,-6.6555 -6.41371,-6.6555 l -35.90252,-0.022 c -14.54093,0 -26.40995,-19.1371 -15.78249,-34.6215 11.09941,-13.917 88.59202,-110.793 88.59202,-110.793 18.58654,19.5757 104.14578,105.5116 107.49992,113.0452 6.65122,8.2712 5.15414,29.9811 -13.82048,32.5757 l -43.18638,-0.1801 c -4.03859,0.049 -6.85975,2.7659 -6.87512,6.7566 -0.0448,12.6308 -0.0396,25.2571 -0.002,37.8879 0.0101,3.9248 2.86902,6.7126 6.82024,6.7653 0,0 166.88103,0.061 239.22345,0 5.42195,0 7.61707,-2.1425 7.61707,-7.5337 0.005,-12.0424 0.009,-24.0848 0,-36.1273 -0.005,-4.8029 -2.58586,-7.3581 -7.33171,-7.4282 -6.54585,-0.1011 -19.55414,0.07 -26.05609,-0.5181 -5.01804,-0.461 -10.08878,-1.4664 -14.87853,-3.0204 -9.16244,-2.9811 -17.3239,-8.0299 -25.16926,-13.5747 -19.06683,-13.4737 -35.04731,-30.2883 -50.86974,-47.239 -13.47805,-14.4378 -50.42633,-54.4298 -52.86731,-57.1549"
style="fill:#4f4c4c;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path14-9" />
<path
inkscape:connector-curvature="0"
d="M 197.92543,904.7538 102.12902,805.18442 C 93.926295,795.97369 84.6642,791.83369 73.870349,791.1273 l -13.133147,-0.062 c -6.271506,-0.45614 -7.423944,-4.68878 -7.425656,-9.64492 -0.0086,-12.13477 -0.0086,-22.55303 0.0017,-34.69135 0.0034,-4.78124 2.608418,-7.42399 7.341627,-7.42913 82.601937,-0.12173 234.101297,-0.0566 239.858227,0.0926 4.21507,0 6.58537,2.59471 6.58537,6.70372 0.0396,12.62708 0.0413,25.2525 0.002,37.88279 0.0443,4.15361 -2.32903,6.7759 -6.58712,6.8018 l -43.41555,0.003 c -20.96825,0 -31.59438,23.0198 -19.94444,39.92575 0,0 76.5992,89.34143 82.80393,96.0348 1.47161,-4.3301 4.284,-12.2378 4.284,-12.2378 L 249.5694,827.15258 c -11.40936,-13.45873 -0.10317,-24.96599 9.77181,-24.96599 l 46.66828,0.0619 c 6.6354,0.14049 12.85858,-5.63707 12.85858,-12.67507 0.0536,-16.64034 0.0979,-32.85667 -0.0891,-49.49674 0,-6.54931 -5.93165,-12.48474 -12.39891,-12.48474 0,0 -241.394685,-0.0172 -251.574561,-0.0274 -7.362217,0 -13.117562,4.94418 -13.117562,13.36107 -0.01198,16.05353 -0.123454,32.0435 -0.114893,48.09906 0.0034,5.94395 4.702345,12.2839 13.042094,13.13122 10.269041,0.0927 -2.188272,-0.14093 8.077389,0.0927 19.591545,-0.91756 24.557787,3.78834 36.785491,16.80453 l 83.114322,85.70061 h 15.33336"
style="fill:#4f4c4c;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path16" />
<path
inkscape:connector-curvature="0"
d="m 371.78607,904.7538 h 13.52721 l 67.4776,-80.35329 c 9.65327,-12.3976 5.40219,-34.0055 -17.4442,-33.91814 l -47.87252,0.15762 c -3.33877,-0.13171 -5.87721,-1.43518 -5.88907,-6.77064 0.079,1.23498 0.11503,-38.19494 0.11503,-38.19494 0.0101,-4.35077 1.93258,-6.99694 6.32809,-7.0055 l 275.62514,0.21095 c 4.3244,-0.0138 6.69513,2.15394 6.69951,6.94892 0.009,12.1435 -0.0395,25.1873 -0.0483,37.32584 0,5.59405 -1.47073,7.65175 -6.18145,7.72726 -14.14976,0.0259 -29.73512,-0.0755 -35.03415,1.25035 -14.27706,3.75409 -18.72438,7.98629 -27.92633,19.47116 0,0 -87.83998,105.84963 -88.5029,106.71715 4.10926,2.05112 6.82244,4.2515 10.22927,7.31765 3.06438,-3.76594 88.09899,-107.89328 88.09899,-107.89328 8.98683,-10.28986 18.00878,-15.23722 32.9356,-15.51864 h 25.41512 c 9.87366,-0.009 12.74488,-5.45707 12.75804,-15.59414 0.0439,-16.54212 0.079,-30.85498 -0.0526,-47.39728 0.005,-7.35023 -4.61853,-12.06975 -11.66926,-12.0646 0,0 -278.86383,0.0617 -286.69427,0.0652 -9.30863,0 -14.3214,4.59261 -14.3214,14.14653 -0.0118,16.15131 -0.0136,34.08097 0,50.23223 0,6.28683 5.02507,10.67707 12.16229,10.61209 l 46.91939,0.0342 c 16.91253,0.26737 17.4442,9.47502 10.00493,20.0116 l -66.65969,82.48169"
style="fill:#4f4c4c;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path18-1" />
<g
id="g20"
transform="matrix(4.3902427,0,0,4.3902427,23.248656,727.17025)">
<g
id="text22"
style="font-variant:normal;font-weight:600;font-size:70.24369812px;font-family:'Metric Semibold';-inkscape-font-specification:Metric-Semibold;writing-mode:lr-tb;fill:#aa1818;fill-opacity:1;fill-rule:nonzero;stroke:none"
transform="matrix(1,0,0,-1,0,42.7367)">
<path
inkscape:connector-curvature="0"
id="path3029"
d="m 29.501973,-22.266966 c 1.619943,-0.582407 2.963334,-1.665316 4.030179,-3.24873 1.066778,-1.583363 1.61994,-3.544305 1.659488,-5.882833 -0.04394,-3.869173 -1.343428,-6.7901 -3.898476,-8.762789 -2.555111,-1.97261 -6.102368,-2.961861 -10.641783,-2.967757 H 4.1443248 V 0 H 22.056237 c 4.536488,-0.01317042 8.072038,-1.0580311 10.606662,-3.1345852 2.534559,-2.0765478 3.822342,-5.1057651 3.863354,-9.0876608 -0.03808,-2.394095 -0.699538,-4.48967 -1.98436,-6.286731 -1.284891,-1.797024 -2.964863,-3.049686 -5.03992,-3.757989 z m -16.717785,-3.160925 v -10.114963 h 7.305251 c 2.146772,-0.0073 3.771134,0.393688 4.873092,1.202909 1.101909,0.809289 1.655071,2.070731 1.659488,3.78433 -0.0044,1.719514 -0.557579,3.00437 -1.659487,3.854572 -1.101957,0.850256 -2.72632,1.27464 -4.873093,1.273152 z m 0,7.024279 h 8.499379 c 2.113113,0.01465 3.716988,0.494643 4.81163,1.439976 1.094589,0.945367 1.644824,2.268272 1.650707,3.968719 -0.0059,1.669741 -0.556116,2.983866 -1.650707,3.9423764 -1.094643,0.958529 -2.698518,1.4473014 -4.81163,1.4663189 h -8.499379 z" />
<path
inkscape:connector-curvature="0"
id="path3031"
d="M 55.112494,-43.129075 38.605438,0 h 8.991077 l 3.793111,-10.395933 H 67.545469 L 71.408823,0 h 9.131563 L 64.033329,-43.129075 Z m 4.355054,10.81739 5.268209,14.32953 H 54.199338 Z" />
<path
inkscape:connector-curvature="0"
id="path3033"
d="m 84.087721,-2.9501973 c 1.57168,1.0126685 3.556037,1.8672879 5.953078,2.56386089 2.397024,0.69657371 5.048686,1.0594948 7.954996,1.08876435 4.676975,-0.001463 8.546175,-1.06973824 11.607625,-3.20482804 3.06138,-2.1350847 4.64769,-5.3282017 4.75895,-9.5793609 0.0131,-3.175544 -0.83856,-5.797939 -2.55509,-7.867192 -1.71658,-2.069214 -4.37703,-3.70821 -7.98133,-4.916996 l -6.743312,-2.318012 c -1.520479,-0.459478 -2.694118,-1.033127 -3.52092,-1.72095 -0.826827,-0.687764 -1.245357,-1.682869 -1.25559,-2.985317 0.04243,-1.469214 0.695101,-2.552123 1.958019,-3.248732 1.262892,-0.696538 2.881401,-1.041897 4.855532,-1.036079 2.481891,0.01613 4.770631,0.422956 6.866231,1.220468 2.09555,0.797583 4.0682,1.889272 5.91796,3.275071 v -8.780349 c -1.75171,-1.062382 -3.70973,-1.887735 -5.87406,-2.476061 -2.16437,-0.58824 -4.56141,-0.886771 -7.191102,-0.895593 -4.325797,0.01468 -7.931591,1.074172 -10.81739,3.178486 -2.885813,2.104395 -4.384325,5.165807 -4.495539,9.184245 0.04244,3.526801 1.0112,6.25456 2.906295,8.183285 1.895085,1.928771 4.461872,3.392162 7.700367,4.390175 l 6.321849,2.107284 c 1.62435,0.557567 2.87115,1.198531 3.74043,1.922895 0.86923,0.724394 1.30825,1.769255 1.31705,3.134586 -0.0468,1.671203 -0.77854,2.8770362 -2.19508,3.6175033 -1.41659,0.7404833 -3.23705,1.1034044 -5.461381,1.0887643 -2.514122,-0.019017 -4.949203,-0.455107 -7.305251,-1.3082725 -2.356066,-0.8531489 -4.510176,-2.0092271 -6.462337,-3.4682371 z" />
<path
inkscape:connector-curvature="0"
id="path3035"
d="M 120.78993,-43.129075 V 0 h 29.29125 V -7.9374357 H 129.64052 V -18.192884 h 18.5441 v -7.656464 h -18.5441 v -9.412534 h 20.44066 v -7.867193 z" />
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -31,24 +31,6 @@ function vue-api:test-select($q )
</json> </json>
}; };
(:~
: get status
:)
declare
%rest:GET %rest:path("/vue-poc/api/status")
%rest:produces("application/json")
%output:method("json")
function vue-api:status( )
{
let $user:=user:current()
let $detail:=user:list-details($user)
return <json type="object" >
<user>{$user}</user>
<permission>{$detail/@permission/string()}</permission>
<session>{session:id()}</session>
<created>{session:created()}</created>
</json>
};