aboutsummaryrefslogtreecommitdiffhomepage
path: root/assets/default
diff options
context:
space:
mode:
Diffstat (limited to 'assets/default')
-rw-r--r--assets/default/js/base.js73
-rw-r--r--assets/default/scss/shaarli.scss199
2 files changed, 247 insertions, 25 deletions
diff --git a/assets/default/js/base.js b/assets/default/js/base.js
index be986ae0..dd532bb7 100644
--- a/assets/default/js/base.js
+++ b/assets/default/js/base.js
@@ -1,4 +1,5 @@
1import Awesomplete from 'awesomplete'; 1import Awesomplete from 'awesomplete';
2import he from 'he';
2 3
3/** 4/**
4 * Find a parent element according to its tag and its attributes 5 * Find a parent element according to its tag and its attributes
@@ -41,19 +42,21 @@ function refreshToken(basePath, callback) {
41 xhr.send(); 42 xhr.send();
42} 43}
43 44
44function createAwesompleteInstance(element, tags = []) { 45function createAwesompleteInstance(element, separator, tags = []) {
45 const awesome = new Awesomplete(Awesomplete.$(element)); 46 const awesome = new Awesomplete(Awesomplete.$(element));
46 // Tags are separated by a space 47
47 awesome.filter = (text, input) => Awesomplete.FILTER_CONTAINS(text, input.match(/[^ ]*$/)[0]); 48 // Tags are separated by separator
49 awesome.filter = (text, input) => Awesomplete.FILTER_CONTAINS(text, input.match(new RegExp(`[^${separator}]*$`))[0]);
48 // Insert new selected tag in the input 50 // Insert new selected tag in the input
49 awesome.replace = (text) => { 51 awesome.replace = (text) => {
50 const before = awesome.input.value.match(/^.+ \s*|/)[0]; 52 const before = awesome.input.value.match(new RegExp(`^.+${separator}+|`))[0];
51 awesome.input.value = `${before}${text} `; 53 awesome.input.value = `${before}${text}${separator}`;
52 }; 54 };
53 // Highlight found items 55 // Highlight found items
54 awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(/[^ ]*$/)[0]); 56 awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(new RegExp(`[^${separator}]*$`))[0]);
55 // Don't display already selected items 57 // Don't display already selected items
56 const reg = /(\w+) /g; 58 // WARNING: pseudo classes does not seem to work with string litterals...
59 const reg = new RegExp(`([^${separator}]+)${separator}`, 'g');
57 let match; 60 let match;
58 awesome.data = (item, input) => { 61 awesome.data = (item, input) => {
59 while ((match = reg.exec(input))) { 62 while ((match = reg.exec(input))) {
@@ -77,13 +80,14 @@ function createAwesompleteInstance(element, tags = []) {
77 * @param selector CSS selector 80 * @param selector CSS selector
78 * @param tags Array of tags 81 * @param tags Array of tags
79 * @param instances List of existing awesomplete instances 82 * @param instances List of existing awesomplete instances
83 * @param separator Tags separator character
80 */ 84 */
81function updateAwesompleteList(selector, tags, instances) { 85function updateAwesompleteList(selector, tags, instances, separator) {
82 if (instances.length === 0) { 86 if (instances.length === 0) {
83 // First load: create Awesomplete instances 87 // First load: create Awesomplete instances
84 const elements = document.querySelectorAll(selector); 88 const elements = document.querySelectorAll(selector);
85 [...elements].forEach((element) => { 89 [...elements].forEach((element) => {
86 instances.push(createAwesompleteInstance(element, tags)); 90 instances.push(createAwesompleteInstance(element, separator, tags));
87 }); 91 });
88 } else { 92 } else {
89 // Update awesomplete tag list 93 // Update awesomplete tag list
@@ -96,15 +100,6 @@ function updateAwesompleteList(selector, tags, instances) {
96} 100}
97 101
98/** 102/**
99 * html_entities in JS
100 *
101 * @see http://stackoverflow.com/questions/18749591/encode-html-entities-in-javascript
102 */
103function htmlEntities(str) {
104 return str.replace(/[\u00A0-\u9999<>&]/gim, (i) => `&#${i.charCodeAt(0)};`);
105}
106
107/**
108 * Add the class 'hidden' to city options not attached to the current selected continent. 103 * Add the class 'hidden' to city options not attached to the current selected continent.
109 * 104 *
110 * @param cities List of <option> elements 105 * @param cities List of <option> elements
@@ -222,6 +217,8 @@ function init(description) {
222 217
223(() => { 218(() => {
224 const basePath = document.querySelector('input[name="js_base_path"]').value; 219 const basePath = document.querySelector('input[name="js_base_path"]').value;
220 const tagsSeparatorElement = document.querySelector('input[name="tags_separator"]');
221 const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || ' ' : ' ';
225 222
226 /** 223 /**
227 * Handle responsive menu. 224 * Handle responsive menu.
@@ -302,7 +299,8 @@ function init(description) {
302 const deleteLinks = document.querySelectorAll('.confirm-delete'); 299 const deleteLinks = document.querySelectorAll('.confirm-delete');
303 [...deleteLinks].forEach((deleteLink) => { 300 [...deleteLinks].forEach((deleteLink) => {
304 deleteLink.addEventListener('click', (event) => { 301 deleteLink.addEventListener('click', (event) => {
305 if (!confirm(document.getElementById('translation-delete-link').innerHTML)) { 302 const type = event.currentTarget.getAttribute('data-type') || 'link';
303 if (!confirm(document.getElementById(`translation-delete-${type}`).innerHTML)) {
306 event.preventDefault(); 304 event.preventDefault();
307 } 305 }
308 }); 306 });
@@ -569,7 +567,7 @@ function init(description) {
569 input.setAttribute('name', totag); 567 input.setAttribute('name', totag);
570 input.setAttribute('value', totag); 568 input.setAttribute('value', totag);
571 findParent(input, 'div', { class: 'rename-tag-form' }).style.display = 'none'; 569 findParent(input, 'div', { class: 'rename-tag-form' }).style.display = 'none';
572 block.querySelector('a.tag-link').innerHTML = htmlEntities(totag); 570 block.querySelector('a.tag-link').innerHTML = he.encode(totag);
573 block 571 block
574 .querySelector('a.tag-link') 572 .querySelector('a.tag-link')
575 .setAttribute('href', `${basePath}/?searchtags=${encodeURIComponent(totag)}`); 573 .setAttribute('href', `${basePath}/?searchtags=${encodeURIComponent(totag)}`);
@@ -582,7 +580,7 @@ function init(description) {
582 580
583 // Refresh awesomplete values 581 // Refresh awesomplete values
584 existingTags = existingTags.map((tag) => (tag === fromtag ? totag : tag)); 582 existingTags = existingTags.map((tag) => (tag === fromtag ? totag : tag));
585 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes); 583 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes, tagsSeparator);
586 } 584 }
587 }; 585 };
588 xhr.send(`renametag=1&fromtag=${fromtagUrl}&totag=${encodeURIComponent(totag)}&token=${refreshedToken}`); 586 xhr.send(`renametag=1&fromtag=${fromtagUrl}&totag=${encodeURIComponent(totag)}&token=${refreshedToken}`);
@@ -622,14 +620,14 @@ function init(description) {
622 refreshToken(basePath); 620 refreshToken(basePath);
623 621
624 existingTags = existingTags.filter((tagItem) => tagItem !== tag); 622 existingTags = existingTags.filter((tagItem) => tagItem !== tag);
625 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes); 623 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes, tagsSeparator);
626 } 624 }
627 }); 625 });
628 }); 626 });
629 627
630 const autocompleteFields = document.querySelectorAll('input[data-multiple]'); 628 const autocompleteFields = document.querySelectorAll('input[data-multiple]');
631 [...autocompleteFields].forEach((autocompleteField) => { 629 [...autocompleteFields].forEach((autocompleteField) => {
632 awesomepletes.push(createAwesompleteInstance(autocompleteField)); 630 awesomepletes.push(createAwesompleteInstance(autocompleteField, tagsSeparator));
633 }); 631 });
634 632
635 const exportForm = document.querySelector('#exportform'); 633 const exportForm = document.querySelector('#exportform');
@@ -642,4 +640,33 @@ function init(description) {
642 }); 640 });
643 }); 641 });
644 } 642 }
643
644 const bulkCreationButton = document.querySelector('.addlink-batch-show-more-block');
645 if (bulkCreationButton != null) {
646 const toggleBulkCreationVisibility = (showMoreBlockElement, formElement) => {
647 if (bulkCreationButton.classList.contains('pure-u-0')) {
648 showMoreBlockElement.classList.remove('pure-u-0');
649 formElement.classList.add('pure-u-0');
650 } else {
651 showMoreBlockElement.classList.add('pure-u-0');
652 formElement.classList.remove('pure-u-0');
653 }
654 };
655
656 const bulkCreationForm = document.querySelector('.addlink-batch-form-block');
657
658 toggleBulkCreationVisibility(bulkCreationButton, bulkCreationForm);
659 bulkCreationButton.querySelector('a').addEventListener('click', (e) => {
660 e.preventDefault();
661 toggleBulkCreationVisibility(bulkCreationButton, bulkCreationForm);
662 });
663
664 // Force to send falsy value if the checkbox is not checked.
665 const privateButton = bulkCreationForm.querySelector('input[type="checkbox"][name="private"]');
666 const privateHiddenButton = bulkCreationForm.querySelector('input[type="hidden"][name="private"]');
667 privateButton.addEventListener('click', () => {
668 privateHiddenButton.disabled = !privateHiddenButton.disabled;
669 });
670 privateHiddenButton.disabled = privateButton.checked;
671 }
645})(); 672})();
diff --git a/assets/default/scss/shaarli.scss b/assets/default/scss/shaarli.scss
index a528adb0..cc8ccc1e 100644
--- a/assets/default/scss/shaarli.scss
+++ b/assets/default/scss/shaarli.scss
@@ -139,6 +139,16 @@ body,
139 } 139 }
140} 140}
141 141
142.page-form,
143.pure-alert {
144 code {
145 display: inline-block;
146 padding: 0 2px;
147 color: $dark-grey;
148 background-color: var(--background-color);
149 }
150}
151
142// Make pure-extras alert closable. 152// Make pure-extras alert closable.
143.pure-alert-closable { 153.pure-alert-closable {
144 .fa-times { 154 .fa-times {
@@ -671,6 +681,10 @@ body,
671 content: ''; 681 content: '';
672 } 682 }
673 } 683 }
684
685 .search-highlight {
686 background-color: yellow;
687 }
674} 688}
675 689
676.linklist-item-buttons { 690.linklist-item-buttons {
@@ -1019,6 +1033,10 @@ body,
1019 &.button-red { 1033 &.button-red {
1020 background: $red; 1034 background: $red;
1021 } 1035 }
1036
1037 &.button-grey {
1038 background: $light-grey;
1039 }
1022 } 1040 }
1023 1041
1024 .submit-buttons { 1042 .submit-buttons {
@@ -1043,7 +1061,7 @@ body,
1043 } 1061 }
1044 1062
1045 table { 1063 table {
1046 margin: auto; 1064 margin: 10px auto 25px auto;
1047 width: 90%; 1065 width: 90%;
1048 1066
1049 .order { 1067 .order {
@@ -1079,6 +1097,11 @@ body,
1079 position: absolute; 1097 position: absolute;
1080 right: 5%; 1098 right: 5%;
1081 } 1099 }
1100
1101 &.button-grey {
1102 position: absolute;
1103 left: 5%;
1104 }
1082 } 1105 }
1083 } 1106 }
1084 } 1107 }
@@ -1253,11 +1276,15 @@ form {
1253 margin: 70px 0 25px; 1276 margin: 70px 0 25px;
1254 } 1277 }
1255 1278
1279 a {
1280 color: var(--main-color);
1281 }
1282
1256 pre { 1283 pre {
1257 margin: 0 20%; 1284 margin: 0 20%;
1258 padding: 20px 0; 1285 padding: 20px 0;
1259 text-align: left; 1286 text-align: left;
1260 line-height: .7em; 1287 line-height: 1em;
1261 } 1288 }
1262} 1289}
1263 1290
@@ -1269,6 +1296,57 @@ form {
1269 } 1296 }
1270} 1297}
1271 1298
1299.loading-input {
1300 position: relative;
1301
1302 @keyframes around {
1303 0% {
1304 transform: rotate(0deg);
1305 }
1306
1307 100% {
1308 transform: rotate(360deg);
1309 }
1310 }
1311
1312 .icon-container {
1313 position: absolute;
1314 right: 60px;
1315 top: calc(50% - 10px);
1316 }
1317
1318 .loader {
1319 position: relative;
1320 height: 20px;
1321 width: 20px;
1322 display: inline-block;
1323 animation: around 5.4s infinite;
1324
1325 &::after,
1326 &::before {
1327 content: "";
1328 background: $form-input-background;
1329 position: absolute;
1330 display: inline-block;
1331 width: 100%;
1332 height: 100%;
1333 border-width: 2px;
1334 border-color: #333 #333 transparent transparent;
1335 border-style: solid;
1336 border-radius: 20px;
1337 box-sizing: border-box;
1338 top: 0;
1339 left: 0;
1340 animation: around 0.7s ease-in-out infinite;
1341 }
1342
1343 &::after {
1344 animation: around 0.7s ease-in-out 0.1s infinite;
1345 background: transparent;
1346 }
1347 }
1348}
1349
1272// LOGIN 1350// LOGIN
1273.login-form-container { 1351.login-form-container {
1274 .remember-me { 1352 .remember-me {
@@ -1641,6 +1719,123 @@ form {
1641 } 1719 }
1642} 1720}
1643 1721
1722// SERVER PAGE
1723
1724.server-tables-page,
1725.server-tables {
1726 .window-subtitle {
1727 &::before {
1728 display: block;
1729 margin: 8px auto;
1730 background: linear-gradient(to right, var(--background-color), $dark-grey, var(--background-color));
1731 width: 50%;
1732 height: 1px;
1733 content: '';
1734 }
1735 }
1736
1737 .server-row {
1738 p {
1739 height: 25px;
1740 padding: 0 10px;
1741 }
1742 }
1743
1744 .server-label {
1745 text-align: right;
1746 font-weight: bold;
1747 }
1748
1749 i {
1750 &.fa-color-green {
1751 color: $main-green;
1752 }
1753
1754 &.fa-color-orange {
1755 color: $orange;
1756 }
1757
1758 &.fa-color-red {
1759 color: $red;
1760 }
1761 }
1762
1763 @media screen and (max-width: 64em) {
1764 .server-label {
1765 text-align: center;
1766 }
1767
1768 .server-row {
1769 p {
1770 text-align: center;
1771 }
1772 }
1773 }
1774}
1775
1776// Batch creation
1777input[name='save_edit_batch'] {
1778 @extend %page-form-button;
1779}
1780
1781.addlink-batch-show-more {
1782 display: flex;
1783 align-items: center;
1784 margin: 20px 0 8px;
1785
1786 a {
1787 color: var(--main-color);
1788 text-decoration: none;
1789 }
1790
1791 &::before,
1792 &::after {
1793 content: "";
1794 flex-grow: 1;
1795 background: rgba(0, 0, 0, 0.35);
1796 height: 1px;
1797 font-size: 0;
1798 line-height: 0;
1799 }
1800
1801 &::before {
1802 margin: 0 16px 0 0;
1803 }
1804
1805 &::after {
1806 margin: 0 0 0 16px;
1807 }
1808}
1809
1810.dark-layer {
1811 display: none;
1812 position: fixed;
1813 height: 100%;
1814 width: 100%;
1815 z-index: 998;
1816 background-color: rgba(0, 0, 0, .75);
1817 color: #fff;
1818
1819 .screen-center {
1820 display: flex;
1821 flex-direction: column;
1822 justify-content: center;
1823 align-items: center;
1824 text-align: center;
1825 min-height: 100vh;
1826 }
1827
1828 .progressbar {
1829 width: 33%;
1830 }
1831}
1832
1833.addlink-batch-form-block {
1834 .pure-alert {
1835 margin: 25px 0 0 0;
1836 }
1837}
1838
1644// Print rules 1839// Print rules
1645@media print { 1840@media print {
1646 .shaarli-menu { 1841 .shaarli-menu {