]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - tpl/default/js/shaarli.js
Remove inline JS and add LibreJS headers in JS files
[github/shaarli/Shaarli.git] / tpl / default / js / shaarli.js
1 /** @licstart The following is the entire license notice for the
2 * JavaScript code in this page.
3 *
4 * Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
5 * (c) 2011-2017 The Shaarli Community, see AUTHORS
6 *
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.
10 *
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:
14 *
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.
19 *
20 * 2. Altered source versions must be plainly marked as such, and must
21 * not be misrepresented as being the original software.
22 *
23 * 3. This notice may not be removed or altered from any source distribution.
24 *
25 * @licend The above is the entire license notice
26 * for the JavaScript code in this page.
27 */
28
29 window.onload = function () {
30
31 /**
32 * Retrieve an element up in the tree from its class name.
33 */
34 function getParentByClass(el, className) {
35 var p = el.parentNode;
36 if (p == null || p.classList.contains(className)) {
37 return p;
38 }
39 return getParentByClass(p, className);
40 }
41
42
43 /**
44 * Handle responsive menu.
45 * Source: http://purecss.io/layouts/tucked-menu-vertical/
46 */
47 (function (window, document) {
48 var menu = document.getElementById('shaarli-menu'),
49 WINDOW_CHANGE_EVENT = ('onorientationchange' in window) ? 'orientationchange':'resize';
50
51 function toggleHorizontal() {
52 [].forEach.call(
53 document.getElementById('shaarli-menu').querySelectorAll('.menu-transform'),
54 function(el){
55 el.classList.toggle('pure-menu-horizontal');
56 }
57 );
58 };
59
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);
65 }
66 else {
67 toggleHorizontal();
68 }
69 menu.classList.toggle('open');
70 document.getElementById('menu-toggle').classList.toggle('x');
71 };
72
73 function closeMenu() {
74 if (menu.classList.contains('open')) {
75 toggleMenu();
76 }
77 }
78
79 document.getElementById('menu-toggle').addEventListener('click', function (e) {
80 toggleMenu();
81 });
82
83 window.addEventListener(WINDOW_CHANGE_EVENT, closeMenu);
84 })(this, this.document);
85
86 /**
87 * Fold/Expand shaares description and thumbnail.
88 */
89 var foldAllButtons = document.getElementsByClassName('fold-all');
90 var foldButtons = document.getElementsByClassName('fold-button');
91
92 [].forEach.call(foldButtons, function (foldButton) {
93 // Retrieve description
94 var description = null;
95 var thumbnail = null;
96 var linklistItem = getParentByClass(foldButton, 'linklist-item');
97 if (linklistItem != null) {
98 description = linklistItem.querySelector('.linklist-item-description');
99 thumbnail = linklistItem.querySelector('.linklist-item-thumbnail');
100 if (description != null || thumbnail != null) {
101 foldButton.style.display = 'inline';
102 }
103 }
104
105 foldButton.addEventListener('click', function (event) {
106 event.preventDefault();
107 toggleFold(event.target, description, thumbnail);
108 });
109 });
110
111 if (foldAllButtons != null) {
112 [].forEach.call(foldAllButtons, function (foldAllButton) {
113 foldAllButton.addEventListener('click', function (event) {
114 event.preventDefault();
115 var state = foldAllButton.firstElementChild.getAttribute('class').indexOf('down') != -1 ? 'down' : 'up';
116 [].forEach.call(foldButtons, function (foldButton) {
117 if (foldButton.firstElementChild.classList.contains('fa-chevron-up') && state == 'down'
118 || foldButton.firstElementChild.classList.contains('fa-chevron-down') && state == 'up'
119 ) {
120 return;
121 }
122 // Retrieve description
123 var description = null;
124 var thumbnail = null;
125 var linklistItem = getParentByClass(foldButton, 'linklist-item');
126 if (linklistItem != null) {
127 description = linklistItem.querySelector('.linklist-item-description');
128 thumbnail = linklistItem.querySelector('.linklist-item-thumbnail');
129 if (description != null || thumbnail != null) {
130 foldButton.style.display = 'inline';
131 }
132 }
133
134 toggleFold(foldButton.firstElementChild, description, thumbnail);
135 });
136 foldAllButton.firstElementChild.classList.toggle('fa-chevron-down');
137 foldAllButton.firstElementChild.classList.toggle('fa-chevron-up');
138 });
139 });
140 }
141
142 function toggleFold(button, description, thumb)
143 {
144 // Switch fold/expand - up = fold
145 if (button.classList.contains('fa-chevron-up')) {
146 button.title = 'Expand';
147 if (description != null) {
148 description.style.display = 'none';
149 }
150 if (thumb != null) {
151 thumb.style.display = 'none';
152 }
153 }
154 else {
155 button.title = 'Fold';
156 if (description != null) {
157 description.style.display = 'block';
158 }
159 if (thumb != null) {
160 thumb.style.display = 'block';
161 }
162 }
163 button.classList.toggle('fa-chevron-down');
164 button.classList.toggle('fa-chevron-up');
165 }
166
167 /**
168 * Confirmation message before deletion.
169 */
170 var deleteLinks = document.querySelectorAll('.confirm-delete');
171 [].forEach.call(deleteLinks, function(deleteLink) {
172 deleteLink.addEventListener('click', function(event) {
173 if(! confirm('Are you sure you want to delete this link ?')) {
174 event.preventDefault();
175 }
176 });
177 });
178
179 /**
180 * Close alerts
181 */
182 var closeLinks = document.querySelectorAll('.pure-alert-close');
183 [].forEach.call(closeLinks, function(closeLink) {
184 closeLink.addEventListener('click', function(event) {
185 var alert = getParentByClass(event.target, 'pure-alert-closable');
186 alert.style.display = 'none';
187 });
188 });
189
190 /**
191 * New version dismiss.
192 * Hide the message for one week using localStorage.
193 */
194 var newVersionDismiss = document.getElementById('new-version-dismiss');
195 var newVersionMessage = document.querySelector('.new-version-message');
196 if (newVersionMessage != null
197 && localStorage.getItem('newVersionDismiss') != null
198 && parseInt(localStorage.getItem('newVersionDismiss')) + 7*24*60*60*1000 > (new Date()).getTime()
199 ) {
200 newVersionMessage.style.display = 'none';
201 }
202 if (newVersionDismiss != null) {
203 newVersionDismiss.addEventListener('click', function () {
204 localStorage.setItem('newVersionDismiss', (new Date()).getTime());
205 });
206 }
207
208 var hiddenReturnurl = document.getElementsByName('returnurl');
209 if (hiddenReturnurl != null) {
210 hiddenReturnurl.value = window.location.href;
211 }
212
213 /**
214 * Autofocus text fields
215 */
216 // ES6 syntax
217 let autofocusElements = document.querySelectorAll('.autofocus');
218 for (let autofocusElement of autofocusElements) {
219 if (autofocusElement.value == '') {
220 autofocusElement.focus();
221 break;
222 }
223 }
224
225 /**
226 * Handle sub menus/forms
227 */
228 var openers = document.getElementsByClassName('subheader-opener');
229 if (openers != null) {
230 [].forEach.call(openers, function(opener) {
231 opener.addEventListener('click', function(event) {
232 event.preventDefault();
233
234 var id = opener.getAttribute('data-open-id');
235 var sub = document.getElementById(id);
236
237 if (sub != null) {
238 [].forEach.call(document.getElementsByClassName('subheader-form'), function (element) {
239 if (element != sub) {
240 removeClass(element, 'open')
241 }
242 });
243
244 sub.classList.toggle('open');
245 }
246 });
247 });
248 }
249
250 function removeClass(element, classname) {
251 element.className = element.className.replace(new RegExp('(?:^|\\s)'+ classname + '(?:\\s|$)'), ' ');
252 }
253
254 /**
255 * Remove CSS target padding (for fixed bar)
256 */
257 if (location.hash != '') {
258 var anchor = document.querySelector(location.hash);
259 if (anchor != null) {
260 var padsize = anchor.clientHeight;
261 console.log(document.querySelector(location.hash).clientHeight);
262 this.window.scroll(0, this.window.scrollY - padsize);
263 anchor.style.paddingTop = 0;
264 }
265 }
266
267 /**
268 * Text area resizer
269 */
270 var description = document.getElementById('lf_description');
271 var observe = function (element, event, handler) {
272 element.addEventListener(event, handler, false);
273 };
274 function init () {
275 function resize () {
276 description.style.height = 'auto';
277 description.style.height = description.scrollHeight+10+'px';
278 }
279 /* 0-timeout to get the already changed text */
280 function delayedResize () {
281 window.setTimeout(resize, 0);
282 }
283 observe(description, 'change', resize);
284 observe(description, 'cut', delayedResize);
285 observe(description, 'paste', delayedResize);
286 observe(description, 'drop', delayedResize);
287 observe(description, 'keydown', delayedResize);
288
289 resize();
290 }
291
292 if (description != null) {
293 init();
294 // Submit editlink form with CTRL + Enter in the text area.
295 description.addEventListener('keydown', function (event) {
296 if (event.ctrlKey && event.keyCode === 13) {
297 document.getElementById('button-save-edit').click();
298 }
299 });
300 }
301
302 /**
303 * TimeZome select
304 * FIXME! way too hackish
305 */
306 var toRemove = document.getElementById('timezone-remove');
307 if (toRemove != null) {
308 var firstSelect = toRemove.getElementsByTagName('select')[0];
309 var secondSelect = toRemove.getElementsByTagName('select')[1];
310 toRemove.parentNode.removeChild(toRemove);
311 var toAdd = document.getElementById('timezone-add');
312 var newTimezone = '<span class="timezone-continent">Continent ' + firstSelect.outerHTML + '</span>';
313 newTimezone += ' <span class="timezone-country">Country ' + secondSelect.outerHTML + '</span>';
314 toAdd.innerHTML = newTimezone;
315 }
316
317 /**
318 * Awesomplete trigger.
319 */
320 var tags = document.getElementById('lf_tags');
321 if (tags != null) {
322 awesompleteUniqueTag('#lf_tags');
323 }
324
325 /**
326 * bLazy trigger
327 */
328 var picwall = document.getElementById('picwall_container');
329 if (picwall != null) {
330 var bLazy = new Blazy();
331 }
332
333 /**
334 * Bookmarklet alert
335 */
336 var bookmarkletLinks = document.querySelectorAll('.bookmarklet-link');
337 var bkmMessage = document.getElementById('bookmarklet-alert');
338 [].forEach.call(bookmarkletLinks, function(link) {
339 link.addEventListener('click', function(event) {
340 event.preventDefault();
341 alert(bkmMessage.value);
342 });
343 });
344
345 /**
346 * Firefox Social
347 */
348 var ffButton = document.getElementById('ff-social-button');
349 if (ffButton != null) {
350 ffButton.addEventListener('click', function(event) {
351 activateFirefoxSocial(event.target);
352 });
353 }
354
355 /**
356 * Plugin admin order
357 */
358 var orderPA = document.querySelectorAll('.order');
359 [].forEach.call(orderPA, function(link) {
360 link.addEventListener('click', function(event) {
361 event.preventDefault();
362 if (event.target.classList.contains('order-up')) {
363 return orderUp(event.target.parentNode.parentNode.getAttribute('data-order'));
364 } else if (event.target.classList.contains('order-down')) {
365 return orderDown(event.target.parentNode.parentNode.getAttribute('data-order'));
366 }
367 });
368 });
369 };
370
371 function activateFirefoxSocial(node) {
372 var loc = location.href;
373 var baseURL = loc.substring(0, loc.lastIndexOf("/"));
374
375 // Keeping the data separated (ie. not in the DOM) so that it's maintainable and diffable.
376 var data = {
377 name: "{$shaarlititle}",
378 description: "The personal, minimalist, super-fast, database free, bookmarking service by the Shaarli community.",
379 author: "Shaarli",
380 version: "1.0.0",
381
382 iconURL: baseURL + "/images/favicon.ico",
383 icon32URL: baseURL + "/images/favicon.ico",
384 icon64URL: baseURL + "/images/favicon.ico",
385
386 shareURL: baseURL + "{noparse}?post=%{url}&title=%{title}&description=%{text}&source=firefoxsocialapi{/noparse}",
387 homepageURL: baseURL
388 };
389 node.setAttribute("data-service", JSON.stringify(data));
390
391 var activate = new CustomEvent("ActivateSocialFeature");
392 node.dispatchEvent(activate);
393 }