This commit is contained in:
Andy Bunce 2017-09-08 11:36:47 +01:00
parent 0e56f4ff44
commit 2c253b93e8
19 changed files with 670 additions and 898 deletions

View file

@ -1,12 +0,0 @@
let $head:="<head/>"
let $app:="<v-app/>"
return ``[
<!DOCTYPE html>
<html>
`{$head}`
<body>
`{$app}`
<script src="/vue-poc/ui/app.js"></script>
</body>
</html>
]``

View file

@ -2,29 +2,21 @@
localforage.config({ localforage.config({
name: 'vuepoc' name: 'vuepoc'
}); });
const AXIOS_CONFIG={
baseURL: "/vue-poc/api/",
headers: {
'X-Custom-Header': 'vue-poc',
accept: 'application/json'
},
paramsSerializer: function(params) {
return Qs.stringify(params)
}
};
// errors displayed by interceptor // errors displayed by interceptor
const HTTP = axios.create({ const HTTP = axios.create(AXIOS_CONFIG);
baseURL: "/vue-poc/api/",
headers: {
'X-Custom-Header': 'vue-poc',
accept: 'application/json'
},
paramsSerializer: function(params) {
return Qs.stringify(params)
}
});
// errors hidden // errors hidden
const HTTPNE = axios.create({ const HTTPNE = axios.create(AXIOS_CONFIG);
baseURL: "/vue-poc/api/",
headers: {
'X-Custom-Header': 'vue-poc',
accept: 'application/json'
},
paramsSerializer: function(params) {
return Qs.stringify(params)
}
});
const axios_json={ headers: {accept: 'application/json'}}; const axios_json={ headers: {accept: 'application/json'}};
const Auth={ const Auth={
@ -40,13 +32,13 @@ Vue.use(Auth);
// read and write settings // read and write settings
// https://vuejs.org/v2/guide/state-management.html // https://vuejs.org/v2/guide/state-management.html
var settings = { var settings = {
debug: true, debug: false,
getItem (key) { getItem (key) {
if (this.debug) console.log('getItem',key); if (this.debug) console.log('getItem',key);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
localforage.getItem(key) localforage.getItem(key)
.then((value) => { .then((value) => {
console.log('GET setting', key,value); //console.log('GET setting', key,value);
resolve(value) resolve(value)
}).catch((err) => { }).catch((err) => {
console.log('GET failed'); console.log('GET failed');
@ -59,7 +51,7 @@ var settings = {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
localforage.setItem(key, value) localforage.setItem(key, value)
.then((value) => { .then((value) => {
console.log('SET ',key, value); //console.log('SET ',key, value);
return new Promise((resolve, reject) => {resolve(value);}) return new Promise((resolve, reject) => {resolve(value);})
}).catch((err) => { }).catch((err) => {
console.log('set failed'); console.log('set failed');
@ -133,194 +125,5 @@ const Fullscreen={
}) } }) }
}; };
Vue.use(Fullscreen); Vue.use(Fullscreen);
const router = new VueRouter({
base:"/vue-poc/ui/",
mode: 'history',
routes: [
{ path: '/', component: Home, meta:{title:"Home"} },
{ path: '/session', component: Session ,meta: {title:"Session"}},
{path: '/images', redirect: '/images/item' },
{ 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/thumbnail', component: Thumbnail, meta:{title:"Thumbnail generator"} },
{ path: '/images/keywords', component: Keywords, meta:{title:"Image keywords"} },
{ path: '/images/dates', component: Dates, meta:{title:"Image dates"} },
{ path: '/select', component: Select, meta:{title:"Select"} },
{ path: '/search', component: Search, meta:{title:"Search"} },
{ path: '/tabs', component: Tabs,meta:{title:"tab test",requiresAuth: true} },
{ path: '/login', component: Login,meta:{title:"login"} },
{ path: '/edit', component: Edit,meta:{title:"Ace editor"} },
{ path: '/server/users', component: Users,meta:{title:"Users"} },
{ path: '/server/repo', component: Repo,meta:{title:"Repository"} },
{ path: '/files', component: Files,meta:{title:"File system"},props:{protocol:"webfile"} },
{ path: '/database', component: Files,meta:{title:"Databases"},props:{protocol:"basexdb"} },
{ path: '/ping', component: Ping,meta:{title:"Ping"} },
{ path: '/settings', component: Settings, meta:{title:"Settings"} },
{ path: '/acesettings', component: Acesettings, meta:{title:"Editor settings"} },
{ path: '/history', component: History, meta:{title:"File History"} },
{ path: '/puzzle', component: Puzzle, meta:{title:"Jigsaw"} },
{ path: '/eval', component: Eval, meta:{title:"Evaluate XQuery"} },
{ path: '/logs', component: Log, meta:{title:"Server logs"} },
{ path: '/tasks', component: Task, meta:{title:"Runnable tasks"} },
{ path: '/tasks/model', component: Model, meta:{title:"build model"} },
{ path: '/tasks/xqdoc', component: Xqdoc, meta:{title:"build xqdoc"} },
{ path: '/tasks/vuecompile', component: Vuecompile, meta:{title:"vue compile"} },
{ path: '/jobs', component: Jobs, meta:{title:"Jobs running"} },
{ path: '/jobs/:job', name:"jobShow", component: Job, props: true, meta:{title:"Job Status"} },
{ path: '/timeline', component: Timeline,meta:{title:"timeline"} },
{ path: '/about', component: About,meta:{title:"About Vue-poc"} },
{ path: '*', component: Notfound,meta:{title:"Page not found"} }
],
});
router.afterEach(function(route) {
document.title = (route.meta.title?route.meta.title:"") + " VUE-Poc";
});
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if ("admin"==Auth.permission) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // make sure to always call next()!
}
});
Vue.use(Vuetify); Vue.use(Vuetify);
new Vuepoc().$mount('#app')
const app = new Vue({
router,
data:function(){return {
q:"",
status:{},
drawer:true,
mini: false,
dark: 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: '/server/users',text: 'Users',icon: 'supervisor_account'},
{href: '/server/repo',text: 'Server code repository',icon: 'local_library'},
{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
},
onDark(dark){
this.dark=dark
alert("theme")
}
},
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
console.log("$$$$$$$$$$$",error)
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-----------")
}
}).$mount('#app');

View file

@ -0,0 +1,60 @@
const router = new VueRouter({
base:"/vue-poc/ui/",
mode: 'history',
routes: [
{ path: '/', component: Home, meta:{title:"Home"} },
{ path: '/session', component: Session ,meta: {title:"Session"}},
{path: '/images', redirect: '/images/item' },
{ 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/thumbnail', component: Thumbnail, meta:{title:"Thumbnail generator"} },
{ path: '/images/keywords', component: Keywords, meta:{title:"Image keywords"} },
{ path: '/images/dates', component: Dates, meta:{title:"Image dates"} },
{ path: '/select', component: Select, meta:{title:"Select"} },
{ path: '/search', component: Search, meta:{title:"Search"} },
{ path: '/tabs', component: Tabs,meta:{title:"tab test",requiresAuth: true} },
{ path: '/login', component: Login,meta:{title:"login"} },
{ path: '/edit', component: Edit,meta:{title:"Ace editor"} },
{ path: '/server/users', component: Users,meta:{title:"Users"} },
{ path: '/server/repo', component: Repo,meta:{title:"Repository"} },
{ path: '/files', component: Files,meta:{title:"File system"},props:{protocol:"webfile"} },
{ path: '/database', component: Files,meta:{title:"Databases"},props:{protocol:"basexdb"} },
{ path: '/ping', component: Ping,meta:{title:"Ping"} },
{ path: '/settings', component: Settings, meta:{title:"Settings"} },
{ path: '/acesettings', component: Acesettings, meta:{title:"Editor settings"} },
{ path: '/history', component: History, meta:{title:"File History"} },
{ path: '/puzzle', component: Puzzle, meta:{title:"Jigsaw"} },
{ path: '/eval', component: Eval, meta:{title:"Evaluate XQuery"} },
{ path: '/logs', component: Log, meta:{title:"Server logs"} },
{ path: '/tasks', component: Task, meta:{title:"Runnable tasks"} },
{ path: '/tasks/model', component: Model, meta:{title:"build model"} },
{ path: '/tasks/xqdoc', component: Xqdoc, meta:{title:"build xqdoc"} },
{ path: '/tasks/vuecompile', component: Vuecompile, meta:{title:"vue compile"} },
{ path: '/jobs', component: Jobs, meta:{title:"Jobs running"} },
{ path: '/jobs/:job', name:"jobShow", component: Job, props: true, meta:{title:"Job Status"} },
{ path: '/timeline', component: Timeline,meta:{title:"timeline"} },
{ path: '/about', component: About,meta:{title:"About Vue-poc"} },
{ path: '*', component: Notfound,meta:{title:"Page not found"} }
],
});
router.afterEach(function(route) {
document.title = (route.meta.title?route.meta.title:"") + " VUE-Poc";
});
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if ("admin"==Auth.permission) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // make sure to always call next()!
}
});

View file

@ -0,0 +1,3 @@
<state>
<last-id>3</last-id>
</state>

View file

@ -1,3 +1,4 @@
<state> <state>
<ping>0</ping>
<last-id>3</last-id> <last-id>3</last-id>
</state> </state>

View file

@ -4,10 +4,26 @@
<v-card > <v-card >
<v-toolbar> <v-toolbar>
<v-btn @click="run()">Run</v-btn>
<v-btn @click="submit()">
<v-icon>play_circle_outline</v-icon> <v-menu offset-y>
Submit</v-btn> <v-btn slot="activator">
<v-icon>play_circle_outline</v-icon>
Run</v-btn>
<v-list>
<v-list-tile @click="submit">
<v-list-tile-title>Submit</v-list-tile-title>
</v-list-tile>
<v-divider></v-divider>
<v-list-tile @click="run">
<v-list-tile-title>Run</v-list-tile-title>
</v-list-tile>
<v-list-tile @click="plan">
<v-list-tile-title>Show query plan</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn @click="imports"> <v-btn @click="imports">
<v-icon>library_books</v-icon> <v-icon>library_books</v-icon>
@ -167,7 +183,7 @@
}, },
plan(){ plan(){
this.awaitResult(false) this.awaitResult(false)
HTTP.post("eval/plan",Qs.stringify({xq:this.xq})) HTTPNE.post("eval/plan",Qs.stringify({xq:this.xq}))
.then(r=>{ .then(r=>{
this.result=r.data.result this.result=r.data.result
}) })

View file

@ -1,5 +1,5 @@
module namespace ping = 'quodatum.test.ping'; module namespace ping = 'quodatum.test.ping';
declare variable $ping:db as xs:string:="doc-doc"; declare variable $ping:db as xs:string:="vue-poc";
declare variable $ping:state as element(state):=db:open($ping:db,"/state.xml")/state; declare variable $ping:state as element(state):=db:open($ping:db,"/state.xml")/state;
(:~ (:~
@ -10,8 +10,8 @@ declare %updating
%output:method("text") %output:method("text")
function ping:dopost() function ping:dopost()
{ {
(replace value of node $ping:state/hits with 1+$ping:state/hits, (replace value of node $ping:state/ping with 1+$ping:state/ping,
db:output(1+$ping:state/hits)) db:output(1+$ping:state/ping))
}; };
(:~ (:~
@ -22,5 +22,5 @@ declare
%rest:GET %rest:path("/vue-poc/api/ping") %rest:GET %rest:path("/vue-poc/api/ping")
function ping:dostate() function ping:dostate()
{ {
$ping:state/hits $ping:state/ping
}; };

View file

@ -1,224 +0,0 @@
<!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

@ -64,12 +64,14 @@ declare function vue:capitalize-first
concat(upper-case(substring($arg,1,1)), substring($arg,2)) concat(upper-case(substring($arg,1,1)), substring($arg,2))
}; };
(: filename of features:)
declare function vue:feature-files($proj) declare function vue:feature-files($proj)
as xs:string* as xs:string*
{ {
let $FEATURES:="features/"=>file:resolve-path($proj) let $FEATURES:="features/"=>file:resolve-path($proj)
return fw:directory-list($FEATURES,map{"include-filter":".*\.vue"}) let $files:= fw:directory-list($FEATURES,map{"include-filter":".*\.vue"})
//c:file/@name/resolve-uri(.,base-uri(.)) //c:file/@name/resolve-uri(.,base-uri(.))
return $files
}; };
declare function vue:feature-build($url as xs:string,$isComp as xs:boolean) declare function vue:feature-build($url as xs:string,$isComp as xs:boolean)
@ -88,8 +90,9 @@ let $FEATURES:="features/"=>file:resolve-path($proj=>trace("proj:"))
let $COMPONENTS:="components/"=>file:resolve-path($proj) let $COMPONENTS:="components/"=>file:resolve-path($proj)
let $CORE:="components/core.js"=>file:resolve-path($proj) let $CORE:="components/core.js"=>file:resolve-path($proj)
let $FILTERS:="components/filters.js"=>file:resolve-path($proj) let $FILTERS:="components/filters.js"=>file:resolve-path($proj)
let $ROUTER:="components/router.js"=>file:resolve-path($proj)
let $DEST:="static/app-gen.js"=>file:resolve-path($proj) let $DEST:="static/app-gen.js"=>file:resolve-path($proj)
let $APP:="vue-poc.vue"=>file:resolve-path($proj)
let $files:=vue:feature-files($proj) let $files:=vue:feature-files($proj)
let $feats:=$files!vue:feature-build(.,false()) let $feats:=$files!vue:feature-build(.,false())
@ -102,5 +105,7 @@ return file:write-text($DEST,string-join(($comment,
$comps, $comps,
fetch:text($FILTERS), fetch:text($FILTERS),
$feats, $feats,
fetch:text($ROUTER),
$APP!vue:feature-build(.,false()),
fetch:text($CORE)))) fetch:text($CORE))))
}; };

