aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2017-03-28 20:11:07 +0200
committerArthurHoaro <arthur@hoa.ro>2017-05-25 15:25:04 +0200
commit82e3bb5f06dc531ee1080a0313833791a1c1f3c7 (patch)
tree228456f9b4d95d03e97fc9bc1b5004aa599f95ee
parentaa4797ba3679b847adc895e2f817ac058779a171 (diff)
downloadShaarli-82e3bb5f06dc531ee1080a0313833791a1c1f3c7.tar.gz
Shaarli-82e3bb5f06dc531ee1080a0313833791a1c1f3c7.tar.zst
Shaarli-82e3bb5f06dc531ee1080a0313833791a1c1f3c7.zip
Tag list: use awesomplete for tag auto completion
-rw-r--r--tpl/default/css/shaarli.css4
-rw-r--r--tpl/default/js/shaarli.js62
-rw-r--r--tpl/default/tag.list.html6
3 files changed, 69 insertions, 3 deletions
diff --git a/tpl/default/css/shaarli.css b/tpl/default/css/shaarli.css
index 2eda5df4..28920648 100644
--- a/tpl/default/css/shaarli.css
+++ b/tpl/default/css/shaarli.css
@@ -1098,6 +1098,10 @@ form[name="linkform"].page-form {
1098 color: #7f7f7f; 1098 color: #7f7f7f;
1099} 1099}
1100 1100
1101#taglist .rename-tag-form {
1102 display: none;
1103}
1104
1101#taglist .delete-tag { 1105#taglist .delete-tag {
1102 color: #ac2925; 1106 color: #ac2925;
1103 display: none; 1107 display: none;
diff --git a/tpl/default/js/shaarli.js b/tpl/default/js/shaarli.js
index e19e9001..4ebb7815 100644
--- a/tpl/default/js/shaarli.js
+++ b/tpl/default/js/shaarli.js
@@ -418,6 +418,9 @@ window.onload = function () {
418 * 418 *
419 * TODO: support error code in the backend for AJAX requests 419 * TODO: support error code in the backend for AJAX requests
420 */ 420 */
421 var existingTags = document.querySelector('input[name="taglist"]').value.split(' ');
422 var awesomepletes = [];
423
421 // Display/Hide rename form 424 // Display/Hide rename form
422 var renameTagButtons = document.querySelectorAll('.rename-tag'); 425 var renameTagButtons = document.querySelectorAll('.rename-tag');
423 [].forEach.call(renameTagButtons, function(rename) { 426 [].forEach.call(renameTagButtons, function(rename) {
@@ -425,7 +428,12 @@ window.onload = function () {
425 event.preventDefault(); 428 event.preventDefault();
426 var block = findParent(event.target, 'div', {'class': 'tag-list-item'}); 429 var block = findParent(event.target, 'div', {'class': 'tag-list-item'});
427 var form = block.querySelector('.rename-tag-form'); 430 var form = block.querySelector('.rename-tag-form');
428 form.style.display = form.style.display == 'none' ? 'block' : 'none'; 431 if (form.style.display == 'none' || form.style.display == '') {
432 form.style.display = 'block';
433 } else {
434 form.style.display = 'none';
435 }
436 block.querySelector('input').focus();
429 }); 437 });
430 }); 438 });
431 439
@@ -454,10 +462,18 @@ window.onload = function () {
454 block.setAttribute('data-tag', totag); 462 block.setAttribute('data-tag', totag);
455 input.setAttribute('name', totag); 463 input.setAttribute('name', totag);
456 input.setAttribute('value', totag); 464 input.setAttribute('value', totag);
457 input.parentNode.style.display = 'none'; 465 findParent(input, 'div', {'class': 'rename-tag-form'}).style.display = 'none';
458 block.querySelector('a.tag-link').innerHTML = htmlEntities(totag); 466 block.querySelector('a.tag-link').innerHTML = htmlEntities(totag);
459 block.querySelector('a.tag-link').setAttribute('href', '?searchtags='+ encodeURIComponent(totag)); 467 block.querySelector('a.tag-link').setAttribute('href', '?searchtags='+ encodeURIComponent(totag));
460 block.querySelector('a.rename-tag').setAttribute('href', '?do=changetag&fromtag='+ encodeURIComponent(totag)); 468 block.querySelector('a.rename-tag').setAttribute('href', '?do=changetag&fromtag='+ encodeURIComponent(totag));
469
470 // Refresh awesomplete values
471 for (var key in existingTags) {
472 if (existingTags[key] == fromtag) {
473 existingTags[key] = totag;
474 }
475 }
476 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes);
461 } 477 }
462 }; 478 };
463 xhr.send('renametag=1&fromtag='+ encodeURIComponent(fromtag) +'&totag='+ encodeURIComponent(totag) +'&token='+ token); 479 xhr.send('renametag=1&fromtag='+ encodeURIComponent(fromtag) +'&totag='+ encodeURIComponent(totag) +'&token='+ token);
@@ -468,6 +484,7 @@ window.onload = function () {
468 // Validate input with enter key 484 // Validate input with enter key
469 var renameTagInputs = document.querySelectorAll('.rename-tag-input'); 485 var renameTagInputs = document.querySelectorAll('.rename-tag-input');
470 [].forEach.call(renameTagInputs, function(rename) { 486 [].forEach.call(renameTagInputs, function(rename) {
487
471 rename.addEventListener('keypress', function(event) { 488 rename.addEventListener('keypress', function(event) {
472 if (event.keyCode === 13) { // enter 489 if (event.keyCode === 13) { // enter
473 findParent(event.target, 'div', {'class': 'tag-list-item'}).querySelector('.validate-rename-tag').click(); 490 findParent(event.target, 'div', {'class': 'tag-list-item'}).querySelector('.validate-rename-tag').click();
@@ -497,8 +514,19 @@ window.onload = function () {
497 } 514 }
498 }); 515 });
499 }); 516 });
517
518 updateAwesompleteList('.rename-tag-input', document.querySelector('input[name="taglist"]').value.split(' '), awesomepletes);
500}; 519};
501 520
521/**
522 * Find a parent element according to its tag and its attributes
523 *
524 * @param element Element where to start the search
525 * @param tagName Expected parent tag name
526 * @param attributes Associative array of expected attributes (name=>value).
527 *
528 * @returns Found element or null.
529 */
502function findParent(element, tagName, attributes) 530function findParent(element, tagName, attributes)
503{ 531{
504 while (element) { 532 while (element) {
@@ -522,6 +550,9 @@ function findParent(element, tagName, attributes)
522 return null; 550 return null;
523} 551}
524 552
553/**
554 * Ajax request to refresh the CSRF token.
555 */
525function refreshToken() 556function refreshToken()
526{ 557{
527 var xhr = new XMLHttpRequest(); 558 var xhr = new XMLHttpRequest();
@@ -534,6 +565,33 @@ function refreshToken()
534} 565}
535 566
536/** 567/**
568 * Update awesomplete list of tag for all elements matching the given selector
569 *
570 * @param selector CSS selector
571 * @param tags Array of tags
572 * @param instances List of existing awesomplete instances
573 */
574function updateAwesompleteList(selector, tags, instances)
575{
576 // First load: create Awesomplete instances
577 if (instances.length == 0) {
578 var elements = document.querySelectorAll(selector);
579 [].forEach.call(elements, function (element) {
580 instances.push(new Awesomplete(
581 element,
582 {'list': tags}
583 ));
584 });
585 } else {
586 // Update awesomplete tag list
587 for (var key in instances) {
588 instances[key].list = tags;
589 }
590 }
591 return instances;
592}
593
594/**
537 * html_entities in JS 595 * html_entities in JS
538 * 596 *
539 * @see http://stackoverflow.com/questions/18749591/encode-html-entities-in-javascript 597 * @see http://stackoverflow.com/questions/18749591/encode-html-entities-in-javascript
diff --git a/tpl/default/tag.list.html b/tpl/default/tag.list.html
index 98971051..62e2e7c6 100644
--- a/tpl/default/tag.list.html
+++ b/tpl/default/tag.list.html
@@ -57,7 +57,7 @@
57 {/loop} 57 {/loop}
58 </div> 58 </div>
59 {if="isLoggedIn()===true"} 59 {if="isLoggedIn()===true"}
60 <div class="rename-tag-form pure-u-1" style="display:none;"> 60 <div class="rename-tag-form pure-u-1">
61 <input type="text" name="{$key}" value="{$key}" class="rename-tag-input" /> 61 <input type="text" name="{$key}" value="{$key}" class="rename-tag-input" />
62 <a href="#" class="validate-rename-tag"><i class="fa fa-check"></i></a> 62 <a href="#" class="validate-rename-tag"><i class="fa fa-check"></i></a>
63 </div> 63 </div>
@@ -74,6 +74,10 @@
74 </div> 74 </div>
75</div> 75</div>
76 76
77{if="isLoggedIn()===true"}
78 <input type="hidden" name="taglist" value="{loop="$tags"}{$key} {/loop}"
79{/if}
80
77{include="tag.sort"} 81{include="tag.sort"}
78 82
79{include="page.footer"} 83{include="page.footer"}