mirror of https://github.com/perkeep/perkeep.git
Merge "newui: filetree"
This commit is contained in:
commit
45287dbd15
|
@ -428,6 +428,11 @@ func (ui *UIHandler) serveNewUI(rw http.ResponseWriter, req *http.Request) {
|
|||
serveStaticFile(rw, req, newuiFiles, file)
|
||||
return
|
||||
}
|
||||
if wantsFileTreePage(req) {
|
||||
file := "/filetree.html"
|
||||
serveStaticFile(rw, req, newuiFiles, file)
|
||||
return
|
||||
}
|
||||
|
||||
if suffix == "new" {
|
||||
// Add a trailing slash.
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright 2011 The Camlistore Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.cam-filetree-page {
|
||||
font: 16px/1.4 normal Arial, sans-serif;
|
||||
}
|
||||
.cam-filetree-nav:before {
|
||||
content: "[";
|
||||
}
|
||||
.cam-filetree-nav:after {
|
||||
content: "]";
|
||||
}
|
||||
.cam-filetree-newp {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
color: darkgreen;
|
||||
margin-left: .4em;
|
||||
font-size: 80%;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Filetree</title>
|
||||
<script src="closure/goog/base.js"></script>
|
||||
<script src="./deps.js"></script>
|
||||
<script src="?camli.mode=config&var=CAMLISTORE_CONFIG"></script>
|
||||
<!-- Begin non-Closure cheating; but depended on by server_connection.js -->
|
||||
<script type="text/javascript" src="../base64.js"></script>
|
||||
<script type="text/javascript" src="../Crypto.js"></script>
|
||||
<script type="text/javascript" src="../SHA1.js"></script>
|
||||
<!-- End non-Closure cheating -->
|
||||
<script>
|
||||
goog.require('camlistore.FiletreePage');
|
||||
</script>
|
||||
<link rel="stylesheet" href="filetree.css">
|
||||
</head>
|
||||
<body class="cam-filetree-page">
|
||||
<div class="cam-filetree-nav"><a href="./">Home</a></div>
|
||||
<h1>FileTree for <span id="curDir" class="cam-filetree-nav"></span> </h1>
|
||||
|
||||
<div id="children"></div>
|
||||
<script>
|
||||
var page = new camlistore.FiletreePage(CAMLISTORE_CONFIG);
|
||||
page.decorate(document.body);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
Copyright 2011 The Camlistore Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Filetree page.
|
||||
*
|
||||
*/
|
||||
goog.provide('camlistore.FiletreePage');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('camlistore.ServerConnection');
|
||||
|
||||
/**
|
||||
* @param {camlistore.ServerType.DiscoveryDocument} config Global config
|
||||
* of the current server this page is being rendered for.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper to use.
|
||||
*
|
||||
* @extends {goog.ui.Component}
|
||||
* @constructor
|
||||
*/
|
||||
camlistore.FiletreePage = function(config, opt_domHelper) {
|
||||
goog.base(this, opt_domHelper);
|
||||
|
||||
/**
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.config_ = config;
|
||||
|
||||
/**
|
||||
* @type {camlistore.ServerConnection}
|
||||
* @private
|
||||
*/
|
||||
this.connection_ = new camlistore.ServerConnection(config);
|
||||
|
||||
};
|
||||
goog.inherits(camlistore.FiletreePage, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
camlistore.FiletreePage.prototype.indentStep_ = 20;
|
||||
|
||||
|
||||
function getPermanodeParam() {
|
||||
var blobRef = getQueryParam('d');
|
||||
return (blobRef && isPlausibleBlobRef(blobRef)) ? blobRef : null;
|
||||
}
|
||||
|
||||
// Returns the first value from the query string corresponding to |key|.
|
||||
// Returns null if the key isn't present.
|
||||
getQueryParam = function(key) {
|
||||
var params = document.location.search.substring(1).split('&');
|
||||
for (var i = 0; i < params.length; ++i) {
|
||||
var parts = params[i].split('=');
|
||||
if (parts.length == 2 && decodeURIComponent(parts[0]) == key)
|
||||
return decodeURIComponent(parts[1]);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Returns true if the passed-in string might be a blobref.
|
||||
isPlausibleBlobRef = function(blobRef) {
|
||||
return /^\w+-[a-f0-9]+$/.test(blobRef);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called when component's element is known to be in the document.
|
||||
*/
|
||||
camlistore.FiletreePage.prototype.enterDocument = function() {
|
||||
camlistore.FiletreePage.superClass_.enterDocument.call(this);
|
||||
var blobref = getPermanodeParam();
|
||||
|
||||
if (blobref) {
|
||||
this.connection_.describeWithThumbnails(
|
||||
blobref,
|
||||
0,
|
||||
goog.bind(this.handleDescribeBlob_, this, blobref),
|
||||
function(msg) {
|
||||
alert("failed to get blob description: " + msg);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} permanode Node to describe.
|
||||
* @param {camlistore.ServerType.DescribeResponse} describeResult Object of properties for the node.
|
||||
* @private
|
||||
*/
|
||||
camlistore.FiletreePage.prototype.handleDescribeBlob_ =
|
||||
function(permanode, describeResult) {
|
||||
var meta = describeResult.meta;
|
||||
if (!meta[permanode]) {
|
||||
alert("didn't get blob " + permanode);
|
||||
return;
|
||||
}
|
||||
var binfo = meta[permanode];
|
||||
if (!binfo) {
|
||||
alert("Error describing blob " + permanode);
|
||||
return;
|
||||
}
|
||||
if (binfo.camliType != "directory") {
|
||||
alert("Does not contain a directory");
|
||||
return;
|
||||
}
|
||||
this.connection_.getBlobContents(
|
||||
permanode,
|
||||
goog.bind(function(data) {
|
||||
var finfo = JSON.parse(data);
|
||||
var fileName = finfo.fileName;
|
||||
var curDir = document.getElementById('curDir');
|
||||
curDir.innerHTML = "<a href='./?b=" + permanode + "'>" + fileName + "</a>";
|
||||
this.buildTree_();
|
||||
}, this),
|
||||
function(msg) {
|
||||
alert("failed to get blobcontents: " + msg);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
camlistore.FiletreePage.prototype.buildTree_ = function() {
|
||||
var blobref = getPermanodeParam();
|
||||
var children = goog.dom.getElement("children");
|
||||
this.connection_.getFileTree(blobref,
|
||||
goog.bind(function(jres) {
|
||||
this.onChildrenFound_(children, 0, jres);
|
||||
}, this)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} div node used as root for the tree
|
||||
* @param {number} depth how deep we are in the tree, for indenting
|
||||
* @param {camlistore.ServerType.DescribeResponse} jres describe result
|
||||
* @private
|
||||
*/
|
||||
camlistore.FiletreePage.prototype.onChildrenFound_ =
|
||||
function(div, depth, jres) {
|
||||
var indent = depth * camlistore.FiletreePage.prototype.indentStep_;
|
||||
div.innerHTML = "";
|
||||
for (var i = 0; i < jres.children.length; i++) {
|
||||
var children = jres.children;
|
||||
var pdiv = goog.dom.createElement("div");
|
||||
var alink = goog.dom.createElement("a");
|
||||
alink.style.paddingLeft=indent + "px"
|
||||
alink.id = children[i].blobRef;
|
||||
switch (children[i].type) {
|
||||
case 'directory':
|
||||
goog.dom.setTextContent(alink, "+ " + children[i].name);
|
||||
goog.events.listen(alink,
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(function (p, d) {
|
||||
this.unFold_(p, d);
|
||||
}, this, alink.id, depth),
|
||||
false, this
|
||||
);
|
||||
break;
|
||||
case 'file':
|
||||
goog.dom.setTextContent(alink, " " + children[i].name);
|
||||
alink.href = "./?b=" + alink.id;
|
||||
break;
|
||||
default:
|
||||
alert("not a file or dir");
|
||||
break;
|
||||
}
|
||||
var newPerm = goog.dom.createElement("span");
|
||||
newPerm.className = "cam-filetree-newp";
|
||||
goog.dom.setTextContent(newPerm, "P");
|
||||
goog.events.listen(newPerm,
|
||||
goog.events.EventType.CLICK,
|
||||
this.newPermWithContent_(alink.id),
|
||||
false, this
|
||||
);
|
||||
goog.dom.appendChild(pdiv, alink);
|
||||
goog.dom.appendChild(pdiv, newPerm);
|
||||
goog.dom.appendChild(div, pdiv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} content blobref of the content
|
||||
* @private
|
||||
*/
|
||||
camlistore.FiletreePage.prototype.newPermWithContent_ =
|
||||
function(content) {
|
||||
var fun = function(e) {
|
||||
this.connection_.createPermanode(
|
||||
goog.bind(function(permanode) {
|
||||
this.connection_.newAddAttributeClaim(
|
||||
permanode, "camliContent", content,
|
||||
function() {
|
||||
alert("permanode created");
|
||||
},
|
||||
function(msg) {
|
||||
// TODO(mpl): "cancel" new permanode
|
||||
alert("set permanode content failed: " + msg);
|
||||
}
|
||||
);
|
||||
}, this),
|
||||
function(msg) {
|
||||
alert("create permanode failed: " + msg);
|
||||
}
|
||||
);
|
||||
}
|
||||
return goog.bind(fun, this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} blobref dir to unfold.
|
||||
* @param {number} depth so we know how much to indent.
|
||||
* @private
|
||||
*/
|
||||
camlistore.FiletreePage.prototype.unFold_ =
|
||||
function(blobref, depth) {
|
||||
var node = goog.dom.getElement(blobref);
|
||||
var div = goog.dom.createElement("div");
|
||||
this.connection_.getFileTree(blobref,
|
||||
goog.bind(function(jres) {
|
||||
this.onChildrenFound_(div, depth+1, jres);
|
||||
insertAfter(node, div);
|
||||
goog.events.removeAll(node);
|
||||
goog.events.listen(node,
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(function(p, d) {
|
||||
this.fold_(p, d);
|
||||
}, this, blobref, depth),
|
||||
false, this
|
||||
);
|
||||
}, this)
|
||||
);
|
||||
}
|
||||
|
||||
function insertAfter( referenceNode, newNode ) {
|
||||
// nextSibling X2 because of the "P" span
|
||||
referenceNode.parentNode.insertBefore( newNode, referenceNode.nextSibling.nextSibling );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} nodeid id of the node to fold.
|
||||
* @param {depth} depth so we know how much to indent.
|
||||
* @private
|
||||
*/
|
||||
camlistore.FiletreePage.prototype.fold_ =
|
||||
function(nodeid, depth) {
|
||||
var node = goog.dom.getElement(nodeid);
|
||||
// nextSibling X2 because of the "P" span
|
||||
node.parentNode.removeChild(node.nextSibling.nextSibling);
|
||||
goog.events.removeAll(node);
|
||||
goog.events.listen(node,
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(function(p, d) {
|
||||
this.unFold_(p, d);
|
||||
}, this, nodeid, depth),
|
||||
false, this
|
||||
);
|
||||
}
|
||||
|
|
@ -153,6 +153,27 @@ function(success, opt_fail, e) {
|
|||
this.handleXhrResponseJson_(success, this.safeFail_(opt_fail), e);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} blobref root of the tree
|
||||
* @param {function} success callback with data.
|
||||
* @param {?Function} opt_fail optional failure calback
|
||||
*/
|
||||
camlistore.ServerConnection.prototype.getFileTree =
|
||||
function(blobref, success, opt_fail) {
|
||||
|
||||
// TODO(mpl): fix when we do the switch to newui. and
|
||||
// redo it relatively to one of the roots anyway?
|
||||
var path = "../tree/" + blobref;
|
||||
|
||||
this.sendXhr_(
|
||||
path,
|
||||
goog.bind(this.genericHandleSearch_, this,
|
||||
success, this.safeFail_(opt_fail)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {function(camlistore.ServerType.SearchRecentResponse)} success callback with data.
|
||||
* @param {number=} opt_thumbnailSize
|
||||
|
|
Loading…
Reference in New Issue