View file

@ -0,0 +1,33 @@
<entity name="query" xmlns="https://github.com/Quodatum/app-doc/entity">
<description>An replx query</description>
<namespace prefix="xqdoc" uri="http://www.xqdoc.org/1.0"/>
<fields>
<field name="id" type="xs:string">
<description>unique id</description>
<xpath>@id</xpath>
</field>
<field name="created" type="xs:string">
<description>date</description>
<xpath>created</xpath>
</field>
<field name="query" type="xs:string">
<description>query</description>
<xpath>query</xpath>
</field>
<field name="result" type="xs:string">
<description>result</description>
<xpath>substring(result,0,1000)</xpath>
</field>
<field name="resultlength" type="xs:integer">
<description>result</description>
<xpath>string-length(result)</xpath>
</field>
</fields>
<views>
<view name="filter">name description</view>
</views>
<iconclass>fa fa-file-code-o</iconclass>
<data type="element(query)">collection("replx/queries")/query
</data>
</entity>

View file

@ -0,0 +1,23 @@
<entity name="search-result" xmlns="https://github.com/Quodatum/app-doc/entity">
<description>About a search result.</description>
<fields>
<field name="title" type="xs:string">
<description>title</description>
<xpath>title</xpath>
</field>
<field name="type" type="xs:string">
<description>type</description>
<xpath>type</xpath>
</field>
<field name="uri" type="xs:string">
<description>link to result</description>
<xpath>uri</xpath>
</field>
<field name="sref" type="xs:string">
<description>ui-router ui-sref data</description>
<xpath>"app.item.index({'name':'benchx'})"</xpath>
</field>
</fields>
<iconclass>fa fa-question-circle</iconclass>
<data type="element(search)"></data>
</entity>

