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