github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/server/camlistored/ui/permanode.js (about) 1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 goog.provide('cam.PermanodePage'); 18 19 goog.require('goog.dom'); 20 goog.require('goog.string'); 21 goog.require('goog.events.EventHandler'); 22 goog.require('goog.events.EventType'); 23 goog.require('goog.events.FileDropHandler'); 24 goog.require('goog.ui.Component'); 25 26 goog.require('cam.BlobItem'); 27 goog.require('cam.BlobItemContainer'); 28 goog.require('cam.ServerConnection'); 29 30 // @param {cam.ServerType.DiscoveryDocument} config Global config of the current server this page is being rendered for. 31 // @param {goog.dom.DomHelper=} opt_domHelper DOM helper to use. 32 // @extends {goog.ui.Component} 33 // @constructor 34 cam.PermanodePage = function(config, opt_domHelper) { 35 goog.base(this, opt_domHelper); 36 37 this.config_ = config; 38 39 this.connection_ = new cam.ServerConnection(config); 40 41 this.blobItemContainer_ = new cam.BlobItemContainer(this.connection_, opt_domHelper); 42 this.blobItemContainer_.thumbnailSize_ = cam.BlobItemContainer.THUMBNAIL_SIZES_[3]; 43 44 this.describeResponse_ = null; 45 }; 46 goog.inherits(cam.PermanodePage, goog.ui.Component); 47 48 cam.PermanodePage.prototype.decorateInternal = function(element) { 49 cam.PermanodePage.superClass_.decorateInternal.call(this, element); 50 51 var el = this.getElement(); 52 goog.dom.classes.add(el, 'cam-permanode-page'); 53 54 }; 55 56 cam.PermanodePage.prototype.disposeInternal = function() { 57 cam.PermanodePage.superClass_.disposeInternal.call(this); 58 this.eh_.dispose(); 59 }; 60 61 cam.PermanodePage.prototype.enterDocument = function() { 62 cam.PermanodePage.superClass_.enterDocument.call(this); 63 var permanode = getPermanodeParam(); 64 if (permanode) { 65 goog.dom.getElement('permanode').innerHTML = "<a href='./?p=" + permanode + "'>" + permanode + "</a>"; 66 goog.dom.getElement('permanodeBlob').innerHTML = "<a href='./?b=" + permanode + "'>view blob</a>"; 67 goog.dom.getElement('detailLink').innerHTML = "<a href='./detail.html?" + permanode + "'>new hotness</a>" 68 } 69 70 // TODO(mpl): use this.eh_ instead? 71 // set up listeners 72 goog.events.listen(goog.dom.getElement('formTitle'), goog.events.EventType.SUBMIT, this.handleFormTitleSubmit_, false, this); 73 goog.events.listen(goog.dom.getElement('formTags'), goog.events.EventType.SUBMIT, this.handleFormTagsSubmit_, false, this); 74 goog.events.listen(goog.dom.getElement('formAccess'), goog.events.EventType.SUBMIT, this.handleFormAccessSubmit_, false, this); 75 goog.events.listen(goog.dom.getElement('btnGallery'), goog.events.EventType.CLICK, function() { 76 var btnGallery = goog.dom.getElement('btnGallery'); 77 if (btnGallery.value == "list") { 78 goog.dom.setTextContent(btnGallery, "List"); 79 btnGallery.value = "thumbnails"; 80 } else { 81 goog.dom.setTextContent(btnGallery, "Thumbnails"); 82 btnGallery.value = "list"; 83 } 84 this.reloadMembers_(); 85 }, false, this); 86 87 // set publish roots 88 this.setupRootsDropdown_(); 89 90 // set dnd and form for file upload 91 this.setupFilesHandlers_(); 92 93 this.describeBlob_() 94 95 this.buildPathsList_() 96 97 this.blobItemContainer_.render(goog.dom.getElement('membersThumbs')); 98 }; 99 100 // Gets the |p| query parameter, assuming that it looks like a blobref. 101 function getPermanodeParam() { 102 var blobRef = getQueryParam('p'); 103 return (blobRef && isPlausibleBlobRef(blobRef)) ? blobRef : null; 104 }; 105 106 cam.PermanodePage.prototype.exitDocument = function() { 107 cam.PermanodePage.superClass_.exitDocument.call(this); 108 }; 109 110 // @param {string} blobRef BlobRef for the uploaded file. 111 // @param {string} permanode Permanode this blobRef is now the content of. 112 cam.PermanodePage.prototype.describeBlob_ = function() { 113 var permanode = getPermanodeParam(); 114 this.connection_.describeWithThumbnails(permanode, this.blobItemContainer_.thumbnailSize_, 115 goog.bind(this.handleDescribeBlob_, this, permanode), 116 function(msg) { 117 alert("failed to get blob description: " + msg); 118 } 119 ); 120 }; 121 122 // @param {string} permanode Node to describe. 123 // @param {Object} describeResult Object of properties for the node. 124 cam.PermanodePage.prototype.handleDescribeBlob_ = function(permanode, describeResult) { 125 var meta = describeResult.meta; 126 if (!meta[permanode]) { 127 alert("didn't get blob " + permanode); 128 return; 129 } 130 var permObj = meta[permanode].permanode; 131 if (!permObj) { 132 alert("blob " + permanode + " isn't a permanode"); 133 return; 134 } 135 this.describeResponse_ = describeResult; 136 137 // title form 138 var permTitleValue = permAttr(permObj, "title") ? permAttr(permObj, "title") : ""; 139 var inputTitle = goog.dom.getElement("inputTitle"); 140 inputTitle.value = permTitleValue; 141 inputTitle.disabled = false; 142 var btnSaveTitle = goog.dom.getElement("btnSaveTitle"); 143 btnSaveTitle.disabled = false; 144 145 // tags form 146 this.reloadTags_(permanode, describeResult); 147 var inputNewTag = goog.dom.getElement("inputNewTag"); 148 inputNewTag.disabled = false; 149 var btnAddTag = goog.dom.getElement("btnAddTag"); 150 btnAddTag.disabled = false; 151 152 // access form 153 var selectAccess = goog.dom.getElement("selectAccess"); 154 var accessValue = permAttr(permObj,"camliAccess") ? permAttr(permObj,"camliAccess") : "private"; 155 selectAccess.value = accessValue; 156 selectAccess.disabled = false; 157 var btnSaveAccess = goog.dom.getElement("btnSaveAccess"); 158 btnSaveAccess.disabled = false; 159 160 // handle type detection 161 handleType(permObj); 162 163 // TODO(mpl): add a line showing something like 164 // "Content: file (blobref)" or 165 // "Content: directory (blobref)" or 166 // "Content: None (has members)". 167 168 // members 169 this.reloadMembers_(); 170 171 // TODO(mpl): use a permanent blobItemContainer instead? 172 /* blob content */ 173 var camliContent = permObj.attr.camliContent; 174 if (camliContent && camliContent.length > 0) { 175 var content = goog.dom.getElement('content'); 176 content.innerHTML = ''; 177 var useFileBlobrefAsLink = "true"; 178 var blobItem = new cam.BlobItem(permanode, meta, useFileBlobrefAsLink); 179 blobItem.decorate(content); 180 blobItem.setSize(300, 300); 181 // TODO(mpl): ideally this should be done by handleType, but it's easier 182 // to do it now that we have a blobItem object to work with. 183 var isdir = blobItem.getDirBlobref_() 184 var mountTip = goog.dom.getElement("cammountTip"); 185 goog.dom.removeChildren(mountTip); 186 if (isdir != "") { 187 var tip = "Mount with:"; 188 goog.dom.setTextContent(mountTip, tip); 189 goog.dom.appendChild(mountTip, goog.dom.createDom("br")); 190 var codeTip = goog.dom.createDom("code"); 191 goog.dom.setTextContent(codeTip, "$ cammount /some/mountpoint " + isdir); 192 goog.dom.appendChild(mountTip, codeTip); 193 } 194 } 195 196 // debug attrs 197 goog.dom.setTextContent(goog.dom.getElement("debugattrs"), JSON.stringify(permObj.attr, null, 2)); 198 }; 199 200 // TODO(mpl): pass directly the permanode object 201 // @param {string} permanode Node to describe. 202 // @param {Object} describeResult Object of properties for the node. 203 cam.PermanodePage.prototype.reloadTags_ = function(permanode, describeResult) { 204 var permanodeObject = describeResult.meta[permanode].permanode; 205 var spanTags = document.getElementById("spanTags"); 206 while (spanTags.firstChild) { 207 spanTags.removeChild(spanTags.firstChild); 208 } 209 var tags = permanodeObject.attr.tag; 210 for (idx in tags) { 211 var tag = tags[idx]; 212 213 var tagSpan = goog.dom.createDom("span"); 214 tagSpan.className = 'cam-permanode-tag-c'; 215 var tagTextEl = goog.dom.createDom("span"); 216 tagTextEl.className = 'cam-permanode-tag'; 217 goog.dom.setTextContent(tagTextEl, tag); 218 goog.dom.appendChild(tagSpan, tagTextEl); 219 220 var tagDel = goog.dom.createDom("span"); 221 tagDel.className = 'cam-permanode-del'; 222 goog.dom.setTextContent(tagDel, "x"); 223 goog.events.listen(tagDel, goog.events.EventType.CLICK, this.deleteTagFunc_(tag, tagTextEl, tagSpan), false, this); 224 225 goog.dom.appendChild(tagSpan, tagDel); 226 goog.dom.appendChild(spanTags, tagSpan); 227 } 228 }; 229 230 // @param {Object} tag tag value to remove. 231 // @param {Object} strikeEle text element to strike while we wait for the removal to take effect. 232 // @param {Object} removeEle element to remove. 233 // @return {Function} 234 cam.PermanodePage.prototype.deleteTagFunc_ = function(tag, strikeEle, removeEle) { 235 var delFunc = function(e) { 236 strikeEle.innerHTML = "<del>" + strikeEle.innerHTML + "</del>"; 237 this.connection_.newDelAttributeClaim(getPermanodeParam(), "tag", tag, 238 function() { removeEle.parentNode.removeChild(removeEle); }, 239 function(msg) { alert(msg); } 240 ); 241 }; 242 return goog.bind(delFunc, this); 243 }; 244 245 cam.PermanodePage.prototype.isCamliPathAttribute_ = function(name) { 246 return goog.string.startsWith(name, "camliPath:"); 247 }; 248 249 cam.PermanodePage.prototype.reloadMembers_ = function() { 250 var membersList = goog.dom.getElement('membersList'); 251 var membersThumbs = goog.dom.getElement('membersThumbs'); 252 membersList.innerHTML = ''; 253 254 var meta = this.describeResponse_.meta; 255 var permanode = meta[getPermanodeParam()].permanode; 256 var attrs = permanode.attr; 257 var hasMembers = false; 258 var btnGallery = goog.dom.getElement('btnGallery'); 259 var doThumbnails = (btnGallery.value == "thumbnails"); 260 261 if (attrs.camliMember) { 262 attrs.camliMember.forEach(function(m) { 263 this.addMember_(m, "camliMember", meta, doThumbnails); 264 hasMembers = true; 265 }.bind(this)); 266 } 267 268 for (var name in attrs) { 269 if (this.isCamliPathAttribute_(name)) { 270 var attr = permAttr(permanode, name); 271 if (attr) { 272 this.addMember_(attr, name, meta, doThumbnails); 273 hasMembers = true; 274 } 275 } 276 } 277 278 if (hasMembers) { 279 if (doThumbnails) { 280 this.blobItemContainer_.show_(); 281 } else { 282 this.blobItemContainer_.hide_(); 283 this.blobItemContainer_.resetChildren_(); 284 } 285 } 286 }; 287 288 // @param {string} pn child permanode. 289 // @param {Object} meta meta in describe response. 290 // @param {boolean} thumbnails whether to display thumbnails or a list 291 cam.PermanodePage.prototype.addMember_ = function(pn, path, meta, thumbnails) { 292 var blobItem = new cam.BlobItem(pn, meta); 293 if (thumbnails) { 294 this.blobItemContainer_.addChild(blobItem, true) 295 } else { 296 var membersList = goog.dom.getElement("membersList"); 297 var ul; 298 if (membersList.innerHTML == "") { 299 ul = goog.dom.createDom("ul"); 300 goog.dom.appendChild(membersList, ul); 301 } else { 302 ul = membersList.firstChild; 303 } 304 var li = goog.dom.createDom("li"); 305 var a = goog.dom.createDom("a"); 306 a.href = "./?p=" + pn; 307 goog.dom.setTextContent(a, blobItem.getTitle_()); 308 309 var del = goog.dom.createDom("span"); 310 del.className = 'cam-permanode-del'; 311 goog.dom.setTextContent(del, "x"); 312 goog.events.listen(del, goog.events.EventType.CLICK, this.deleteMemberFunc_(pn, path, a, li), false, this); 313 314 goog.dom.appendChild(li, a); 315 goog.dom.appendChild(li, del); 316 goog.dom.appendChild(ul, li); 317 } 318 }; 319 320 // @param {string} member child permanode 321 // @param {Object} strikeEle text element to strike while we wait for the removal to take effect. 322 // @param {Object} removeEle element to remove. 323 // @return {Function} 324 cam.PermanodePage.prototype.deleteMemberFunc_ = function(member, path, strikeEle, removeEle) { 325 var delFunc = function(e) { 326 strikeEle.innerHTML = "<del>" + strikeEle.innerHTML + "</del>"; 327 this.connection_.newDelAttributeClaim(getPermanodeParam(), path, member, 328 goog.bind(function() { 329 removeEle.parentNode.removeChild(removeEle); 330 // TODO(mpl): refreshing the whole thing is kindof heavy, maybe? 331 this.describeBlob_(); 332 }, this), 333 function(msg) { 334 alert(msg); 335 } 336 ); 337 }; 338 return goog.bind(delFunc, this); 339 }; 340 341 // @param {string} sourcePermanode permanode pointed by the path. 342 // @param {string} path path to remove. 343 // @param {Object} strikeEle element to remove. 344 // @return {Function} 345 cam.PermanodePage.prototype.deletePathFunc_ = function(sourcePermanode, path, strikeEle) { 346 var delFunc = function(e) { 347 strikeEle.innerHTML = "<del>" + strikeEle.innerHTML + "</del>"; 348 this.connection_.newDelAttributeClaim( 349 sourcePermanode, 350 "camliPath:" + path, 351 getPermanodeParam(), 352 goog.bind(function() { 353 this.buildPathsList_(); 354 }, this), 355 function(msg) { 356 alert(msg); 357 } 358 ); 359 }; 360 return goog.bind(delFunc, this); 361 }; 362 363 cam.PermanodePage.prototype.handleFormTitleSubmit_ = function(e) { 364 e.stopPropagation(); 365 e.preventDefault(); 366 367 var inputTitle = goog.dom.getElement("inputTitle"); 368 inputTitle.disabled = true; 369 var btnSaveTitle = goog.dom.getElement("btnSaveTitle"); 370 btnSaveTitle.disabled = true; 371 372 var startTime = new Date(); 373 this.connection_.newSetAttributeClaim( 374 getPermanodeParam(), "title", inputTitle.value, 375 goog.bind(function() { 376 var elapsedMs = new Date().getTime() - startTime.getTime(); 377 setTimeout(goog.bind(function() { 378 inputTitle.disabled = false; 379 btnSaveTitle.disabled = false; 380 this.describeBlob_(); 381 },this), Math.max(250 - elapsedMs, 0)); 382 }, this), 383 function(msg) { 384 alert(msg); 385 inputTitle.disabled = false; 386 btnSaveTitle.disabled = false; 387 } 388 ); 389 }; 390 391 cam.PermanodePage.prototype.handleFormTagsSubmit_ = function(e) { 392 e.stopPropagation(); 393 e.preventDefault(); 394 395 var input = goog.dom.getElement("inputNewTag"); 396 var btn = goog.dom.getElement("btnAddTag"); 397 if (input.value == "") { 398 return; 399 } 400 input.disabled = true; 401 btn.disabled = true; 402 403 var startTime = new Date(); 404 var tags = input.value.split(/\s*,\s*/); 405 var nRemain = tags.length; 406 var oneDone = goog.bind(function() { 407 nRemain--; 408 if (nRemain == 0) { 409 var elapsedMs = new Date().getTime() - startTime.getTime(); 410 setTimeout(goog.bind(function() { 411 input.value = ''; 412 input.disabled = false; 413 btn.disabled = false; 414 this.describeBlob_(); 415 }, this), Math.max(250 - elapsedMs, 0)); 416 } 417 }, this); 418 for (idx in tags) { 419 var tag = tags[idx]; 420 this.connection_.newAddAttributeClaim( 421 getPermanodeParam(), "tag", tag, oneDone, 422 function(msg) { 423 alert(msg); 424 oneDone(); 425 } 426 ); 427 } 428 }; 429 430 cam.PermanodePage.prototype.handleFormAccessSubmit_ = function(e) { 431 e.stopPropagation(); 432 e.preventDefault(); 433 434 var selectAccess = goog.dom.getElement("selectAccess"); 435 selectAccess.disabled = true; 436 var btnSaveAccess = goog.dom.getElement("btnSaveAccess"); 437 btnSaveAccess.disabled = true; 438 439 var operation = this.connection_.newDelAttributeClaim; 440 var value = ""; 441 if (selectAccess.value != "private") { 442 operation = this.connection_.newSetAttributeClaim; 443 value = selectAccess.value; 444 } 445 446 var startTime = new Date(); 447 operation = goog.bind(operation, this.connection_); 448 operation( 449 getPermanodeParam(), 450 "camliAccess", 451 value, 452 function() { 453 var elapsedMs = new Date().getTime() - startTime.getTime(); 454 setTimeout(function() { 455 selectAccess.disabled = false; 456 btnSaveAccess.disabled = false; 457 }, Math.max(250 - elapsedMs, 0)); 458 }, 459 function(msg) { 460 alert(msg); 461 selectAccess.disabled = false; 462 btnSaveAccess.disabled = false; 463 } 464 ); 465 }; 466 467 cam.PermanodePage.prototype.setupRootsDropdown_ = function() { 468 var selRoots = goog.dom.getElement("selectPublishRoot"); 469 if (!this.config_.publishRoots) { 470 console.log("no publish roots"); 471 return; 472 } 473 for (var rootName in this.config_.publishRoots) { 474 var opt = goog.dom.createElement("option"); 475 opt.setAttribute("value", rootName); 476 goog.dom.appendChild(opt, goog.dom.createTextNode(this.config_.publishRoots[rootName].prefix[0])); 477 goog.dom.appendChild(selRoots, opt); 478 } 479 goog.events.listen(goog.dom.getElement("btnSavePublish"), goog.events.EventType.CLICK, this.handleSavePublish_, false, this); 480 }; 481 482 cam.PermanodePage.prototype.handleSavePublish_ = function(e) { 483 var selRoots = goog.dom.getElement("selectPublishRoot"); 484 var suffix = goog.dom.getElement("publishSuffix"); 485 486 var ourPermanode = getPermanodeParam(); 487 if (!ourPermanode) { 488 return; 489 } 490 491 var publishRoot = selRoots.value; 492 if (!publishRoot) { 493 alert("no publish root selected"); 494 return; 495 } 496 var pathSuffix = suffix.value; 497 if (!pathSuffix) { 498 alert("no path suffix specified"); 499 return; 500 } 501 502 selRoots.disabled = true; 503 suffix.disabled = true; 504 505 var enabled = function() { 506 selRoots.disabled = false; 507 suffix.disabled = false; 508 }; 509 510 // Step 1: resolve selRoots.value -> blobref of the root's permanode. 511 // Step 2: set attribute on the root's permanode, or a sub-permanode 512 // if multiple path components in suffix: 513 // "camliPath:<suffix>" => permanode-of-ourselves 514 515 var sigconf = this.config_.signing; 516 var handleFindCamliRoot = function(pnres) { 517 if (!pnres.permanode) { 518 alert("failed to publish root's permanode"); 519 enabled(); 520 return; 521 } 522 var handleSetCamliPath = function() { 523 console.log("success."); 524 enabled(); 525 selRoots.value = ""; 526 suffix.value = ""; 527 this.buildPathsList_(); 528 }; 529 var handleFailCamliPath = function() { 530 alert("failed to set attribute"); 531 enabled(); 532 }; 533 this.connection_.newSetAttributeClaim( 534 pnres.permanode, "camliPath:" + pathSuffix, ourPermanode, 535 goog.bind(handleSetCamliPath, this), handleFailCamliPath 536 ); 537 }; 538 var handleFailFindCamliRoot = function() { 539 alert("failed to find publish root's permanode"); 540 enabled(); 541 }; 542 this.connection_.permanodeOfSignerAttrValue( 543 sigconf.publicKeyBlobRef, "camliRoot", publishRoot, 544 goog.bind(handleFindCamliRoot, this), handleFailFindCamliRoot 545 ); 546 }; 547 548 cam.PermanodePage.prototype.buildPathsList_ = function() { 549 var ourPermanode = getPermanodeParam(); 550 if (!ourPermanode) { 551 return; 552 } 553 var sigconf = this.config_.signing; 554 555 var handleFindPath = function(jres) { 556 var div = goog.dom.getElement("existingPaths"); 557 558 // TODO: there can be multiple paths in this list, but the HTML 559 // UI only shows one. The UI should show all, and when adding a new one 560 // prompt users whether they want to add to or replace the existing one. 561 // For now we just update the UI to show one. 562 // alert(JSON.stringify(jres, null, 2)); 563 if (jres.paths && jres.paths.length > 0) { 564 div.innerHTML = "Existing paths for this permanode:"; 565 var ul = goog.dom.createElement("ul"); 566 goog.dom.appendChild(div,ul); 567 for (var idx in jres.paths) { 568 var path = jres.paths[idx]; 569 var li = goog.dom.createElement("li"); 570 var span = goog.dom.createElement("span"); 571 goog.dom.appendChild(li,span); 572 573 var blobLink = goog.dom.createElement("a"); 574 blobLink.href = ".?p=" + path.baseRef; 575 goog.dom.setTextContent(blobLink, path.baseRef); 576 goog.dom.appendChild(span,blobLink); 577 578 goog.dom.appendChild(span,goog.dom.createTextNode(" - ")); 579 580 var pathLink = goog.dom.createElement("a"); 581 pathLink.href = ""; 582 goog.dom.setTextContent(pathLink, path.suffix); 583 for (var key in this.config_.publishRoots) { 584 var root = this.config_.publishRoots[key]; 585 if (root.currentPermanode == path.baseRef) { 586 // Prefix should include a trailing slash. 587 pathLink.href = root.prefix[0] + path.suffix; 588 // TODO: Check if we're the latest permanode 589 // for this path and display some "old" notice 590 // if not. 591 break; 592 } 593 } 594 goog.dom.appendChild(span,pathLink); 595 596 var del = goog.dom.createElement("span"); 597 del.className = "cam-permanode-del"; 598 goog.dom.setTextContent(del, "x"); 599 goog.events.listen(del, goog.events.EventType.CLICK, this.deletePathFunc_(path.baseRef, path.suffix, span), false, this); 600 goog.dom.appendChild(span,del); 601 602 goog.dom.appendChild(ul,li); 603 } 604 } else { 605 div.innerHTML = ""; 606 } 607 }; 608 this.connection_.pathsOfSignerTarget(sigconf.publicKeyBlobRef, ourPermanode, goog.bind(handleFindPath, this), alert); 609 }; 610 611 // TODO(mpl): reuse blobitem code for dnd? 612 cam.PermanodePage.prototype.setupFilesHandlers_ = function() { 613 var dnd = goog.dom.getElement("dnd"); 614 goog.events.listen(goog.dom.getElement("fileForm"), goog.events.EventType.SUBMIT, this.handleFilesSubmit_, false, this); 615 goog.events.listen(goog.dom.getElement("fileInput"), goog.events.EventType.CHANGE, onFileInputChange, false, this); 616 617 var stop = function(e) { 618 this.classList && 619 goog.dom.classes.add(this, 'cam-permanode-dnd-over'); 620 e.stopPropagation(); 621 e.preventDefault(); 622 }; 623 goog.events.listen(dnd, goog.events.EventType.DRAGENTER, stop, false, this); 624 goog.events.listen(dnd, goog.events.EventType.DRAGOVER, stop, false, this); 625 goog.events.listen(dnd, goog.events.EventType.DRAGLEAVE, 626 goog.bind(function() { 627 goog.dom.classes.remove(this, 'cam-permanode-dnd-over'); 628 }, this), false, this); 629 630 var drop = function(e) { 631 goog.dom.classes.remove(this, 'cam-permanode-dnd-over'); 632 stop(e); 633 var dt = e.getBrowserEvent().dataTransfer; 634 var files = dt.files; 635 goog.dom.getElement("info").innerHTML = ""; 636 this.handleFiles_(files); 637 }; 638 goog.events.listen(dnd, goog.events.FileDropHandler.EventType.DROP, goog.bind(drop, this), false, this); 639 }; 640 641 cam.PermanodePage.prototype.handleFilesSubmit_ = function(e) { 642 e.stopPropagation(); 643 e.preventDefault(); 644 this.handleFiles_(document.getElementById("fileInput").files); 645 }; 646 647 // @param {Array<files>} files the files to upload. 648 cam.PermanodePage.prototype.handleFiles_ = function(files) { 649 for (var i = 0; i < files.length; i++) { 650 var file = files[i]; 651 this.startFileUpload_(file); 652 } 653 }; 654 655 cam.PermanodePage.prototype.startFileUpload_ = function(file) { 656 var dnd = goog.dom.getElement("dnd"); 657 var up = goog.dom.createElement("div"); 658 up.className= 'cam-permanode-dnd-item'; 659 goog.dom.appendChild(dnd, up); 660 var info = "name=" + file.name + " size=" + file.size + "; type=" + file.type; 661 662 var setStatus = function(status) { 663 up.innerHTML = info + " " + status; 664 }; 665 setStatus("(scanning)"); 666 667 var onFail = function(msg) { 668 up.innerHTML = info + " <strong>fail:</strong> "; 669 goog.dom.appendChild(up, goog.dom.createTextNode(msg)); 670 }; 671 672 var onGotFileSchemaRef = function(fileref) { 673 setStatus(" <strong>fileref: " + fileref + "</strong>"); 674 this.connection_.createPermanode( 675 goog.bind(function(filepn) { 676 var doneWithAll = goog.bind(function() { 677 setStatus("- done"); 678 this.describeBlob_(); 679 }, this); 680 var addMemberToParent = function() { 681 setStatus("adding member"); 682 this.connection_.newAddAttributeClaim( 683 getPermanodeParam(), "camliMember", filepn, 684 doneWithAll, onFail 685 ); 686 }; 687 var makePermanode = goog.bind(function() { 688 setStatus("making permanode"); 689 this.connection_.newSetAttributeClaim( 690 filepn, "camliContent", fileref, 691 goog.bind(addMemberToParent, this), onFail 692 ); 693 }, this); 694 makePermanode(); 695 }, this), 696 onFail 697 ); 698 }; 699 700 this.connection_.uploadFile(file, goog.bind(onGotFileSchemaRef, this), onFail, 701 function(contentsRef) { 702 setStatus("(checking for dup of " + contentsRef + ")"); 703 }); 704 }; 705 706 // Returns the first value from the query string corresponding to |key|. 707 // Returns null if the key isn't present. 708 getQueryParam = function(key) { 709 var params = document.location.search.substring(1).split('&'); 710 for (var i = 0; i < params.length; ++i) { 711 var parts = params[i].split('='); 712 if (parts.length == 2 && decodeURIComponent(parts[0]) == key) 713 return decodeURIComponent(parts[1]); 714 } 715 return null; 716 }; 717 718 // Returns true if the passed-in string might be a blobref. 719 isPlausibleBlobRef = function(blobRef) { 720 return /^\w+-[a-f0-9]+$/.test(blobRef); 721 }; 722 723 function hasNamedMembers(permanode) { 724 for (var name in permanode.attr) { 725 if (/^camliPath:/.test(name)) { 726 return Boolean(permAttr(permanode, name)); 727 } 728 } 729 return false; 730 } 731 732 function hasUnnamedMembers(permanode) { 733 return permAttr(permanode, "camliMember"); 734 } 735 736 function permAttr(permanodeObject, name) { 737 if (!(name in permanodeObject.attr)) { 738 return null; 739 } 740 if (permanodeObject.attr[name].length == 0) { 741 return null; 742 } 743 return permanodeObject.attr[name][0]; 744 }; 745 746 function handleType(permObj) { 747 var disablePublish = false; 748 var selType = goog.dom.getElement("type"); 749 var dnd = goog.dom.getElement("dnd"); 750 var btnGallery = goog.dom.getElement("btnGallery"); 751 var membersDiv = goog.dom.getElement("members"); 752 dnd.style.display = "none"; 753 btnGallery.style.visibility = 'hidden'; 754 goog.dom.setTextContent(membersDiv, ""); 755 if (permAttr(permObj, "camliRoot")) { 756 disablePublish = true; // can't give a URL to a root with a claim 757 } else if (hasNamedMembers(permObj) || hasUnnamedMembers(permObj)) { 758 dnd.style.display = "block"; 759 btnGallery.style.visibility = 'visible'; 760 goog.dom.setTextContent(membersDiv, "Members:"); 761 } 762 763 goog.dom.getElement("selectPublishRoot").disabled = disablePublish; 764 goog.dom.getElement("publishSuffix").disabled = disablePublish; 765 goog.dom.getElement("btnSavePublish").disabled = disablePublish; 766 }; 767 768 function $(id) { return goog.dom.getElement(id) } 769 770 function onFileInputChange(e) { 771 var s = ""; 772 var files = $("fileInput").files; 773 for (var i = 0; i < files.length; i++) { 774 var file = files[i]; 775 s += "<p>" + file.name + "</p>"; 776 } 777 var fl = $("filelist"); 778 fl.innerHTML = s; 779 }