| | @@ -395,10 +395,11 @@ |
| 395 | 395 | ** Query parameters: |
| 396 | 396 | ** |
| 397 | 397 | ** name=PATH Directory to display. Optional |
| 398 | 398 | ** ci=LABEL Show only files in this check-in. Optional. |
| 399 | 399 | ** re=REGEXP Show only files matching REGEXP. Optional. |
| 400 | +** expand Begin with the tree fully expanded. |
| 400 | 401 | */ |
| 401 | 402 | void page_tree(void){ |
| 402 | 403 | char *zD = fossil_strdup(P("name")); |
| 403 | 404 | int nD = zD ? strlen(zD)+1 : 0; |
| 404 | 405 | const char *zCI = P("ci"); |
| | @@ -413,10 +414,11 @@ |
| 413 | 414 | char *zREx = ""; /* Extra parameters for path hyperlinks */ |
| 414 | 415 | ReCompiled *pRE = 0; /* Compiled regular expression */ |
| 415 | 416 | FileTreeNode *p; /* One line of the tree */ |
| 416 | 417 | FileTree sTree; /* The complete tree of files */ |
| 417 | 418 | HQuery sURI; /* Hyperlink */ |
| 419 | + int startExpanded; /* True to start out with the tree expanded */ |
| 418 | 420 | char *zProjectName = db_get("project-name", 0); |
| 419 | 421 | |
| 420 | 422 | if( strcmp(PD("type",""),"flat")==0 ){ page_dir(); return; } |
| 421 | 423 | memset(&sTree, 0, sizeof(sTree)); |
| 422 | 424 | login_check_credentials(); |
| | @@ -424,10 +426,11 @@ |
| 424 | 426 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| 425 | 427 | style_header("File List"); |
| 426 | 428 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 427 | 429 | pathelementFunc, 0, 0); |
| 428 | 430 | url_initialize(&sURI, "tree"); |
| 431 | + startExpanded = P("expand")!=0; |
| 429 | 432 | |
| 430 | 433 | /* If a regular expression is specified, compile it */ |
| 431 | 434 | zRE = P("re"); |
| 432 | 435 | if( zRE ){ |
| 433 | 436 | re_compile(&pRE, zRE, 0); |
| | @@ -541,12 +544,20 @@ |
| 541 | 544 | @ <h2>%d(nFile) files from all %d(n) check-ins |
| 542 | 545 | @ %s(blob_str(&dirname))</h2> |
| 543 | 546 | } |
| 544 | 547 | |
| 545 | 548 | |
| 546 | | - /* Generate a multi-column table listing the contents of zD[] |
| 547 | | - ** directory. |
| 549 | + /* Generate tree of lists. |
| 550 | + ** |
| 551 | + ** Each file and directory is a list element: <li>. Files have class=file |
| 552 | + ** and if the filename as the suffix "xyz" the file also has class=file-xyz. |
| 553 | + ** Directories have class=dir. The directory specfied by the name= query |
| 554 | + ** parameter (or the top-level directory if there is no name= query parameter) |
| 555 | + ** adds class=subdir. |
| 556 | + ** |
| 557 | + ** The <li> element for directories also contains a sublist <ul> |
| 558 | + ** for the contents of that directory. |
| 548 | 559 | */ |
| 549 | 560 | @ <div class="filetree"><ul> |
| 550 | 561 | if( nD ){ |
| 551 | 562 | char *zLink = href("%s", url_render(&sURI, "name", 0, 0, 0)); |
| 552 | 563 | @ <li class="dir"> |
| | @@ -556,22 +567,23 @@ |
| 556 | 567 | @ <a>%h(zProjectName)</a> |
| 557 | 568 | } |
| 558 | 569 | @ <ul> |
| 559 | 570 | for(p=sTree.pFirst; p; p=p->pNext){ |
| 560 | 571 | if( p->isDir ){ |
| 561 | | - if( nD && strlen(p->zFullName)==nD-1 ){ |
| 572 | + if( p->nFullName==nD-1 ){ |
| 562 | 573 | @ <li class="dir subdir"> |
| 563 | | - }else{ |
| 564 | | - @ <li class="dir"> |
| 565 | | - } |
| 566 | | - if( fossil_strcmp(p->zFullName, zD)==0 ){ |
| 567 | 574 | @ <a>%h(p->zName)</a> |
| 568 | 575 | }else{ |
| 569 | 576 | char *zLink = href("%s", url_render(&sURI, "name", p->zFullName, 0, 0)); |
| 577 | + @ <li class="dir"> |
| 570 | 578 | @ %z(zLink)%h(p->zName)</a> |
| 571 | 579 | } |
| 572 | | - @ <ul> |
| 580 | + if( startExpanded || p->nFullName<=nD ){ |
| 581 | + @ <ul> |
| 582 | + }else{ |
| 583 | + @ <ul style='display:none;'> |
| 584 | + } |
| 573 | 585 | }else{ |
| 574 | 586 | char *zLink; |
| 575 | 587 | if( zCI ){ |
| 576 | 588 | zLink = href("%R/artifact/%s",p->zUuid); |
| 577 | 589 | }else{ |
| | @@ -586,10 +598,45 @@ |
| 586 | 598 | } |
| 587 | 599 | } |
| 588 | 600 | } |
| 589 | 601 | @ </ul> |
| 590 | 602 | @ </ul></div> |
| 603 | + @ <script>(function(){ |
| 604 | + @ function style(elem, prop){ |
| 605 | + @ return window.getComputedStyle(elem).getPropertyValue(prop); |
| 606 | + @ } |
| 607 | + @ |
| 608 | + @ function toggleAll(tree){ |
| 609 | + @ var lists = tree.querySelectorAll('.subdir > ul > li ul'); |
| 610 | + @ var display = 'block'; /* Default action: make all sublists visible */ |
| 611 | + @ for( var i=0; lists[i]; i++ ){ |
| 612 | + @ if( style(lists[i], 'display')!='none'){ |
| 613 | + @ display = 'none'; /* Any already visible - make them all hidden */ |
| 614 | + @ break; |
| 615 | + @ } |
| 616 | + @ } |
| 617 | + @ for( var i=0; lists[i]; i++ ){ |
| 618 | + @ lists[i].style.display = display; |
| 619 | + @ } |
| 620 | + @ } |
| 621 | + @ |
| 622 | + @ var outer_ul = document.querySelector('.filetree > ul'); |
| 623 | + @ outer_ul.querySelector('.subdir > a').style.cursor = 'pointer'; |
| 624 | + @ outer_ul.onclick = function( e ){ |
| 625 | + @ var a = e.target; |
| 626 | + @ if( a.nodeName!='A' ) return; |
| 627 | + @ if( a.parentNode.className.indexOf('subdir')>=0 ){ |
| 628 | + @ toggleAll(outer_ul); |
| 629 | + @ return false; |
| 630 | + @ } |
| 631 | + @ if( style(a.parentNode, 'display')=='inline' ) return; |
| 632 | + @ var ul = a.nextSibling; |
| 633 | + @ while( ul && ul.nodeName!='UL' ) ul = ul.nextSibling; |
| 634 | + @ ul.style.display = style(ul, 'display')=='none' ? 'block' : 'none'; |
| 635 | + @ return false; |
| 636 | + @ } |
| 637 | + @ }())</script> |
| 591 | 638 | style_footer(); |
| 592 | 639 | |
| 593 | 640 | /* We could free memory used by sTree here if we needed to. But |
| 594 | 641 | ** the process is about to exit, so doing so would not really accomplish |
| 595 | 642 | ** anything useful. */ |
| 596 | 643 | |