View file

@ -1,4 +1,4 @@
// generated 2017-09-05T15:21:42.951+01:00 // generated 2017-09-07T22:40:46.775+01:00
Vue.component('qd-fullscreen',{template:` Vue.component('qd-fullscreen',{template:`
<a @click="toggle()" href="javascript:void(0);" title="Fullscreen toggle"> <a @click="toggle()" href="javascript:void(0);" title="Fullscreen toggle">
<v-icon>{{ fullscreenIcon }}</v-icon> <v-icon>{{ fullscreenIcon }}</v-icon>
@ -936,10 +936,26 @@ Vue.filter('round', function(value, decimals) {
<v-card> <v-card>
<v-toolbar> <v-toolbar>
<v-btn @click="run()">Run</v-btn>
<v-btn @click="submit()">
<v-icon>play_circle_outline</v-icon> <v-menu offset-y="">
Submit</v-btn> <v-btn slot="activator">
<v-icon>play_circle_outline</v-icon>
Run</v-btn>
<v-list>
<v-list-tile @click="submit">
<v-list-tile-title>Submit</v-list-tile-title>
</v-list-tile>
<v-divider></v-divider>
<v-list-tile @click="run">
<v-list-tile-title>Run</v-list-tile-title>
</v-list-tile>
<v-list-tile @click="plan">
<v-list-tile-title>Show query plan</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn @click="imports"> <v-btn @click="imports">
<v-icon>library_books</v-icon> <v-icon>library_books</v-icon>
@ -1095,7 +1111,7 @@ Vue.filter('round', function(value, decimals) {
}, },
plan(){ plan(){
this.awaitResult(false) this.awaitResult(false)
HTTP.post("eval/plan",Qs.stringify({xq:this.xq})) HTTPNE.post("eval/plan",Qs.stringify({xq:this.xq}))
.then(r=>{ .then(r=>{
this.result=r.data.result this.result=r.data.result
}) })
@ -2849,365 +2865,7 @@ users todo
} }
); );
const Vuepoc=Vue.extend({template:` const router = new VueRouter({
<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 -----------------------
localforage.config({
name: 'vuepoc'
});
// errors displayed by interceptor
const HTTP = axios.create({
baseURL: "/vue-poc/api/",
headers: {
'X-Custom-Header': 'vue-poc',
accept: 'application/json'
},
paramsSerializer: function(params) {
return Qs.stringify(params)
}
});
// errors hidden
const HTTPNE = axios.create({
baseURL: "/vue-poc/api/",
headers: {
'X-Custom-Header': 'vue-poc',
accept: 'application/json'
},
paramsSerializer: function(params) {
return Qs.stringify(params)
}
});
const axios_json={ headers: {accept: 'application/json'}};
const Auth={
user:"guest",
permission:null,
install: function(Vue){
Object.defineProperty(Vue.prototype, '$auth', {
get () { return Auth }
}) }
};
Vue.use(Auth);
// read and write settings
// https://vuejs.org/v2/guide/state-management.html
var settings = {
debug: true,
getItem (key) {
if (this.debug) console.log('getItem',key);
return new Promise((resolve, reject) => {
localforage.getItem(key)
.then((value) => {
console.log('GET setting', key,value);
resolve(value)
}).catch((err) => {
console.log('GET failed');
reject(err)
});
});
},
setItem (key,value) {
if (this.debug) console.log('setItem',key,value);
return new Promise((resolve, reject) => {
localforage.setItem(key, value)
.then((value) => {
console.log('SET ',key, value);
return new Promise((resolve, reject) => {resolve(value);})
}).catch((err) => {
console.log('set failed');
return new Promise((resolve, reject) => {reject(err);})
});
})
},
keys(){
return localforage.keys() // returns array of keys
},
clear(){
localforage.clear()
}
};
//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
//N milliseconds. If `immediate` is passed, trigger the function on the
//leading edge, instead of the trailing. https://gist.github.com/nmsdvid/8807205
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
};
// https://stackoverflow.com/questions/36672561/how-to-exit-fullscreen-onclick-using-javascript
const Fullscreen={
isInFullScreen(){
return (document.fullscreenElement && document.fullscreenElement !== null) ||
(document.webkitFullscreenElement && document.webkitFullscreenElement !== null) ||
(document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
(document.msFullscreenElement && document.msFullscreenElement !== null);
},
toggle(){
var docElm = document.documentElement;
if (!this.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();
}
}
},
install: function(Vue){
Object.defineProperty(Vue.prototype, '$fullscreen', {
get () { return Fullscreen }
}) }
};
Vue.use(Fullscreen);
const router = new VueRouter({
base:"/vue-poc/ui/", base:"/vue-poc/ui/",
mode: 'history', mode: 'history',
routes: [ routes: [
@ -3266,11 +2924,65 @@ router.beforeEach((to, from, next) => {
} else { } else {
next() // make sure to always call next()! next() // make sure to always call next()!
} }
}); });const Vuepoc=Vue.extend({template:`
<v-app id="app" :dark="dark" @theme="onDark">
<v-navigation-drawer persistent="" :mini-variant.sync="mini" v-model="drawer" :disable-route-watcher="true" class="grey lighten-4 pb-0">
<v-list class="pa-0">
Vue.use(Vuetify); <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>
<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>
`,
const app = new Vue({
router, router,
data:function(){return { data:function(){return {
q:"", q:"",
@ -3395,4 +3107,137 @@ const app = new Vue({
console.log("destory-----------") console.log("destory-----------")
} }
}).$mount('#app'); }
);
// base -----------------------
localforage.config({
name: 'vuepoc'
});
const AXIOS_CONFIG={
baseURL: "/vue-poc/api/",
headers: {
'X-Custom-Header': 'vue-poc',
accept: 'application/json'
},
paramsSerializer: function(params) {
return Qs.stringify(params)
}
};
// errors displayed by interceptor
const HTTP = axios.create(AXIOS_CONFIG);
// errors hidden
const HTTPNE = axios.create(AXIOS_CONFIG);
const axios_json={ headers: {accept: 'application/json'}};
const Auth={
user:"guest",
permission:null,
install: function(Vue){
Object.defineProperty(Vue.prototype, '$auth', {
get () { return Auth }
}) }
};
Vue.use(Auth);
// read and write settings
// https://vuejs.org/v2/guide/state-management.html
var settings = {
debug: true,
getItem (key) {
if (this.debug) console.log('getItem',key);
return new Promise((resolve, reject) => {
localforage.getItem(key)
.then((value) => {
console.log('GET setting', key,value);
resolve(value)
}).catch((err) => {
console.log('GET failed');
reject(err)
});
});
},
setItem (key,value) {
if (this.debug) console.log('setItem',key,value);
return new Promise((resolve, reject) => {
localforage.setItem(key, value)
.then((value) => {
console.log('SET ',key, value);
return new Promise((resolve, reject) => {resolve(value);})
}).catch((err) => {
console.log('set failed');
return new Promise((resolve, reject) => {reject(err);})
});
})
},
keys(){
return localforage.keys() // returns array of keys
},
clear(){
localforage.clear()
}
};
//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
//N milliseconds. If `immediate` is passed, trigger the function on the
//leading edge, instead of the trailing. https://gist.github.com/nmsdvid/8807205
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
};
// https://stackoverflow.com/questions/36672561/how-to-exit-fullscreen-onclick-using-javascript
const Fullscreen={
isInFullScreen(){
return (document.fullscreenElement && document.fullscreenElement !== null) ||
(document.webkitFullscreenElement && document.webkitFullscreenElement !== null) ||
(document.mozFullScreenElement && document.mozFullScreenElement !== null) ||
(document.msFullscreenElement && document.msFullscreenElement !== null);
},
toggle(){
var docElm = document.documentElement;
if (!this.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();
}
}
},
install: function(Vue){
Object.defineProperty(Vue.prototype, '$fullscreen', {
get () { return Fullscreen }
}) }
};
Vue.use(Fullscreen);
Vue.use(Vuetify);
new Vuepoc().$mount('#app')

View file

@ -1,6 +1,57 @@
/* app.css */ /* app.css */
body {
overflow-y:hidden!; /* http://tobiasahlin.com/spinkit/ */
.spinner {
margin: 100px auto;
width: 50px;
height: 40px;
text-align: center;
font-size: 10px;
}
.spinner > div {
background-color: #0000a0;
height: 100%;
width: 6px;
display: inline-block;
-webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out;
animation: sk-stretchdelay 1.2s infinite ease-in-out;
}
.spinner .rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.spinner .rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
.spinner .rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
.spinner .rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
@-webkit-keyframes sk-stretchdelay {
0%, 40%, 100% { -webkit-transform: scaleY(0.4) }
20% { -webkit-transform: scaleY(1.0) }
}
@keyframes sk-stretchdelay {
0%, 40%, 100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
} 20% {
transform: scaleY(1.0);
-webkit-transform: scaleY(1.0);
}
} }
.fade-enter-active, .fade-leave-active { .fade-enter-active, .fade-leave-active {
transition-property: opacity; transition-property: opacity;

View file

@ -15,6 +15,22 @@
<link href="/vue-poc/ui/app.css" rel="stylesheet" type="text/css"> <link href="/vue-poc/ui/app.css" rel="stylesheet" type="text/css">
<link rel="shortcut icon" href="/vue-poc/ui/icon.png"/> <link rel="shortcut icon" href="/vue-poc/ui/icon.png"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.20.1/vis-timeline-graph2d.min.css" />
</head>
<body>
<div id="app">
<div class="spinner">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.1/vue.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.1/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.5.3/vue-router.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.5.3/vue-router.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.1/axios.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.1/axios.js"></script>
@ -30,71 +46,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/localforage/1.4.3/localforage.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/localforage/1.4.3/localforage.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.20.1/vis-timeline-graph2d.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.20.1/vis-timeline-graph2d.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.20.1/vis-timeline-graph2d.min.css" />
<script src="/vue-poc/ui/perf-stat.js"></script> <script src="/vue-poc/ui/perf-stat.js"></script>
</head>
<body>
<v-app id="app" :dark="dark" @theme="onDark">
<v-navigation-drawer persistent :mini-variant.sync="mini" v-model="drawer"
:disable-route-watcher="true" 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>
<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>
<script src="/vue-poc/ui/app-gen.js"></script> <script src="/vue-poc/ui/app-gen.js"></script>
</body> </body>
</html> </html>

View file

@ -1,6 +1,6 @@
declare namespace fw="quodatum:collection.walker"; declare namespace fw="quodatum:collection.walker";
declare namespace c="http://www.w3.org/ns/xproc-step"; declare namespace c="http://www.w3.org/ns/xproc-step";
import module namespace tree="quodatum.data.tree" at "lib/tree.xqm"; import module namespace tree="quodatum.data.tree" at "../lib/tree.xqm";
let $paths:=uri-collection("/ALO") let $paths:=uri-collection("/ALO")
return tree:build($paths) return tree:build($paths)

View file

@ -0,0 +1,13 @@
(:~
: create vue-poc db
:)
import module namespace dbtools = 'quodatum.dbtools' at "../lib/dbtools.xqm";
declare variable $target-db:="vue-poc";
declare variable $data-uri:=resolve-uri("../data/vue-poc/");
(dbtools:sync-from-files(
$target-db
,$data-uri
,file:list($data-uri,fn:true())
,hof:id#1))

View file

@ -12,7 +12,18 @@ declare namespace c="http://www.w3.org/ns/xproc-step";
declare namespace wadl="http://wadl.dev.java.net/2009/02"; declare namespace wadl="http://wadl.dev.java.net/2009/02";
(:~
: get status
:)
declare
%rest:GET %rest:path("/vue-poc/api/start")
function vue-api:start( )
{
if(db:exists("vue-poc")) then
()
else
()
};
(:~ (:~
: Returns a query result. : Returns a query result.

190
src/vue-poc/vue-poc.vue Normal file
View file

@ -0,0 +1,190 @@
<!DOCTYPE html>
<template id="vuepoc">
<v-app id="app" :dark="dark" @theme="onDark">
<v-navigation-drawer persistent :mini-variant.sync="mini" v-model="drawer"
:disable-route-watcher="true" 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>
<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,
dark: 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: '/server/users',text: 'Users',icon: 'supervisor_account'},
{href: '/server/repo',text: 'Server code repository',icon: 'local_library'},
{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
},
onDark(dark){
this.dark=dark
alert("theme")
}
},
created(){
console.log("create-----------")
var that=this
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);
that.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
console.log("$$$$$$$$$$$",error)
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

@ -40,7 +40,9 @@ function vue-poc:file(
}; };
(:~
:web serve $file if it exists otherwise serve $vue-poc:index
:)
declare function vue-poc:get-file($file) declare function vue-poc:get-file($file)
{ {
let $path := resolve-uri( 'static/' || $file,static-base-uri()) let $path := resolve-uri( 'static/' || $file,static-base-uri())