2015-02-12 15:12:19 +03:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<style>
|
|
|
|
|
|
|
|
|
|
.container {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
|
|
|
|
top: 100px;
|
|
|
|
|
left: 100px;
|
2015-02-14 03:30:13 +03:00
|
|
|
|
|
|
|
|
box-shadow: rgba(0,0,0,0.5) 0.1em 0.1em 0.4em;
|
2015-03-03 05:27:47 +03:00
|
|
|
|
|
|
|
|
/* make the container expand only to a certain size, then scroll */
|
|
|
|
|
/* XXX need to:
|
|
|
|
|
- auto-scroll vertically
|
|
|
|
|
- use custom scroll bars
|
|
|
|
|
- shorten path to fit width
|
|
|
|
|
i.e. manage width manually when at max-width...
|
|
|
|
|
*/
|
|
|
|
|
max-height: 60vh;
|
|
|
|
|
max-width: 60vw;
|
|
|
|
|
height: auto;
|
|
|
|
|
width: auto;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
overflow-x: hidden;
|
2015-02-12 15:12:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.browse {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
min-width: 300px;
|
|
|
|
|
width: initial;
|
2015-02-13 03:09:40 +03:00
|
|
|
background: gray;
|
|
|
|
|
color: rgba(255,255,255,0.8);
|
|
|
|
|
padding: 5px;
|
|
|
|
|
font-family: sans-serif;
|
2015-02-12 15:12:19 +03:00
|
|
|
}
|
2015-02-15 21:59:14 +03:00
|
|
|
/*
|
|
|
|
|
.browse:not(:focus) {
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
}
|
|
|
|
|
*/
|
2015-02-12 15:12:19 +03:00
|
|
|
.browse .v-block {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: auto;
|
|
|
|
|
|
|
|
|
|
box-sizing: border-box;
|
2015-02-13 03:09:40 +03:00
|
|
|
|
|
|
|
|
border-top: 1px solid rgba(255,255,255, 0.3);
|
|
|
|
|
}
|
|
|
|
|
.browse .v-block:first-of-type {
|
|
|
|
|
border-top: none;
|
2015-02-12 15:12:19 +03:00
|
|
|
}
|
|
|
|
|
.browse .v-block:empty {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-13 03:09:40 +03:00
|
|
|
.browse .title {
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
color: rgba(255,255,255,0.9);
|
|
|
|
|
padding: 5px;
|
|
|
|
|
padding-left: 10px;
|
|
|
|
|
padding-right: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-12 15:12:19 +03:00
|
|
|
.browse .path {
|
2015-02-13 03:09:40 +03:00
|
|
|
padding: 5px;
|
|
|
|
|
padding-left: 10px;
|
|
|
|
|
padding-right: 10px;
|
2015-02-12 15:12:19 +03:00
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
2015-02-13 02:28:42 +03:00
|
|
|
.browse .path:empty {
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
.browse .path:before {
|
|
|
|
|
content: "/";
|
|
|
|
|
}
|
2015-02-12 15:12:19 +03:00
|
|
|
.browse .path .dir {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
}
|
|
|
|
|
.browse .path .dir:after {
|
|
|
|
|
content: "/";
|
|
|
|
|
}
|
2015-02-15 21:59:14 +03:00
|
|
|
/* XXX need to make this resizable up but only to a certain size (~80vh) */
|
|
|
|
|
/*
|
|
|
|
|
.browse .list {
|
|
|
|
|
max-height: 50vh;
|
|
|
|
|
}
|
|
|
|
|
.browse .list:empty {
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
*/
|
2015-02-13 03:09:40 +03:00
|
|
|
.browse .list div {
|
|
|
|
|
padding: 5px;
|
|
|
|
|
padding-left: 10px;
|
|
|
|
|
padding-right: 10px;
|
2015-02-12 17:16:00 +03:00
|
|
|
}
|
|
|
|
|
|
2015-02-14 03:30:13 +03:00
|
|
|
.browse:focus .list div.selected,
|
2015-02-12 15:12:19 +03:00
|
|
|
.browse .path .dir:hover,
|
|
|
|
|
.browse .list div:hover {
|
2015-02-13 03:09:40 +03:00
|
|
|
color: white;
|
2015-02-14 03:30:13 +03:00
|
|
|
background: rgba(0,0,0, 0.05);
|
2015-02-13 03:09:40 +03:00
|
|
|
}
|
|
|
|
|
|
2015-02-14 03:30:13 +03:00
|
|
|
.browse:focus .list div.selected {
|
2015-02-12 15:12:19 +03:00
|
|
|
background: rgba(0,0,0, 0.1);
|
2015-02-13 03:09:40 +03:00
|
|
|
box-shadow: rgba(0,0,0,0.2) 0.1em 0.1em 0.2em;
|
2015-02-12 15:12:19 +03:00
|
|
|
}
|
|
|
|
|
|
2015-02-14 03:30:13 +03:00
|
|
|
.browse .list div.selected {
|
|
|
|
|
background: rgba(0,0,0, 0.08);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-13 03:09:40 +03:00
|
|
|
|
2015-02-12 15:12:19 +03:00
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
<script src="../ext-lib/jquery.js"></script>
|
|
|
|
|
<script src="../ext-lib/jquery-ui.js"></script>
|
|
|
|
|
|
|
|
|
|
<script src="../lib/jli.js"></script>
|
|
|
|
|
|
2015-02-12 19:07:13 +03:00
|
|
|
<script src="../ext-lib/require.js"></script>
|
|
|
|
|
|
2015-02-12 15:12:19 +03:00
|
|
|
<script>
|
|
|
|
|
|
|
|
|
|
var TREE = {
|
|
|
|
|
dir_a: {},
|
|
|
|
|
dir_b: {
|
|
|
|
|
file1: 'this is a file',
|
|
|
|
|
file2: 'this is a file',
|
|
|
|
|
file3: 'this is a file',
|
|
|
|
|
},
|
|
|
|
|
dir_c: {
|
|
|
|
|
file1: 'this is a file',
|
|
|
|
|
dir_b: {
|
|
|
|
|
file1: 'this is a file',
|
|
|
|
|
},
|
2015-03-03 05:27:47 +03:00
|
|
|
dir_c: {},
|
|
|
|
|
dir_d: {},
|
|
|
|
|
dir_e: {},
|
|
|
|
|
dir_f: {},
|
|
|
|
|
dir_g: {},
|
|
|
|
|
dir_h: {},
|
|
|
|
|
dir_i: {},
|
|
|
|
|
dir_j: {},
|
|
|
|
|
dir_k: {},
|
|
|
|
|
dir_l: {},
|
|
|
|
|
dir_m: {},
|
|
|
|
|
dir_o: {},
|
|
|
|
|
dir_p: {},
|
|
|
|
|
dir_q: {},
|
|
|
|
|
dir_r: {},
|
|
|
|
|
dir_s: {},
|
|
|
|
|
dir_t: {},
|
|
|
|
|
dir_u: {},
|
2015-02-12 15:12:19 +03:00
|
|
|
},
|
|
|
|
|
file: 'this is a file',
|
|
|
|
|
}
|
2015-02-13 02:28:42 +03:00
|
|
|
// add some recursion for testing...
|
|
|
|
|
TREE.dir_d = TREE.dir_c.dir_b
|
|
|
|
|
TREE.dir_a.tree = TREE
|
2015-02-14 03:30:13 +03:00
|
|
|
TREE.dir_c.tree = TREE
|
2015-02-13 02:28:42 +03:00
|
|
|
TREE.dir_c.dir_b.tree = TREE
|
2015-02-12 15:12:19 +03:00
|
|
|
|
2015-02-12 19:07:13 +03:00
|
|
|
|
2015-02-12 15:12:19 +03:00
|
|
|
function skipFiles(e, v){
|
|
|
|
|
return typeof(v) != typeof('str')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function make(title){
|
|
|
|
|
var browser = $('<div>')
|
|
|
|
|
.addClass('browse')
|
2015-02-14 03:37:24 +03:00
|
|
|
// make thie widget focusable...
|
2015-02-28 15:11:05 +03:00
|
|
|
// NOTE: tabindex 0 means automatic tab indexing and -1 means
|
|
|
|
|
// focusable bot not tabable...
|
|
|
|
|
//.attr('tabindex', -1)
|
|
|
|
|
.attr('tabindex', 0)
|
2015-02-14 03:37:24 +03:00
|
|
|
// focus the widget if something inside is clicked...
|
2015-02-14 03:30:13 +03:00
|
|
|
.click(function(){
|
|
|
|
|
$(this).focus()
|
|
|
|
|
})
|
2015-02-12 15:12:19 +03:00
|
|
|
.append($('<div>')
|
|
|
|
|
.addClass('v-block title')
|
|
|
|
|
.text(title))
|
|
|
|
|
.append($('<div>')
|
|
|
|
|
.addClass('v-block path'))
|
|
|
|
|
.append($('<div>')
|
|
|
|
|
.addClass('v-block list'))
|
|
|
|
|
.append($('<div>')
|
|
|
|
|
.addClass('v-block info'))
|
|
|
|
|
.append($('<div>')
|
|
|
|
|
.addClass('v-block actions'))
|
|
|
|
|
return browser
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// low level update...
|
|
|
|
|
function update(browser, path, list){
|
|
|
|
|
browser = browser || $('.browse')
|
|
|
|
|
var p = browser.find('.path').empty()
|
|
|
|
|
var l = browser.find('.list').empty()
|
|
|
|
|
|
|
|
|
|
path.forEach(function(e){
|
|
|
|
|
p.append($('<div>')
|
|
|
|
|
.addClass('dir')
|
|
|
|
|
.click(popDir)
|
|
|
|
|
.text(e))
|
|
|
|
|
})
|
|
|
|
|
list.forEach(function(e){
|
|
|
|
|
l.append($('<div>')
|
|
|
|
|
.click(pushDir)
|
|
|
|
|
.text(e))
|
|
|
|
|
})
|
|
|
|
|
return browser
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function showPath(browser, path, tree, skip){
|
|
|
|
|
// XXX remove for pruduction...
|
|
|
|
|
skip = skip || skipFiles
|
|
|
|
|
|
|
|
|
|
browser = browser || $('.browse')
|
|
|
|
|
path = path.constructor !== Array ? path.split(/[\\\/]+/g) : path
|
|
|
|
|
path = path.filter(function(e){ return e != '' })
|
|
|
|
|
|
|
|
|
|
var dir = tree
|
|
|
|
|
path.forEach(function(d){
|
|
|
|
|
dir = dir[d]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// XXX do error checking...
|
|
|
|
|
|
|
|
|
|
dir = skip != null
|
|
|
|
|
// skip files....
|
|
|
|
|
? Object.keys(dir).filter(function(e){ return skip(e, dir[e]) })
|
|
|
|
|
: Object.keys(dir)
|
|
|
|
|
|
|
|
|
|
update(browser, path, dir)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// XXX this will skip incuding the current dir...
|
|
|
|
|
// ...this might work and might not work...
|
|
|
|
|
function getPath(browser, to){
|
|
|
|
|
var skip = false
|
|
|
|
|
return browser.find('.path .dir')
|
|
|
|
|
.filter(function(i, e){
|
|
|
|
|
if(e === to){
|
|
|
|
|
skip = true
|
|
|
|
|
}
|
|
|
|
|
return !skip
|
|
|
|
|
})
|
|
|
|
|
.map(function(i, e){ return $(e).text() })
|
|
|
|
|
.toArray()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function pushDir(){
|
|
|
|
|
var dir = $(this).text()
|
|
|
|
|
var browser = $(this).parents('.browse')
|
|
|
|
|
var path = getPath(browser)
|
|
|
|
|
path.push(dir)
|
|
|
|
|
|
|
|
|
|
showPath(browser, path, TREE)
|
|
|
|
|
}
|
|
|
|
|
function popDir(){
|
|
|
|
|
var dir = $(this).text()
|
|
|
|
|
var browser = $(this).parents('.browse')
|
|
|
|
|
var path = getPath(browser, this)
|
|
|
|
|
|
|
|
|
|
showPath(browser, path, TREE)
|
2015-02-13 02:28:42 +03:00
|
|
|
|
|
|
|
|
select('"'+dir+'"')
|
2015-02-12 15:12:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// base control actions...
|
|
|
|
|
// XXX add keywords:
|
|
|
|
|
// 'first'
|
|
|
|
|
// 'prev'
|
|
|
|
|
// 'next'
|
|
|
|
|
// 'last'
|
|
|
|
|
// 'none' -- deselect
|
2015-02-28 12:04:43 +03:00
|
|
|
// '!' -- return selected element if it exists...
|
2015-02-12 15:12:19 +03:00
|
|
|
// <number> -- select by sequence number...
|
|
|
|
|
// <elem> -- select a specific element...
|
|
|
|
|
function select(elem, browser){
|
|
|
|
|
browser = browser || $('.browse')
|
|
|
|
|
var elems = browser.find('.list div')
|
|
|
|
|
|
|
|
|
|
if(elems.length == 0){
|
|
|
|
|
return $()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
elem = elem || select('!')
|
|
|
|
|
// if none selected get the first...
|
|
|
|
|
elem = elem.length == 0 ? 'first' : elem
|
|
|
|
|
|
|
|
|
|
// first/last...
|
|
|
|
|
if(elem == 'first' || elem == 'last'){
|
|
|
|
|
return select(elems[elem](), browser)
|
|
|
|
|
|
|
|
|
|
// prev/next...
|
|
|
|
|
} else if(elem == 'prev' || elem == 'next'){
|
|
|
|
|
var to = select('!', browser)[elem]('.list div')
|
|
|
|
|
if(to.length == 0){
|
|
|
|
|
return select(elem == 'prev' ? 'last' : 'first', browser)
|
|
|
|
|
}
|
|
|
|
|
select('none')
|
|
|
|
|
return select(to, browser)
|
|
|
|
|
|
|
|
|
|
// deselect...
|
|
|
|
|
} else if(elem == 'none'){
|
|
|
|
|
return elems
|
|
|
|
|
.filter('.selected')
|
|
|
|
|
.removeClass('selected')
|
|
|
|
|
|
|
|
|
|
// strict...
|
|
|
|
|
} else if(elem == '!'){
|
|
|
|
|
return elems.filter('.selected')
|
|
|
|
|
|
|
|
|
|
// number...
|
|
|
|
|
} else if(typeof(elem) == typeof(123)){
|
|
|
|
|
return select($(elems[elem]), browser)
|
|
|
|
|
|
|
|
|
|
// string...
|
|
|
|
|
} else if(typeof(elem) == typeof('str') && /^'.*'$|^".*"$/.test(elem.trim())){
|
|
|
|
|
elem = elem.trim().slice(1, -1)
|
|
|
|
|
return select(browser.find('.list div').filter(function(i, e){
|
|
|
|
|
return $(e).text() == elem
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
// element...
|
|
|
|
|
} else {
|
|
|
|
|
select('none', browser)
|
|
|
|
|
return elem.addClass('selected')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
function push(browser){
|
|
|
|
|
browser = browser || $('.browse')
|
|
|
|
|
var elem = select('!', browser)
|
|
|
|
|
|
|
|
|
|
if(elem.length == 0){
|
|
|
|
|
return select()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var path = getPath(browser)
|
|
|
|
|
|
|
|
|
|
path.push(elem.text())
|
|
|
|
|
|
|
|
|
|
var res = showPath(browser, path, TREE)
|
|
|
|
|
|
|
|
|
|
select(null, browser)
|
|
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
function pop(browser){
|
|
|
|
|
browser = browser || $('.browse')
|
|
|
|
|
var path = getPath(browser)
|
|
|
|
|
var dir = path.pop()
|
|
|
|
|
|
|
|
|
|
var res = showPath(browser, path, TREE)
|
|
|
|
|
|
|
|
|
|
select('"'+dir+'"')
|
|
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
function next(browser){
|
|
|
|
|
return select('next', browser)
|
|
|
|
|
}
|
|
|
|
|
function prev(browser){
|
|
|
|
|
return select('prev', browser)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-13 02:28:42 +03:00
|
|
|
|
|
|
|
|
// default action...
|
|
|
|
|
function action(browser){
|
|
|
|
|
browser = browser || $('.browse')
|
|
|
|
|
|
2015-02-13 03:14:53 +03:00
|
|
|
var cur = select('!', browser).text()
|
|
|
|
|
alert('/' + getPath(browser).concat(cur == '' ? [cur] : [cur, '']).join('/'))
|
2015-02-13 02:28:42 +03:00
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-12 19:07:13 +03:00
|
|
|
var KB = {
|
|
|
|
|
'*':{
|
|
|
|
|
F5: function(){ window.location.reload() },
|
2015-02-13 02:28:42 +03:00
|
|
|
},
|
2015-02-28 12:04:43 +03:00
|
|
|
//'.browse:focus':{
|
|
|
|
|
'.browse':{
|
2015-02-12 19:07:13 +03:00
|
|
|
Up: prev,
|
2015-02-13 02:28:42 +03:00
|
|
|
Backspace: 'Up',
|
2015-02-12 19:07:13 +03:00
|
|
|
Down: next,
|
|
|
|
|
Left: pop,
|
|
|
|
|
Right: push,
|
2015-02-13 02:28:42 +03:00
|
|
|
|
|
|
|
|
Enter: action,
|
2015-02-12 19:07:13 +03:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-03-16 20:40:08 +03:00
|
|
|
//---
|
|
|
|
|
|
2015-03-18 17:15:03 +03:00
|
|
|
// XXX NOTE: the widget itself does not need a title, that's the job for
|
|
|
|
|
// a container widget (dialog, field, ...)
|
|
|
|
|
// ...it can be implemented trivially via an attribute and a :before
|
|
|
|
|
// CSS class...
|
2015-03-16 20:40:08 +03:00
|
|
|
var BrowserClassPrototype = {
|
|
|
|
|
// construct the dom...
|
2015-03-18 17:15:03 +03:00
|
|
|
make: function(options){
|
2015-03-16 20:40:08 +03:00
|
|
|
var browser = $('<div>')
|
|
|
|
|
.addClass('browse')
|
|
|
|
|
// make thie widget focusable...
|
|
|
|
|
// NOTE: tabindex 0 means automatic tab indexing and -1 means
|
|
|
|
|
// focusable bot not tabable...
|
|
|
|
|
//.attr('tabindex', -1)
|
|
|
|
|
.attr('tabindex', 0)
|
|
|
|
|
// focus the widget if something inside is clicked...
|
|
|
|
|
.click(function(){
|
|
|
|
|
$(this).focus()
|
|
|
|
|
})
|
2015-03-18 17:15:03 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if(options.path == null || options.show_path){
|
|
|
|
|
browser
|
|
|
|
|
.append($('<div>')
|
|
|
|
|
.addClass('v-block path'))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
browser
|
2015-03-16 20:40:08 +03:00
|
|
|
.append($('<div>')
|
|
|
|
|
.addClass('v-block list'))
|
2015-03-18 17:15:03 +03:00
|
|
|
|
2015-03-16 20:40:08 +03:00
|
|
|
return browser
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-18 17:15:03 +03:00
|
|
|
// XXX Q: should we make a base list dialog and build this on that or
|
|
|
|
|
// simplify this to implement a list (removing the path and disbling
|
|
|
|
|
// traversal)??
|
|
|
|
|
// XXX need a search/filter field...
|
|
|
|
|
// XXX need base events:
|
|
|
|
|
// - opne
|
|
|
|
|
// - update
|
|
|
|
|
// - select (???)
|
2015-03-16 20:40:08 +03:00
|
|
|
var BrowserPrototype = {
|
|
|
|
|
dom: null,
|
|
|
|
|
|
2015-03-18 17:15:03 +03:00
|
|
|
options: {
|
|
|
|
|
//path: null,
|
|
|
|
|
//show_path: null,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// XXX this should prevent event handler deligation...
|
2015-03-16 20:40:08 +03:00
|
|
|
keyboard: {
|
|
|
|
|
'.browse':{
|
|
|
|
|
Up: 'prev',
|
|
|
|
|
Backspace: 'Up',
|
|
|
|
|
Down: 'next',
|
|
|
|
|
Left: 'pop',
|
|
|
|
|
Right: 'push',
|
|
|
|
|
|
|
|
|
|
Enter: 'action',
|
|
|
|
|
Esc: 'close',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// base api...
|
|
|
|
|
// NOTE: to avoid duplicating and syncing data, the actual path is
|
|
|
|
|
// stored in DOM...
|
2015-03-18 17:15:03 +03:00
|
|
|
// XXX does the path includes the currently selected element?
|
2015-03-16 20:40:08 +03:00
|
|
|
get path(){
|
|
|
|
|
var skip = false
|
|
|
|
|
return this.dom.find('.path .dir')
|
|
|
|
|
.map(function(i, e){ return $(e).text() })
|
|
|
|
|
.toArray()
|
|
|
|
|
},
|
|
|
|
|
set path(value){
|
|
|
|
|
// XXX normalize path...
|
|
|
|
|
return this.update(path)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// update path...
|
2015-03-18 17:15:03 +03:00
|
|
|
// XXX trigger an "update" event...
|
2015-03-16 20:40:08 +03:00
|
|
|
update: function(path){
|
|
|
|
|
var browser = this.dom
|
|
|
|
|
|
|
|
|
|
var p = browser.find('.path').empty()
|
|
|
|
|
var l = browser.find('.list').empty()
|
|
|
|
|
|
|
|
|
|
// fill the path field...
|
|
|
|
|
path.forEach(function(e){
|
|
|
|
|
p.append($('<div>')
|
|
|
|
|
.addClass('dir')
|
|
|
|
|
.click(popDir)
|
|
|
|
|
.text(e))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// fill the children list...
|
|
|
|
|
this.list(path)
|
|
|
|
|
.forEach(function(e){
|
|
|
|
|
l.append($('<div>')
|
|
|
|
|
.click(pushDir)
|
|
|
|
|
.text(e))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// internal actions...
|
|
|
|
|
|
2015-03-18 17:15:03 +03:00
|
|
|
// Select a list element...
|
2015-03-16 20:40:08 +03:00
|
|
|
//
|
|
|
|
|
// Select first/last child
|
|
|
|
|
// .select('first')
|
|
|
|
|
// .select('last')
|
|
|
|
|
// -> elem
|
|
|
|
|
//
|
|
|
|
|
// Select previous/lext child
|
|
|
|
|
// .select('prev')
|
|
|
|
|
// .select('next')
|
|
|
|
|
// -> elem
|
|
|
|
|
//
|
|
|
|
|
// Deselect
|
|
|
|
|
// .select('none')
|
|
|
|
|
// -> elem
|
|
|
|
|
//
|
|
|
|
|
// Get selected element if it exists, null otherwise...
|
|
|
|
|
// .select('!')
|
|
|
|
|
// -> elem
|
|
|
|
|
// -> $()
|
|
|
|
|
//
|
|
|
|
|
// Select element by sequence number
|
|
|
|
|
// .select(<number>)
|
|
|
|
|
// -> elem
|
|
|
|
|
//
|
|
|
|
|
// Select element by its text...
|
|
|
|
|
// .select('"<text>"')
|
|
|
|
|
// -> elem
|
|
|
|
|
//
|
|
|
|
|
// .select(<elem>)
|
|
|
|
|
// -> elem
|
|
|
|
|
//
|
|
|
|
|
// This will return a jQuery object.
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// XXX revise return values...
|
2015-03-18 17:15:03 +03:00
|
|
|
// XXX Q: should this trigger a "select" event???
|
2015-03-16 20:40:08 +03:00
|
|
|
select: function(elem){
|
|
|
|
|
var browser = this.dom
|
|
|
|
|
var elems = browser.find('.list div')
|
|
|
|
|
|
|
|
|
|
if(elems.length == 0){
|
|
|
|
|
return $()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
elem = elem || this.select('!')
|
|
|
|
|
// if none selected get the first...
|
|
|
|
|
elem = elem.length == 0 ? 'first' : elem
|
|
|
|
|
|
|
|
|
|
// first/last...
|
|
|
|
|
if(elem == 'first' || elem == 'last'){
|
|
|
|
|
return this.select(elems[elem]())
|
|
|
|
|
|
|
|
|
|
// prev/next...
|
|
|
|
|
} else if(elem == 'prev' || elem == 'next'){
|
|
|
|
|
var to = this.select('!', browser)[elem]('.list div')
|
|
|
|
|
if(to.length == 0){
|
|
|
|
|
return this.select(elem == 'prev' ? 'last' : 'first', browser)
|
|
|
|
|
}
|
|
|
|
|
this.select('none')
|
|
|
|
|
return this.select(to)
|
|
|
|
|
|
|
|
|
|
// deselect...
|
|
|
|
|
} else if(elem == 'none'){
|
|
|
|
|
return elems
|
|
|
|
|
.filter('.selected')
|
|
|
|
|
.removeClass('selected')
|
|
|
|
|
|
|
|
|
|
// strict...
|
|
|
|
|
} else if(elem == '!'){
|
|
|
|
|
return elems.filter('.selected')
|
|
|
|
|
|
|
|
|
|
// number...
|
|
|
|
|
} else if(typeof(elem) == typeof(123)){
|
|
|
|
|
return this.select($(elems[elem]))
|
|
|
|
|
|
|
|
|
|
// string...
|
|
|
|
|
} else if(typeof(elem) == typeof('str')
|
|
|
|
|
&& /^'.*'$|^".*"$/.test(elem.trim())){
|
|
|
|
|
elem = elem.trim().slice(1, -1)
|
|
|
|
|
return this.select(browser.find('.list div')
|
|
|
|
|
.filter(function(i, e){
|
|
|
|
|
return $(e).text() == elem
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
// element...
|
|
|
|
|
} else {
|
|
|
|
|
this.select('none')
|
|
|
|
|
return elem.addClass('selected')
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// XXX check if we need to do the ,action when the element id not traversable...
|
|
|
|
|
push: function(elem){
|
|
|
|
|
var browser = this.dom
|
|
|
|
|
var elem = this.select(elem || '!')
|
|
|
|
|
|
|
|
|
|
// nothing selected, select first and exit...
|
|
|
|
|
if(elem.length == 0){
|
|
|
|
|
this.select()
|
|
|
|
|
return this
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-18 17:15:03 +03:00
|
|
|
var path = this.path.push(elem.text())
|
|
|
|
|
|
|
|
|
|
// if not traversable call the action...
|
|
|
|
|
if(this.isTraversable && ! this.isTraversable(path)){
|
|
|
|
|
return this.action(path)
|
|
|
|
|
}
|
2015-03-16 20:40:08 +03:00
|
|
|
|
|
|
|
|
this
|
2015-03-18 17:15:03 +03:00
|
|
|
.update(path)
|
2015-03-16 20:40:08 +03:00
|
|
|
.select()
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
},
|
|
|
|
|
// pop an element off the path / go up one level...
|
|
|
|
|
pop: function(){
|
|
|
|
|
var browser = this.dom
|
|
|
|
|
var path = this.path
|
|
|
|
|
var dir = path.pop()
|
|
|
|
|
|
|
|
|
|
this.update(path)
|
|
|
|
|
|
|
|
|
|
this.select('"'+dir+'"')
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
},
|
|
|
|
|
next: function(elem){
|
|
|
|
|
if(elem != null){
|
|
|
|
|
this.select(elem)
|
|
|
|
|
}
|
|
|
|
|
this.select('next')
|
|
|
|
|
return this
|
|
|
|
|
},
|
|
|
|
|
prev: function(elem){
|
|
|
|
|
if(elem != null){
|
|
|
|
|
this.select(elem)
|
|
|
|
|
}
|
|
|
|
|
this.select('prev')
|
|
|
|
|
return this
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// XXX think about the API...
|
2015-03-18 17:15:03 +03:00
|
|
|
// XXX trigger an "open" event...
|
2015-03-16 20:40:08 +03:00
|
|
|
action: function(){
|
2015-03-18 17:15:03 +03:00
|
|
|
var elem = this.select('!')
|
2015-03-16 20:40:08 +03:00
|
|
|
|
2015-03-18 17:15:03 +03:00
|
|
|
// nothing selected, select first and exit...
|
|
|
|
|
if(elem.length == 0){
|
|
|
|
|
this.select()
|
|
|
|
|
return this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var path = this.path.push(elem.text())
|
|
|
|
|
|
|
|
|
|
var res = this.open(path)
|
2015-03-16 20:40:08 +03:00
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// extension methods...
|
2015-03-18 17:15:03 +03:00
|
|
|
open: function(){ },
|
|
|
|
|
list: function(){ },
|
2015-03-16 20:40:08 +03:00
|
|
|
isTraversable: null,
|
|
|
|
|
|
2015-03-18 17:15:03 +03:00
|
|
|
// XXX need to get a container....
|
|
|
|
|
// XXX prepare/merge options...
|
|
|
|
|
__init__: function(parent, options){
|
|
|
|
|
// XXX merge options...
|
|
|
|
|
// XXX
|
|
|
|
|
|
2015-03-16 20:40:08 +03:00
|
|
|
// build the dom...
|
2015-03-18 17:15:03 +03:00
|
|
|
var dom = this.dom = this.constructor.make(options)
|
2015-03-16 20:40:08 +03:00
|
|
|
|
|
|
|
|
// add keyboard handler...
|
|
|
|
|
dom.keydown(
|
|
|
|
|
keyboard.makeKeyboardHandler(
|
|
|
|
|
this.keyboard,
|
2015-03-18 17:15:03 +03:00
|
|
|
// XXX
|
2015-03-16 20:40:08 +03:00
|
|
|
function(k){ window.DEBUG && console.log(k) },
|
|
|
|
|
this))
|
|
|
|
|
|
|
|
|
|
// attach to parent...
|
|
|
|
|
if(parent != null){
|
|
|
|
|
parent.append(dom)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
var Browser =
|
|
|
|
|
//module.Browser =
|
|
|
|
|
object.makeConstructor('Browser',
|
|
|
|
|
BrowserClassPrototype,
|
|
|
|
|
BrowserPrototype)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
|
2015-02-12 19:07:13 +03:00
|
|
|
|
2015-03-16 20:40:08 +03:00
|
|
|
requirejs(['../lib/keyboard', '../object'], function(k, o){
|
|
|
|
|
keyboard = k
|
|
|
|
|
object = o
|
2015-02-12 19:07:13 +03:00
|
|
|
|
|
|
|
|
// setup base keyboard for devel, in case something breaks...
|
2015-02-28 12:04:43 +03:00
|
|
|
//$(document)
|
|
|
|
|
$('.browse')
|
2015-02-12 19:07:13 +03:00
|
|
|
.keydown(
|
|
|
|
|
keyboard.makeKeyboardHandler(
|
|
|
|
|
KB,
|
2015-02-13 02:28:42 +03:00
|
|
|
function(k){ window.DEBUG && console.log(k) }))
|
2015-02-12 19:07:13 +03:00
|
|
|
|
|
|
|
|
})
|
2015-02-12 15:12:19 +03:00
|
|
|
|
|
|
|
|
$(function(){
|
|
|
|
|
var browser = make()
|
|
|
|
|
$('.container')
|
|
|
|
|
.empty()
|
|
|
|
|
.append(browser)
|
|
|
|
|
showPath(browser, '/', TREE)
|
2015-02-14 03:30:13 +03:00
|
|
|
|
2015-02-14 03:37:24 +03:00
|
|
|
$('.container').draggable({
|
|
|
|
|
cancel: ".path .dir, .list div"
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
browser.focus()
|
2015-02-12 15:12:19 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<body>
|
|
|
|
|
|
2015-02-12 17:16:00 +03:00
|
|
|
<button onclick="pop()"><</button>
|
|
|
|
|
<button onclick="push()">></button>
|
|
|
|
|
<button onclick="prev()">^</button>
|
|
|
|
|
<button onclick="next()">v</button>
|
|
|
|
|
|
2015-02-12 15:12:19 +03:00
|
|
|
|
|
|
|
|
<div class="container">
|
|
|
|
|
<div class="browse">
|
|
|
|
|
<!-- title, optional -->
|
|
|
|
|
<div class="v-block title">
|
|
|
|
|
[title]
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- the actual list -->
|
|
|
|
|
<div class="v-block path">
|
|
|
|
|
<div class="dir">
|
|
|
|
|
[dir]
|
|
|
|
|
</div>
|
|
|
|
|
<div class="dir">
|
|
|
|
|
[dir]
|
|
|
|
|
</div>
|
|
|
|
|
<div class="dir">
|
|
|
|
|
[dir]
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- the actual list -->
|
|
|
|
|
<div class="v-block list">
|
|
|
|
|
<div>
|
|
|
|
|
[dir]
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
[dir]
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
[dir]
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- info, optional -->
|
|
|
|
|
<div class="v-block info">
|
|
|
|
|
[info]
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- buttons, optional -->
|
|
|
|
|
<div class="v-block actions">
|
|
|
|
|
[actions]
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
2015-02-28 12:04:43 +03:00
|
|
|
<!-- vim:set ts=4 sw=4 : -->
|