]>
git.immae.eu Git - github/shaarli/Shaarli.git/blob - tpl/default/js/shaarli.js
1 /** @licstart The following is the entire license notice for the
2 * JavaScript code in this page.
4 * Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
5 * (c) 2011-2017 The Shaarli Community, see AUTHORS
7 * This software is provided 'as-is', without any express or implied warranty.
8 * In no event will the authors be held liable for any damages arising from
9 * the use of this software.
11 * Permission is granted to anyone to use this software for any purpose,
12 * including commercial applications, and to alter it and redistribute it
13 * freely, subject to the following restrictions:
15 * 1. The origin of this software must not be misrepresented; you must not
16 * claim that you wrote the original software. If you use this software
17 * in a product, an acknowledgment in the product documentation would
18 * be appreciated but is not required.
20 * 2. Altered source versions must be plainly marked as such, and must
21 * not be misrepresented as being the original software.
23 * 3. This notice may not be removed or altered from any source distribution.
25 * @licend The above is the entire license notice
26 * for the JavaScript code in this page.
29 window
.onload = function () {
32 * Retrieve an element up in the tree from its class name.
34 function getParentByClass(el
, className
) {
35 var p
= el
.parentNode
;
36 if (p
== null || p
.classList
.contains(className
)) {
39 return getParentByClass(p
, className
);
44 * Handle responsive menu.
45 * Source: http://purecss.io/layouts/tucked-menu-vertical/
47 (function (window
, document
) {
48 var menu
= document
.getElementById('shaarli-menu'),
49 WINDOW_CHANGE_EVENT
= ('onorientationchange' in window
) ? 'orientationchange':'resize';
51 function toggleHorizontal() {
53 document
.getElementById('shaarli-menu').querySelectorAll('.menu-transform'),
55 el
.classList
.toggle('pure-menu-horizontal');
60 function toggleMenu() {
61 // set timeout so that the panel has a chance to roll up
62 // before the menu switches states
63 if (menu
.classList
.contains('open')) {
64 setTimeout(toggleHorizontal
, 500);
69 menu
.classList
.toggle('open');
70 document
.getElementById('menu-toggle').classList
.toggle('x');
73 function closeMenu() {
74 if (menu
.classList
.contains('open')) {
79 var menuToggle
= document
.getElementById('menu-toggle');
80 if (menuToggle
!= null) {
81 menuToggle
.addEventListener('click', function (e
) {
86 window
.addEventListener(WINDOW_CHANGE_EVENT
, closeMenu
);
87 })(this, this.document
);
90 * Fold/Expand shaares description and thumbnail.
92 var foldAllButtons
= document
.getElementsByClassName('fold-all');
93 var foldButtons
= document
.getElementsByClassName('fold-button');
95 [].forEach
.call(foldButtons
, function (foldButton
) {
96 // Retrieve description
97 var description
= null;
99 var linklistItem
= getParentByClass(foldButton
, 'linklist-item');
100 if (linklistItem
!= null) {
101 description
= linklistItem
.querySelector('.linklist-item-description');
102 thumbnail
= linklistItem
.querySelector('.linklist-item-thumbnail');
103 if (description
!= null || thumbnail
!= null) {
104 foldButton
.style
.display
= 'inline';
108 foldButton
.addEventListener('click', function (event
) {
109 event
.preventDefault();
110 toggleFold(event
.target
, description
, thumbnail
);
114 if (foldAllButtons
!= null) {
115 [].forEach
.call(foldAllButtons
, function (foldAllButton
) {
116 foldAllButton
.addEventListener('click', function (event
) {
117 event
.preventDefault();
118 var state
= foldAllButton
.firstElementChild
.getAttribute('class').indexOf('down') != -1 ? 'down' : 'up';
119 [].forEach
.call(foldButtons
, function (foldButton
) {
120 if (foldButton
.firstElementChild
.classList
.contains('fa-chevron-up') && state
== 'down'
121 || foldButton
.firstElementChild
.classList
.contains('fa-chevron-down') && state
== 'up'
125 // Retrieve description
126 var description
= null;
127 var thumbnail
= null;
128 var linklistItem
= getParentByClass(foldButton
, 'linklist-item');
129 if (linklistItem
!= null) {
130 description
= linklistItem
.querySelector('.linklist-item-description');
131 thumbnail
= linklistItem
.querySelector('.linklist-item-thumbnail');
132 if (description
!= null || thumbnail
!= null) {
133 foldButton
.style
.display
= 'inline';
137 toggleFold(foldButton
.firstElementChild
, description
, thumbnail
);
139 foldAllButton
.firstElementChild
.classList
.toggle('fa-chevron-down');
140 foldAllButton
.firstElementChild
.classList
.toggle('fa-chevron-up');
145 function toggleFold(button
, description
, thumb
)
147 // Switch fold/expand - up = fold
148 if (button
.classList
.contains('fa-chevron-up')) {
149 button
.title
= 'Expand';
150 if (description
!= null) {
151 description
.style
.display
= 'none';
154 thumb
.style
.display
= 'none';
158 button
.title
= 'Fold';
159 if (description
!= null) {
160 description
.style
.display
= 'block';
163 thumb
.style
.display
= 'block';
166 button
.classList
.toggle('fa-chevron-down');
167 button
.classList
.toggle('fa-chevron-up');
171 * Confirmation message before deletion.
173 var deleteLinks
= document
.querySelectorAll('.confirm-delete');
174 [].forEach
.call(deleteLinks
, function(deleteLink
) {
175 deleteLink
.addEventListener('click', function(event
) {
176 if(! confirm('Are you sure you want to delete this link ?')) {
177 event
.preventDefault();
185 var closeLinks
= document
.querySelectorAll('.pure-alert-close');
186 [].forEach
.call(closeLinks
, function(closeLink
) {
187 closeLink
.addEventListener('click', function(event
) {
188 var alert
= getParentByClass(event
.target
, 'pure-alert-closable');
189 alert
.style
.display
= 'none';
194 * New version dismiss.
195 * Hide the message for one week using localStorage.
197 var newVersionDismiss
= document
.getElementById('new-version-dismiss');
198 var newVersionMessage
= document
.querySelector('.new-version-message');
199 if (newVersionMessage
!= null
200 && localStorage
.getItem('newVersionDismiss') != null
201 && parseInt(localStorage
.getItem('newVersionDismiss')) + 7*24*60*60*1000 > (new Date()).getTime()
203 newVersionMessage
.style
.display
= 'none';
205 if (newVersionDismiss
!= null) {
206 newVersionDismiss
.addEventListener('click', function () {
207 localStorage
.setItem('newVersionDismiss', (new Date()).getTime());
211 var hiddenReturnurl
= document
.getElementsByName('returnurl');
212 if (hiddenReturnurl
!= null) {
213 hiddenReturnurl
.value
= window
.location
.href
;
217 * Autofocus text fields
220 let autofocusElements
= document
.querySelectorAll('.autofocus');
221 for (let autofocusElement
of autofocusElements
) {
222 if (autofocusElement
.value
== '') {
223 autofocusElement
.focus();
229 * Handle sub menus/forms
231 var openers
= document
.getElementsByClassName('subheader-opener');
232 if (openers
!= null) {
233 [].forEach
.call(openers
, function(opener
) {
234 opener
.addEventListener('click', function(event
) {
235 event
.preventDefault();
237 var id
= opener
.getAttribute('data-open-id');
238 var sub
= document
.getElementById(id
);
241 [].forEach
.call(document
.getElementsByClassName('subheader-form'), function (element
) {
242 if (element
!= sub
) {
243 removeClass(element
, 'open')
247 sub
.classList
.toggle('open');
253 function removeClass(element
, classname
) {
254 element
.className
= element
.className
.replace(new RegExp('(?:^|\\s)'+ classname
+ '(?:\\s|$)'), ' ');
258 * Remove CSS target padding (for fixed bar)
260 if (location
.hash
!= '') {
261 var anchor
= document
.getElementById(location
.hash
.substr(1));
262 if (anchor
!= null) {
263 var padsize
= anchor
.clientHeight
;
264 this.window
.scroll(0, this.window
.scrollY
- padsize
);
265 anchor
.style
.paddingTop
= 0;
272 var description
= document
.getElementById('lf_description');
273 var observe = function (element
, event
, handler
) {
274 element
.addEventListener(event
, handler
, false);
278 description
.style
.height
= 'auto';
279 description
.style
.height
= description
.scrollHeight
+10+'px';
281 /* 0-timeout to get the already changed text */
282 function delayedResize () {
283 window
.setTimeout(resize
, 0);
285 observe(description
, 'change', resize
);
286 observe(description
, 'cut', delayedResize
);
287 observe(description
, 'paste', delayedResize
);
288 observe(description
, 'drop', delayedResize
);
289 observe(description
, 'keydown', delayedResize
);
294 if (description
!= null) {
296 // Submit editlink form with CTRL + Enter in the text area.
297 description
.addEventListener('keydown', function (event
) {
298 if (event
.ctrlKey
&& event
.keyCode
=== 13) {
299 document
.getElementById('button-save-edit').click();
305 * Awesomplete trigger.
307 var tags
= document
.getElementById('lf_tags');
309 awesompleteUniqueTag('#lf_tags');
315 var picwall
= document
.getElementById('picwall_container');
316 if (picwall
!= null) {
317 var bLazy
= new Blazy();
323 var bookmarkletLinks
= document
.querySelectorAll('.bookmarklet-link');
324 var bkmMessage
= document
.getElementById('bookmarklet-alert');
325 [].forEach
.call(bookmarkletLinks
, function(link
) {
326 link
.addEventListener('click', function(event
) {
327 event
.preventDefault();
328 alert(bkmMessage
.value
);
335 var ffButton
= document
.getElementById('ff-social-button');
336 if (ffButton
!= null) {
337 ffButton
.addEventListener('click', function(event
) {
338 activateFirefoxSocial(event
.target
);
345 var orderPA
= document
.querySelectorAll('.order');
346 [].forEach
.call(orderPA
, function(link
) {
347 link
.addEventListener('click', function(event
) {
348 event
.preventDefault();
349 if (event
.target
.classList
.contains('order-up')) {
350 return orderUp(event
.target
.parentNode
.parentNode
.getAttribute('data-order'));
351 } else if (event
.target
.classList
.contains('order-down')) {
352 return orderDown(event
.target
.parentNode
.parentNode
.getAttribute('data-order'));
357 var continent
= document
.getElementById('continent');
358 var city
= document
.getElementById('city');
359 if (continent
!= null && city
!= null) {
360 continent
.addEventListener('change', function(event
) {
361 hideTimezoneCities(city
, continent
.options
[continent
.selectedIndex
].value
, true);
363 hideTimezoneCities(city
, continent
.options
[continent
.selectedIndex
].value
, false);
367 function activateFirefoxSocial(node
) {
368 var loc
= location
.href
;
369 var baseURL
= loc
.substring(0, loc
.lastIndexOf("/"));
371 // Keeping the data separated (ie. not in the DOM) so that it's maintainable and diffable.
373 name: "{$shaarlititle}",
374 description: "The personal, minimalist, super-fast, database free, bookmarking service by the Shaarli community.",
378 iconURL: baseURL
+ "/images/favicon.ico",
379 icon32URL: baseURL
+ "/images/favicon.ico",
380 icon64URL: baseURL
+ "/images/favicon.ico",
382 shareURL: baseURL
+ "{noparse}?post=%{url}&title=%{title}&description=%{text}&source=firefoxsocialapi{/noparse}",
385 node
.setAttribute("data-service", JSON
.stringify(data
));
387 var activate
= new CustomEvent("ActivateSocialFeature");
388 node
.dispatchEvent(activate
);
392 * Add the class 'hidden' to city options not attached to the current selected continent.
394 * @param cities List of <option> elements
395 * @param currentContinent Current selected continent
396 * @param reset Set to true to reset the selected value
398 function hideTimezoneCities(cities
, currentContinent
, reset
= false) {
400 [].forEach
.call(cities
, function(option
) {
401 if (option
.getAttribute('data-continent') != currentContinent
) {
402 option
.className
= 'hidden';
404 option
.className
= '';
405 if (reset
=== true && first
=== true) {
406 option
.setAttribute('selected', 'selected');