aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2019-02-13 15:06:28 +0100
committerGravatar GitHub <noreply@github.com> 2019-02-13 15:06:28 +0100
commit2374374ba972eb4cca84d7f71b1900f806c2b914 (patch)
tree71496d87cf87d14650da48154f7d232418404c21
parent142c80a4b8e13ce93f6c2eb697afeb7e6b54431e (diff)
Less jQuery (#2234)
* Less jQuery Follow-up of https://github.com/FreshRSS/FreshRSS/pull/2199 * Even less jQuery + global view unread title fix * Even less jQuery * Yet even less jQuery * Even less jQuery * Reduce some events * Even less jQuery * jQuery gone from main view +Fixed English i18n * Fix feed folded view * Remove Firefox 64 workaround Remove workaround for Gecko bug 1514498 in Firefox 64, fixed in Firefox 65 * Split to extra.js Avoid loading unneeded JavaScript code for the main view. + several adjustements * Improve CSS transition fold category * Rewrite shortcuts Remove library. Much faster, shorter, one listener instead of many. Control of the shortcut context. Fix https://github.com/FreshRSS/FreshRSS/issues/2215 * Remove debug * Minor syntax * Filter out unwanted shortcut modifiers * Menu overflow fix * Typo * Fix unfolding in mobile view * Remove jQuery from category.js * Remove jQuery from Global view
-rw-r--r--README.fr.md1
-rw-r--r--README.md1
-rw-r--r--app/Controllers/authController.php3
-rwxr-xr-xapp/Controllers/indexController.php1
-rw-r--r--app/Controllers/statsController.php1
-rw-r--r--app/Controllers/subscriptionController.php3
-rw-r--r--app/Controllers/userController.php4
-rw-r--r--app/FreshRSS.php5
-rw-r--r--app/i18n/en/conf.php2
-rw-r--r--app/i18n/ru/conf.php2
-rw-r--r--app/i18n/tr/conf.php2
-rw-r--r--app/layout/aside_feed.phtml6
-rw-r--r--app/views/helpers/index/normal/entry_bottom.phtml2
-rw-r--r--app/views/index/reader.phtml2
-rw-r--r--lib/Minz/Request.php25
-rw-r--r--p/scripts/category.js178
-rw-r--r--p/scripts/extra.js235
-rw-r--r--p/scripts/global_view.js128
-rw-r--r--p/scripts/main.js1589
-rw-r--r--p/scripts/repartition.js4
-rw-r--r--p/scripts/shortcut.js225
-rw-r--r--p/scripts/stats.js8
-rw-r--r--p/themes/base-theme/template.css40
23 files changed, 1180 insertions, 1287 deletions
diff --git a/README.fr.md b/README.fr.md
index e40ab8296..58647ae4a 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -210,7 +210,6 @@ Tout client supportant une API de type Fever ; Sélection :
* [php-http-304](https://alexandre.alapetite.fr/doc-alex/php-http-304/)
* [jQuery](https://jquery.com/)
* [lib_opml](https://github.com/marienfressinaud/lib_opml)
-* [keyboard_shortcuts](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
* [flotr2](http://www.humblesoftware.com/flotr2)
## Uniquement pour certaines options ou configurations
diff --git a/README.md b/README.md
index a5a2af725..190ee5639 100644
--- a/README.md
+++ b/README.md
@@ -210,7 +210,6 @@ Supported clients are:
* [php-http-304](https://alexandre.alapetite.fr/doc-alex/php-http-304/)
* [jQuery](https://jquery.com/)
* [lib_opml](https://github.com/marienfressinaud/lib_opml)
-* [keyboard_shortcuts](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
* [flotr2](http://www.humblesoftware.com/flotr2)
## Only for some options or configurations
diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php
index 3b2d78b19..75d4acae0 100644
--- a/app/Controllers/authController.php
+++ b/app/Controllers/authController.php
@@ -109,8 +109,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
public function formLoginAction() {
invalidateHttpCache();
- $file_mtime = @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js');
- Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . $file_mtime));
+ Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')));
$conf = Minz_Configuration::get('system');
$limits = $conf->limits;
diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php
index fa914ef87..824d65815 100755
--- a/app/Controllers/indexController.php
+++ b/app/Controllers/indexController.php
@@ -104,6 +104,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
return;
}
+ Minz_View::appendScript(Minz_Url::display('/scripts/extra.js?' . @filemtime(PUBLIC_PATH . '/scripts/extra.js')));
Minz_View::appendScript(Minz_Url::display('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js')));
try {
diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php
index acfacb890..1d0d9c124 100644
--- a/app/Controllers/statsController.php
+++ b/app/Controllers/statsController.php
@@ -52,6 +52,7 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
*/
public function indexAction() {
$statsDAO = FreshRSS_Factory::createStatsDAO();
+ Minz_View::prependScript(Minz_Url::display('/scripts/jquery.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.min.js')));
Minz_View::appendScript(Minz_Url::display('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js')));
$this->view->repartition = $statsDAO->calculateEntryRepartition();
$entryCount = $statsDAO->calculateEntryCount();
diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php
index 46503fc19..62fb3d384 100644
--- a/app/Controllers/subscriptionController.php
+++ b/app/Controllers/subscriptionController.php
@@ -29,8 +29,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
* It displays categories and associated feeds.
*/
public function indexAction() {
- Minz_View::appendScript(Minz_Url::display('/scripts/category.js?' .
- @filemtime(PUBLIC_PATH . '/scripts/category.js')));
+ Minz_View::appendScript(Minz_Url::display('/scripts/category.js?' . @filemtime(PUBLIC_PATH . '/scripts/category.js')));
Minz_View::prependTitle(_t('sub.title') . ' · ');
$this->view->onlyFeedsWithError = Minz_Request::paramTernary('error');
diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php
index 2338c8b2a..71172b9ef 100644
--- a/app/Controllers/userController.php
+++ b/app/Controllers/userController.php
@@ -128,9 +128,7 @@ class FreshRSS_user_Controller extends Minz_ActionController {
public function profileAction() {
Minz_View::prependTitle(_t('conf.profile.title') . ' · ');
- Minz_View::appendScript(Minz_Url::display(
- '/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')
- ));
+ Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')));
if (Minz_Request::isPost()) {
$passwordPlain = Minz_Request::param('newPasswordPlain', '', true);
diff --git a/app/FreshRSS.php b/app/FreshRSS.php
index dec446a8e..9371081e4 100644
--- a/app/FreshRSS.php
+++ b/app/FreshRSS.php
@@ -94,9 +94,10 @@ class FreshRSS extends Minz_FrontController {
}
}
//Use prepend to insert before extensions. Added in reverse order.
+ if (Minz_Request::controllerName() !== 'index') {
+ Minz_View::prependScript(Minz_Url::display('/scripts/extra.js?' . @filemtime(PUBLIC_PATH . '/scripts/extra.js')));
+ }
Minz_View::prependScript(Minz_Url::display('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js')));
- Minz_View::prependScript(Minz_Url::display('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js')));
- Minz_View::prependScript(Minz_Url::display('/scripts/jquery.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.min.js')));
}
private static function loadNotifications() {
diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php
index c6471426f..b3d4d8a5c 100644
--- a/app/i18n/en/conf.php
+++ b/app/i18n/en/conf.php
@@ -92,7 +92,7 @@ return array(
'auto_remove_article' => 'Hide articles after reading',
'confirm_enabled' => 'Display a confirmation dialog on “mark all as read” actions',
'display_articles_unfolded' => 'Show articles unfolded by default',
- 'display_categories_unfolded' => 'Show categories folded by default',
+ 'display_categories_unfolded' => 'Show categories unfolded by default',
'hide_read_feeds' => 'Hide categories & feeds with no unread articles (does not work with “Show all articles” configuration)',
'img_with_lazyload' => 'Use "lazy load" mode to load pictures',
'jump_next' => 'jump to next unread sibling (feed or category)',
diff --git a/app/i18n/ru/conf.php b/app/i18n/ru/conf.php
index 59ac480bc..841477964 100644
--- a/app/i18n/ru/conf.php
+++ b/app/i18n/ru/conf.php
@@ -92,7 +92,7 @@ return array(
'auto_remove_article' => 'Hide articles after reading', //TODO - Translation
'confirm_enabled' => 'Display a confirmation dialog on “mark all as read” actions', //TODO - Translation
'display_articles_unfolded' => 'Show articles unfolded by default', //TODO - Translation
- 'display_categories_unfolded' => 'Show categories folded by default', //TODO - Translation
+ 'display_categories_unfolded' => 'Show categories unfolded by default', //TODO - Translation
'hide_read_feeds' => 'Hide categories & feeds with no unread article (does not work with “Show all articles” configuration)', //TODO - Translation
'img_with_lazyload' => 'Use "lazy load" mode to load pictures', //TODO - Translation
'jump_next' => 'jump to next unread sibling (feed or category)', //TODO - Translation
diff --git a/app/i18n/tr/conf.php b/app/i18n/tr/conf.php
index 507558487..6c57d39da 100644
--- a/app/i18n/tr/conf.php
+++ b/app/i18n/tr/conf.php
@@ -92,7 +92,7 @@ return array(
'auto_remove_article' => 'Okuduktan sonra makaleleri gizle',
'confirm_enabled' => '"Hepsini okundu say" eylemi için onay iste',
'display_articles_unfolded' => 'Show articles unfolded by default',
- 'display_categories_unfolded' => 'Show categories folded by default',
+ 'display_categories_unfolded' => 'Show categories unfolded by default',
'hide_read_feeds' => 'Okunmamış makalesi olmayan kategori veya akışı gizle ("Tüm makaleleri göster" komutunda çalışmaz)',
'img_with_lazyload' => 'Resimleri yüklemek için "tembel modu" kullan',
'jump_next' => 'Bir sonraki benzer okunmamışa geç (akış veya kategori)',
diff --git a/app/layout/aside_feed.phtml b/app/layout/aside_feed.phtml
index 5efaa54d1..637acf4a4 100644
--- a/app/layout/aside_feed.phtml
+++ b/app/layout/aside_feed.phtml
@@ -37,13 +37,14 @@
<?php
$t_active = FreshRSS_Context::isCurrentGet('T');
+ $t_show = $t_active || FreshRSS_Context::$user_conf->display_categories;
?>
<li class="tree-folder category tags<?php echo $t_active ? ' active' : ''; ?>">
<div class="tree-folder-title">
<a class="dropdown-toggle" href="#"><?php echo _i($t_active ? 'up' : 'down'); ?></a>
<a class="title" data-unread="<?php echo format_number($this->nbUnreadTags); ?>" href="<?php echo _url('index', $actual_view, 'get', 'T'); ?>"><?php echo _t('index.menu.tags'); ?></a>
</div>
- <ul class="tree-folder-items<?php echo $t_active ? ' active' : ''; ?>">
+ <ul class="tree-folder-items<?php echo $t_show ? ' active' : ''; ?>">
<?php
foreach ($this->tags as $tag):
?>
@@ -64,8 +65,7 @@
$feeds = $cat->feeds();
if (!empty($feeds)) {
$c_active = FreshRSS_Context::isCurrentGet('c_' . $cat->id());
- $c_show = $c_active && (!FreshRSS_Context::$user_conf->display_categories ||
- FreshRSS_Context::$current_get['feed']);
+ $c_show = $c_active || FreshRSS_Context::$user_conf->display_categories;
?>
<li class="tree-folder category<?php echo $c_active ? ' active' : ''; ?>" data-unread="<?php echo $cat->nbNotRead(); ?>">
<div class="tree-folder-title">
diff --git a/app/views/helpers/index/normal/entry_bottom.phtml b/app/views/helpers/index/normal/entry_bottom.phtml
index 1f35318e3..c0edbdf7d 100644
--- a/app/views/helpers/index/normal/entry_bottom.phtml
+++ b/app/views/helpers/index/normal/entry_bottom.phtml
@@ -93,7 +93,7 @@
<a target="_blank" rel="noreferrer" href="<?php echo $share->url(); ?>"><?php echo $share->name(); ?></a>
<?php } else {?>
<a href="POST"><?php echo $share->name(); ?></a>
- <form method="POST" data-url="<?php echo $share->url(); ?>">
+ <form method="POST" action="<?php echo $share->url(); ?>" disabled="disabled">
<input type="hidden" value="<?php echo $link; ?>" name="<?php echo $share->field(); ?>"/>
</form>
<?php } ?>
diff --git a/app/views/index/reader.phtml b/app/views/index/reader.phtml
index 1485a1997..fbe37d2e3 100644
--- a/app/views/index/reader.phtml
+++ b/app/views/index/reader.phtml
@@ -42,7 +42,7 @@ if (!empty($this->entries)) {
<a class="bookmark" href="<?php echo Minz_Url::display($favoriteUrl); ?>">
<?php echo _i($item->isFavorite() ? 'starred' : 'non-starred'); ?>
</a>
- <a href="<?php echo _url('index', 'reader', 'get', 'f_' . $feed->id()); ?>">
+ <a class="website" href="<?php echo _url('index', 'reader', 'get', 'f_' . $feed->id()); ?>">
<img class="favicon" src="<?php echo $feed->favicon(); ?>" alt="✇" /> <span><?php echo $feed->name(); ?></span>
</a>
<h1 class="title"><a target="_blank" rel="noreferrer" class="go_website" href="<?php echo $item->link(); ?>"><?php echo $item->title(); ?></a></h1>
diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php
index 8b2b610d6..912c354ac 100644
--- a/lib/Minz/Request.php
+++ b/lib/Minz/Request.php
@@ -95,6 +95,7 @@ class Minz_Request {
*/
public static function init() {
self::magicQuotesOff();
+ self::initJSON();
}
/**
@@ -238,6 +239,30 @@ class Minz_Request {
}
/**
+ * Allows receiving POST data as application/json
+ */
+ private static function initJSON() {
+ $contentType = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '';
+ if ($contentType == '') { //PHP < 5.3.16
+ $contentType = isset($_SERVER['HTTP_CONTENT_TYPE']) ? $_SERVER['HTTP_CONTENT_TYPE'] : '';
+ }
+ $contentType = strtolower(trim($contentType));
+ if ($contentType === 'application/json') {
+ $ORIGINAL_INPUT = file_get_contents('php://input', false, null, 0, 1048576);
+ if ($ORIGINAL_INPUT != '') {
+ $json = json_decode($ORIGINAL_INPUT, true);
+ if ($json != null) {
+ foreach ($json as $k => $v) {
+ if (!isset($_POST[$k])) {
+ $_POST[$k] = $v;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Permet de récupérer une variable de type $_POST
* @param $param nom de la variable
* @param $default valeur par défaut à attribuer à la variable
diff --git a/p/scripts/category.js b/p/scripts/category.js
index caa4fa22f..86d8542f1 100644
--- a/p/scripts/category.js
+++ b/p/scripts/category.js
@@ -1,6 +1,6 @@
"use strict";
-/* globals i18n */
-/* jshint globalstrict: true */
+/* globals context */
+/* jshint esversion:6, strict:global */
var loading = false,
dnd_successful = false;
@@ -9,7 +9,7 @@ function dragend_process(t) {
t.setAttribute('draggable', 'false');
if (loading) {
- window.setTimeout(function() {
+ setTimeout(function() {
dragend_process(t);
}, 50);
return;
@@ -20,11 +20,11 @@ function dragend_process(t) {
t.style.opacity = '';
t.setAttribute('draggable', 'true');
} else {
- var parent = $(t.parentNode);
- $(t).remove();
+ const p = t.parentElement;
+ t.remove();
- if (parent.children().length <= 0) {
- parent.append('<li class="item disabled" dropzone="move">' + i18n.category_empty + '</li>');
+ if (p.childElementCount <= 0) {
+ p.insertAdjacentHTML('beforeend', '<li class="item disabled" dropzone="move">' + context.i18n.category_empty + '</li>');
}
}
}
@@ -33,89 +33,109 @@ var dragFeedId = '',
dragHtml = '';
function init_draggable() {
- if (!(window.$ && window.i18n)) {
+ if (!window.context) {
if (window.console) {
- console.log('FreshRSS waiting for JS…');
+ console.log('FreshRSS category waiting for JS…');
}
- window.setTimeout(init_draggable, 50);
+ setTimeout(init_draggable, 50);
return;
}
- var draggable = '[draggable="true"]',
- dropzone = '[dropzone="move"]';
-
- $('.drop-section').on('dragstart', draggable, function(e) {
- var drag = $(e.target).closest('[draggable]')[0];
- e.originalEvent.dataTransfer.effectAllowed = 'move';
- dragHtml = drag.outerHTML;
- dragFeedId = drag.getAttribute('data-feed-id');
- e.originalEvent.dataTransfer.setData('text', dragFeedId);
- drag.style.opacity = 0.3;
-
- dnd_successful = false;
- });
- $('.drop-section').on('dragend', draggable, function(e) {
- dragend_process(e.target);
- });
-
- $('.drop-section').on('dragenter', dropzone, function(e) {
- $(this).addClass('drag-hover');
-
- e.preventDefault();
- });
- $('.drop-section').on('dragleave', dropzone, function(e) {
- var pos_this = $(this).position(),
- scroll_top = $(document).scrollTop(),
- top = pos_this.top,
- left = pos_this.left,
- right = left + $(this).width(),
- bottom = top + $(this).height(),
- mouse_x = e.originalEvent.screenX,
- mouse_y = e.originalEvent.clientY + scroll_top;
-
- if (left <= mouse_x && mouse_x <= right &&
- top <= mouse_y && mouse_y <= bottom) {
- // HACK because dragleave is triggered when hovering children!
- return;
- }
- $(this).removeClass('drag-hover');
- });
- $('.drop-section').on('dragover', dropzone, function(e) {
- e.originalEvent.dataTransfer.dropEffect = "move";
-
- e.preventDefault();
- return false;
- });
- $('.drop-section').on('drop', dropzone, function(e) {
- loading = true;
-
- $.ajax({
- type: 'POST',
- url: './?c=feed&a=move',
- data: {
- f_id: dragFeedId,
- c_id: e.target.parentNode.getAttribute('data-cat-id'),
- _csrf: context.csrf,
+ const draggable = '[draggable="true"]',
+ dropzone = '[dropzone="move"]',
+ dropSection = document.querySelector('.drop-section');
+
+ dropSection.ondragstart = function(ev) {
+ const li = ev.target.closest ? ev.target.closest(draggable) : null;
+ if (li) {
+ const drag = ev.target.closest('[draggable]');
+ ev.dataTransfer.effectAllowed = 'move';
+ dragHtml = drag.outerHTML;
+ dragFeedId = drag.getAttribute('data-feed-id');
+ ev.dataTransfer.setData('text', dragFeedId);
+ drag.style.opacity = 0.3;
+ dnd_successful = false;
}
- }).done(function() {
- $(e.target).after(dragHtml);
- if ($(e.target).hasClass('disabled')) {
- $(e.target).remove();
+ };
+
+ dropSection.ondragend = function(ev) {
+ const li = ev.target.closest ? ev.target.closest(draggable) : null;
+ if (li) {
+ dragend_process(li);
}
- dnd_successful = true;
- }).always(function() {
- loading = false;
- dragFeedId = '';
- dragHtml = '';
- });
+ };
- $(this).removeClass('drag-hover');
+ dropSection.ondragenter = function(ev) {
+ const li = ev.target.closest ? ev.target.closest(dropzone) : null;
+ if (li) {
+ li.classList.add('drag-hover');
+ return false;
+ }
+ };
+
+ dropSection.onddragleave = function(ev) {
+ const li = ev.target.closest ? ev.target.closest(dropzone) : null;
+ if (li) {
+ const scroll_top = document.documentElement.scrollTop,
+ top = li.offsetTop,
+ left = li.offsetLeft,
+ right = left + li.clientWidth,
+ bottom = top + li.clientHeight,
+ mouse_x = ev.screenX,
+ mouse_y = ev.clientY + scroll_top;
+
+ if (left <= mouse_x && mouse_x <= right &&
+ top <= mouse_y && mouse_y <= bottom) {
+ // HACK because dragleave is triggered when hovering children!
+ return;
+ }
+ li.classList.remove('drag-hover');
+ }
+ };
- e.preventDefault();
- });
+ dropSection.ondragover = function(ev) {
+ const li = ev.target.closest ? ev.target.closest(dropzone) : null;
+ if (li) {
+ ev.dataTransfer.dropEffect = "move";
+ return false;
+ }
+ };
+
+ dropSection.ondrop = function(ev) {
+ const li = ev.target.closest ? ev.target.closest(dropzone) : null;
+ if (li) {
+ loading = true;
+
+ const req = new XMLHttpRequest();
+ req.open('POST', './?c=feed&a=move', true);
+ req.responseType = 'json';
+ req.onload = function (e) {
+ if (this.status == 200) {
+ li.insertAdjacentHTML('afterend', dragHtml);
+ if (li.classList.contains('disabled')) {
+ li.remove();
+ }
+ dnd_successful = true;
+ }
+ };
+ req.onloadend = function (e) {
+ loading = false;
+ dragFeedId = '';
+ dragHtml = '';
+ };
+ req.setRequestHeader('Content-Type', 'application/json');
+ req.send(JSON.stringify({
+ f_id: dragFeedId,
+ c_id: li.parentElement.getAttribute('data-cat-id'),
+ _csrf: context.csrf,
+ }));
+
+ li.classList.remove('drag-hover');
+ return false;
+ }
+ };
}
-
if (document.readyState && document.readyState !== 'loading') {
init_draggable();
} else if (document.addEventListener) {
diff --git a/p/scripts/extra.js b/p/scripts/extra.js
new file mode 100644
index 000000000..7a0ef0477
--- /dev/null
+++ b/p/scripts/extra.js
@@ -0,0 +1,235 @@
+"use strict";
+/* globals context, openNotification, xmlHttpRequestJson */
+/* jshint esversion:6, strict:global */
+
+//<crypto form (Web login)>
+function poormanSalt() { //If crypto.getRandomValues is not available
+ const base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz';
+ let text = '$2a$04$';
+ for (let i = 22; i > 0; i--) {
+ text += base.charAt(Math.floor(Math.random() * 64));
+ }
+ return text;
+}
+
+function init_crypto_form() {
+ /* globals dcodeIO */
+ const crypto_form = document.getElementById('crypto-form');
+ if (!crypto_form) {
+ return;
+ }
+
+ if (!(window.dcodeIO)) {
+ if (window.console) {
+ console.log('FreshRSS waiting for bcrypt.js…');
+ }
+ setTimeout(init_crypto_form, 100);
+ return;
+ }
+
+ crypto_form.onsubmit = function (e) {
+ const submit_button = this.querySelector('button[type="submit"]');
+ submit_button.disabled = true;
+ let success = false;
+
+ const req = new XMLHttpRequest();
+ req.open('GET', './?c=javascript&a=nonce&user=' + document.getElementById('username').value, false);
+ req.onerror = function () {
+ openNotification('Communication error!', 'bad');
+ };
+ req.send();
+ if (req.status == 200) {
+ const json = xmlHttpRequestJson(req);
+ if (!json.salt1 || !json.nonce) {
+ openNotification('Invalid user!', 'bad');
+ } else {
+ try {
+ const strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'),
+ s = dcodeIO.bcrypt.hashSync(document.getElementById('passwordPlain').value, json.salt1),
+ c = dcodeIO.bcrypt.hashSync(json.nonce + s, strong ? dcodeIO.bcrypt.genSaltSync(4) : poormanSalt());
+ document.getElementById('challenge').value = c;
+ if (!s || !c) {
+ openNotification('Crypto error!', 'bad');
+ } else {
+ success = true;
+ }
+ } catch (ex) {
+ openNotification('Crypto exception! ' + ex, 'bad');
+ }
+ }
+ } else {
+ req.onerror();
+ }
+
+ submit_button.disabled = false;
+ return success;
+ };
+}
+//</crypto form (Web login)>
+
+function init_share_observers() {
+ let shares = document.querySelectorAll('.group-share').length;
+ const shareAdd = document.querySelector('.share.add');
+ if (shareAdd) {
+ shareAdd.onclick = function (ev) {
+ const s = this.parentElement.querySelector('select'),
+ opt = s.options[s.selectedIndex];
+ let row = this.closest('form').getAttribute('data-' + opt.getAttribute('data-form'));
+ row = row.replace(/##label##/g, opt.text);
+ row = row.replace(/##type##/g, opt.value);
+ row = row.replace(/##help##/g, opt.getAttribute('data-help'));
+ row = row.replace(/##key##/g, shares);
+ row = row.replace(/##method##/g, opt.getAttribute('data-method'));
+ row = row.replace(/##field##/g, opt.getAttribute('data-field'));
+ this.closest('.form-group').insertAdjacentHTML('beforebegin', row);
+ shares++;
+ return false;
+ };
+ }
+}
+
+
+function init_remove_observers() {
+ document.querySelectorAll('.post').forEach(function (div) {
+ div.onclick = function (ev) {
+ const a = ev.target.closest('a.remove');
+ if (a) {
+ const remove_what = a.getAttribute('data-remove');
+ if (remove_what !== undefined) {
+ const d = document.getElementById(remove_what);
+ if (d) {
+ d.remove();
+ }
+ }
+ return false;
+ }
+ };
+ });
+}
+
+function init_feed_observers() {
+ const s = document.getElementById('category');
+ if (s && s.matches('select')) {
+ s.onchange = function (ev) {
+ const detail = document.getElementById('new_category_name').parentElement;
+ if (this.value === 'nc') {
+ detail.setAttribute('aria-hidden', 'false');
+ detail.querySelector('input').focus();
+ } else {
+ detail.setAttribute('aria-hidden', 'true');
+ }
+ };
+ }
+}
+
+function init_password_observers() {
+ document.querySelectorAll('.toggle-password').forEach(function (a) {
+ a.onmousedown = function (ev) {
+ const passwordField = document.getElementById(this.getAttribute('data-toggle'));
+ passwordField.setAttribute('type', 'text');
+ this.classList.add('active');
+ return false;
+ };
+ a.onmouseup = function (ev) {
+ const passwordField = document.getElementById(this.getAttribute('data-toggle'));
+ passwordField.setAttribute('type', 'password');
+ this.classList.remove('active');
+ return false;
+ };
+ });
+}
+
+function init_select_observers() {
+ document.querySelectorAll('.select-change').forEach(function (s) {
+ s.onchange = function (ev) {
+ const opt = s.options[s.selectedIndex];
+ location.href = opt.getAttribute('data-url');
+ };
+ });
+}
+
+function init_slider_observers() {
+ const slider = document.getElementById('slider'),
+ closer = document.getElementById('close-slider');
+ if (!slider) {
+ return;
+ }
+
+ document.querySelector('.post').onclick = function (ev) {
+ const a = ev.target.closest('.open-slider');
+ if (a) {
+ if (!context.ajax_loading) {
+ context.ajax_loading = true;
+
+ const req = new XMLHttpRequest();
+ req.open('GET', a.href + '&ajax=1', true);
+ req.responseType = 'document';
+ req.onload = function (e) {
+ slider.innerHTML = this.response.body.innerHTML;
+ slider.classList.add('active');
+ closer.classList.add('active');
+ context.ajax_loading = false;
+ };
+ req.send();
+ return false;
+ }
+ }
+ };
+
+ closer.onclick = function (ev) {
+ closer.classList.remove('active');
+ slider.classList.remove('active');
+ return false;
+ };
+}
+
+function init_configuration_alert() {
+ window.onsubmit = function (e) {
+ window.hasSubmit = true;
+ };
+ window.onbeforeunload = function (e) {
+ if (window.hasSubmit) {
+ return;
+ }
+ const ds = document.querySelectorAll('[data-leave-validation]');
+ for (let i = ds.length - 1; i >= 0; i--) {
+ const input = ds[i];
+ if (input.type === 'checkbox' || input.type === 'radio') {
+ if (input.checked != input.getAttribute('data-leave-validation')) {
+ return false;
+ }
+ } else if (input.value != input.getAttribute('data-leave-validation')) {
+ return false;
+ }
+ }
+ };
+}
+
+function init_extra() {
+ if (!window.context) {
+ if (window.console) {
+ console.log('FreshRSS extra waiting for JS…');
+ }
+ window.setTimeout(init_extra, 50); //Wait for all js to be loaded
+ return;
+ }
+ init_crypto_form();
+ init_share_observers();
+ init_remove_observers();
+ init_feed_observers();
+ init_password_observers();
+ init_select_observers();
+ init_slider_observers();
+ init_configuration_alert();
+}
+
+if (document.readyState && document.readyState !== 'loading') {
+ init_extra();
+} else {
+ document.addEventListener('DOMContentLoaded', function () {
+ if (window.console) {
+ console.log('FreshRSS extra waiting for DOMContentLoaded…');
+ }
+ init_extra();
+ }, false);
+}
diff --git a/p/scripts/global_view.js b/p/scripts/global_view.js
index c5aaa48b1..b1581614a 100644
--- a/p/scripts/global_view.js
+++ b/p/scripts/global_view.js
@@ -1,6 +1,6 @@
"use strict";
-/* globals init_load_more, init_posts, init_stream */
-/* jshint globalstrict: true */
+/* globals context, init_load_more, init_posts, init_stream */
+/* jshint esversion:6, strict:global */
var panel_loading = false;
@@ -11,68 +11,88 @@ function load_panel(link) {
panel_loading = true;
- $.get(link, function (data) {
- $("#panel").append($(".nav_menu, #stream .day, #stream .flux, #stream .pagination, #stream.prompt", data));
-
- $("#panel .nav_menu").children().not("#nav_menu_read_all").remove();
-
- init_load_more($("#panel"));
- init_posts();
-
- $("#overlay").fadeIn();
- $("#panel").slideToggle();
-
- // force le démarrage du scroll en haut.
- // Sans ça, si l'on scroll en lisant une catégorie par exemple,
- // en en ouvrant une autre ensuite, on se retrouve au même point de scroll
- $("#panel").scrollTop(0);
- $(window).scrollTop(0);
-
- $('#panel').on('click', '#nav_menu_read_all button, #bigMarkAsRead', function () {
- console.log($(this).attr("formaction"));
- $.ajax({
- type: "POST",
- url: $(this).attr("formaction"),
- data: {
- _csrf: context.csrf,
- },
- async: false
- });
- window.location.reload(false);
- return false;
- });
-
- panel_loading = false;
- });
+ const req = new XMLHttpRequest();
+ req.open('GET', link, true);
+ req.responseType = 'document';
+ req.onload = function (e) {
+ if (this.status != 200) {
+ return;
+ }
+ const html = this.response,
+ foreign = html.querySelectorAll('.nav_menu, #stream .day, #stream .flux, #stream .pagination, #stream.prompt'),
+ panel = document.getElementById('panel');
+ foreign.forEach(function (el) {
+ panel.appendChild(document.adoptNode(el));
+ });
+ panel.querySelectorAll('.nav_menu > :not([id="nav_menu_read_all"])').forEach(function (el) {
+ el.remove();
+ });
+
+ init_load_more(panel);
+ init_posts();
+
+ document.getElementById('overlay').classList.add('visible');
+ panel.classList.add('visible');
+
+ // force le démarrage du scroll en haut.
+ // Sans ça, si l'on scroll en lisant une catégorie par exemple,
+ // en en ouvrant une autre ensuite, on se retrouve au même point de scroll
+ panel.scrollTop = 0;
+ document.documentElement.scrollTop = 0;
+
+ //We already have a click listener in main.js
+ panel.addEventListener('click', function (ev) {
+ const b = ev.target.closest('#nav_menu_read_all button, #bigMarkAsRead');
+ if (b) {
+ console.log(b.formAction);
+
+ const req2 = new XMLHttpRequest();
+ req2.open('POST', b.formAction, false);
+ req2.setRequestHeader('Content-Type', 'application/json');
+ req2.send(JSON.stringify({
+ _csrf: context.csrf,
+ }));
+ if (req2.status == 200) {
+ location.reload(false);
+ return false;
+ }
+ }
+ });
+
+ panel_loading = false;
+ };
+ req.send();
}
function init_close_panel() {
- $("#overlay .close").click(function () {
- $("#panel").html('');
- $("#panel").slideToggle();
- $("#overlay").fadeOut();
-
- return false;
- });
+ const panel = document.getElementById('panel');
+ document.querySelector('#overlay .close').onclick = function (ev) {
+ panel.innerHTML = '';
+ panel.classList.remove('visible');
+ document.getElementById('overlay').classList.remove('visible');
+ return false;
+ };
}
function init_global_view() {
- // TODO: should be based on generic classes.
- $(".box a").click(function () {
- var link = $(this).attr("href");
-
- load_panel(link);
-
- return false;
- });
+ // TODO: should be based on generic classes
+ document.querySelectorAll('.box a').forEach(function (a) {
+ a.onclick = function (ev) {
+ load_panel(a.href);
+ return false;
+ };
+ });
- $(".nav_menu #nav_menu_read_all, .nav_menu .toggle_aside").remove();
+ document.querySelectorAll('.nav_menu #nav_menu_read_all, .nav_menu .toggle_aside').forEach(function (el) {
+ el.remove();
+ });
- init_stream($("#panel"));
+ const panel = document.getElementById('panel');
+ init_stream(panel);
}
function init_all_global_view() {
- if (!(window.$ && window.init_stream)) {
+ if (!window.context) {
if (window.console) {
console.log('FreshRSS Global view waiting for JS…');
}
@@ -85,7 +105,7 @@ function init_all_global_view() {
if (document.readyState && document.readyState !== 'loading') {
init_all_global_view();
-} else if (document.addEventListener) {
+} else {
document.addEventListener('DOMContentLoaded', function () {
init_all_global_view();
}, false);
diff --git a/p/scripts/main.js b/p/scripts/main.js
index 82dc79ce7..345cbe749 100644
--- a/p/scripts/main.js
+++ b/p/scripts/main.js
@@ -1,5 +1,4 @@
"use strict";
-/* globals $, jQuery, shortcut */
/* jshint esversion:6, strict:global */
//<Polyfills>
@@ -16,36 +15,34 @@ if (!Element.prototype.closest) Element.prototype.closest = function (s) {
if (!Element.prototype.remove) Element.prototype.remove = function () { if (this.parentNode) this.parentNode.removeChild(this); };
//</Polyfills>
-//<Global variables>
-var context, i18n, icons, shortcuts, urls;
+//<Utils>
+function xmlHttpRequestJson(req) {
+ let json = req.response;
+ if (req.responseType !== 'json') { //IE11
+ try { json = JSON.parse(req.responseText); }
+ catch (ex) { json = null; }
+ }
+ return json;
+}
+//</Utils>
+
+//<Global context>
+var context;
(function parseJsonVars() {
const jsonVars = document.getElementById('jsonVars'),
json = JSON.parse(jsonVars.innerHTML);
jsonVars.outerHTML = '';
context = json.context;
- i18n = json.i18n;
- shortcuts = json.shortcuts;
- urls = json.urls;
- icons = json.icons;
- icons.read = decodeURIComponent(icons.read);
- icons.unread = decodeURIComponent(icons.unread);
+ context.ajax_loading = false;
+ context.i18n = json.i18n;
+ context.shortcuts = json.shortcuts;
+ context.urls = json.urls;
+ context.icons = json.icons;
+ context.icons.read = decodeURIComponent(context.icons.read);
+ context.icons.unread = decodeURIComponent(context.icons.unread);
}());
-
-var $stream = null,
- ajax_loading = false,
- $nav_entries = null;
-//</Global variables>
-
-function redirect(url, new_tab) {
- if (url) {
- if (new_tab) {
- window.open(url);
- } else {
- location.href = url;
- }
- }
-}
+//</Global context>
function needsScroll(elem) {
const winBottom = document.documentElement.scrollTop + document.documentElement.clientHeight,
@@ -160,54 +157,64 @@ var pending_entries = {},
mark_read_queue = [];
function send_mark_read_queue(queue, asRead) {
- $.ajax({
- type: 'POST',
- url: '.?c=entry&a=read' + (asRead ? '' : '&is_read=0'),
- data: {
- ajax: true,
- _csrf: context.csrf,
- 'id[]': queue,
- },
- }).done(function (data) {
- for (let i = queue.length - 1; i >= 0; i--) {
- const div = document.getElementById('flux_' + queue[i]),
- myIcons = icons;
- let inc = 0;
- if (div.classList.contains('not_read')) {
- div.classList.remove('not_read');
- div.querySelectorAll('a.read').forEach(function (a) { a.setAttribute('href', a.getAttribute('href').replace('&is_read=0', '') + '&is_read=1'); });
- div.querySelectorAll('a.read > .icon').forEach(function (img) { img.outerHTML = myIcons.read; });
- inc--;
- } else {
- div.classList.add('not_read');
- div.classList.add('keep_unread'); //Split for IE11
- div.querySelectorAll('a.read').forEach(function (a) { a.setAttribute('href', a.getAttribute('href').replace('&is_read=1', '')); });
- div.querySelectorAll('a.read > .icon').forEach(function (img) { img.outerHTML = myIcons.unread; });
- inc++;
+ const req = new XMLHttpRequest();
+ req.open('POST', '.?c=entry&a=read' + (asRead ? '' : '&is_read=0'), true);
+ req.responseType = 'json';
+ req.onerror = function (e) {
+ openNotification(context.i18n.notif_request_failed, 'bad');
+ for (let i = queue.length - 1; i >= 0; i--) {
+ delete pending_entries['flux_' + queue[i]];
}
- let feed_link = div.querySelector('.website > a');
- if (feed_link) {
- let feed_url = feed_link.getAttribute('href');
- let feed_id = feed_url.substr(feed_url.lastIndexOf('f_'));
- incUnreadsFeed(div, feed_id, inc);
+ };
+ req.onload = function (e) {
+ if (this.status != 200) {
+ return req.onerror(e);
}
- delete pending_entries['flux_' + queue[i]];
- }
- faviconNbUnread();
- if (data.tags) {
- let tagIds = Object.keys(data.tags);
- for (let i = tagIds.length - 1; i >= 0; i--) {
- let tagId = tagIds[i];
- incUnreadsTag(tagId, (asRead ? -1 : 1) * data.tags[tagId].length);
+ const json = xmlHttpRequestJson(this);
+ for (let i = queue.length - 1; i >= 0; i--) {
+ const div = document.getElementById('flux_' + queue[i]),
+ myIcons = context.icons;
+ let inc = 0;
+ if (div.classList.contains('not_read')) {
+ div.classList.remove('not_read');
+ div.querySelectorAll('a.read').forEach(function (a) {
+ a.href = a.href.replace('&is_read=0', '') + '&is_read=1';
+ });
+ div.querySelectorAll('a.read > .icon').forEach(function (img) { img.outerHTML = myIcons.read; });
+ inc--;
+ } else {
+ div.classList.add('not_read');
+ div.classList.add('keep_unread'); //Split for IE11
+ div.querySelectorAll('a.read').forEach(function (a) {
+ a.href = a.href.replace('&is_read=1', '');
+ });
+ div.querySelectorAll('a.read > .icon').forEach(function (img) { img.outerHTML = myIcons.unread; });
+ inc++;
+ }
+ let feed_link = div.querySelector('.website > a, a.website');
+ if (feed_link) {
+ let feed_url = feed_link.href;
+ let feed_id = feed_url.substr(feed_url.lastIndexOf('f_'));
+ incUnreadsFeed(div, feed_id, inc);
+ }
+ delete pending_entries['flux_' + queue[i]];
}
- }
- onScroll();
- }).fail(function (data) {
- openNotification(i18n.notif_request_failed, 'bad');
- for (let i = queue.length - 1; i >= 0; i--) {
- delete pending_entries['flux_' + queue[i]];
- }
- });
+ faviconNbUnread();
+ if (json.tags) {
+ let tagIds = Object.keys(json.tags);
+ for (let i = tagIds.length - 1; i >= 0; i--) {
+ let tagId = tagIds[i];
+ incUnreadsTag(tagId, (asRead ? -1 : 1) * json.tags[tagId].length);
+ }
+ }
+ onScroll();
+ };
+ req.setRequestHeader('Content-Type', 'application/json');
+ req.send(JSON.stringify({
+ ajax: true,
+ _csrf: context.csrf,
+ id: queue,
+ }));
}
var send_mark_read_queue_timeout = 0;
@@ -246,7 +253,7 @@ function mark_favorite(div) {
}
let a = div.querySelector('a.bookmark'),
- url = a ? a.getAttribute('href') : '';
+ url = a ? a.href : '';
if (!url) {
return false;
}
@@ -256,45 +263,51 @@ function mark_favorite(div) {
}
pending_entries[div.id] = true;
- $.ajax({
- type: 'POST',
- url: url,
- data: {
- ajax: true,
- _csrf: context.csrf,
- },
- }).done(function (data) {
- let inc = 0;
- if (div.classList.contains('favorite')) {
- div.classList.remove('favorite');
- inc--;
- } else {
- div.classList.add('favorite');
- inc++;
- }
- div.querySelectorAll('a.bookmark').forEach(function (a) { a.setAttribute('href', data.url); });
- div.querySelectorAll('a.bookmark > .icon').forEach(function (img) { img.outerHTML = data.icon; });
+ const req = new XMLHttpRequest();
+ req.open('POST', url, true);
+ req.responseType = 'json';
+ req.onerror = function (e) {
+ openNotification(context.i18n.notif_request_failed, 'bad');
+ delete pending_entries[div.id];
+ };
+ req.onload = function (e) {
+ if (this.status != 200) {
+ return req.onerror(e);
+ }
+ const json = xmlHttpRequestJson(this);
+ let inc = 0;
+ if (div.classList.contains('favorite')) {
+ div.classList.remove('favorite');
+ inc--;
+ } else {
+ div.classList.add('favorite');
+ inc++;
+ }
+ div.querySelectorAll('a.bookmark').forEach(function (a) { a.href = json.url; });
+ div.querySelectorAll('a.bookmark > .icon').forEach(function (img) { img.outerHTML = json.icon; });
- const favourites = document.querySelector('#aside_feed .favorites .title');
- if (favourites) {
- favourites.textContent = favourites.textContent.replace(/((?: \([ 0-9]+\))?\s*)$/, function (m, p1) {
- return incLabel(p1, inc, false);
- });
- }
+ const favourites = document.querySelector('#aside_feed .favorites .title');
+ if (favourites) {
+ favourites.textContent = favourites.textContent.replace(/((?: \([ 0-9]+\))?\s*)$/, function (m, p1) {
+ return incLabel(p1, inc, false);
+ });
+ }
- if (div.classList.contains('not_read')) {
- const elem = document.querySelector('#aside_feed .favorites .title'),
- feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0;
- if (elem) {
- elem.setAttribute('data-unread', numberFormat(feed_unreads + inc));
+ if (div.classList.contains('not_read')) {
+ const elem = document.querySelector('#aside_feed .favorites .title'),
+ feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0;
+ if (elem) {
+ elem.setAttribute('data-unread', numberFormat(feed_unreads + inc));
+ }
}
- }
- delete pending_entries[div.id];
- }).fail(function (data) {
- openNotification(i18n.notif_request_failed, 'bad');
- delete pending_entries[div.id];
- });
+ delete pending_entries[div.id];
+ };
+ req.setRequestHeader('Content-Type', 'application/json');
+ req.send(JSON.stringify({
+ ajax: true,
+ _csrf: context.csrf,
+ }));
}
var freshrssOpenArticleEvent = document.createEvent('Event');
@@ -307,9 +320,9 @@ function toggleContent(new_active, old_active, skipping) {
}
if (context.does_lazyload && !skipping) {
- new_active.querySelectorAll('img[data-original], iframe[data-original]').forEach(function (elem) {
- elem.setAttribute('src', elem.getAttribute('data-original'));
- elem.removeAttribute('data-original');
+ new_active.querySelectorAll('img[data-original], iframe[data-original]').forEach(function (el) {
+ el.src = el.getAttribute('data-original');
+ el.removeAttribute('data-original');
});
}
@@ -322,12 +335,12 @@ function toggleContent(new_active, old_active, skipping) {
old_active.classList.remove('active');
old_active.classList.remove('current'); //Split for IE11
}
- } else { // collapse_entry calls toggleContent(flux_current, flux_current, false)
+ } else {
new_active.classList.toggle('active');
}
const relative_move = context.current_view === 'global',
- box_to_move = relative_move ? document.getElementById('#panel') : document.documentElement;
+ box_to_move = relative_move ? document.getElementById('panel') : document.documentElement;
if (context.sticky_post) {
let prev_article = new_active.previousElementSibling,
@@ -355,7 +368,7 @@ function toggleContent(new_active, old_active, skipping) {
if (context.auto_mark_article) {
mark_read(new_active, true);
}
- new_active[0].dispatchEvent(freshrssOpenArticleEvent);
+ new_active.dispatchEvent(freshrssOpenArticleEvent);
}
onScroll();
}
@@ -373,18 +386,28 @@ function next_entry(skipping) {
}
function prev_feed() {
- const $active_feed = $('#aside_feed .tree-folder-items .item.active');
- if ($active_feed.length > 0) {
- $active_feed.prevAll(':visible:first').find('a').each(function () { this.click(); });
+ const active_feed = document.querySelector('#aside_feed .feed.active');
+ if (active_feed) {
+ let feed = active_feed;
+ do feed = feed.previousElementSibling;
+ while (feed && getComputedStyle(feed).display === 'none');
+ if (feed) {
+ feed.querySelector('a.item-title').click();
+ }
} else {
last_feed();
}
}
function next_feed() {
- const $active_feed = $('#aside_feed .tree-folder-items .item.active');
- if ($active_feed.length > 0) {
- $active_feed.nextAll(':visible:first').find('a').each(function () { this.click(); });
+ const active_feed = document.querySelector('#aside_feed .feed.active');
+ if (active_feed) {
+ let feed = active_feed;
+ do feed = feed.nextElementSibling;
+ while (feed && getComputedStyle(feed).display === 'none');
+ if (feed) {
+ feed.querySelector('a.item-title').click();
+ }
} else {
first_feed();
}
@@ -405,31 +428,31 @@ function last_feed() {
}
function prev_category() {
- const $active_cat = $('#aside_feed .tree-folder.active');
-
- if ($active_cat.length > 0) {
- const $prev_cat = $active_cat.prevAll(':visible:first').find('.tree-folder-title .title');
- if ($prev_cat.length > 0) {
- $prev_cat[0].click();
+ const active_cat = document.querySelector('#aside_feed .category.active');
+ if (active_cat) {
+ let cat = active_cat;
+ do cat = cat.previousElementSibling;
+ while (cat && getComputedStyle(cat).display === 'none');
+ if (cat) {
+ cat.querySelector('a.title').click();
}
} else {
last_category();
}
- return;
}
function next_category() {
- const $active_cat = $('#aside_feed .tree-folder.active');
-
- if ($active_cat.length > 0) {
- const $next_cat = $active_cat.nextAll(':visible:first').find('.tree-folder-title .title');
- if ($next_cat.length > 0) {
- $next_cat[0].click();
+ const active_cat = document.querySelector('#aside_feed .category.active');
+ if (active_cat) {
+ let cat = active_cat;
+ do cat = cat.nextElementSibling;
+ while (cat && getComputedStyle(cat).display === 'none');
+ if (cat) {
+ cat.querySelector('a.title').click();
}
} else {
first_category();
}
- return;
}
function first_category() {
@@ -452,21 +475,21 @@ function collapse_entry() {
}
function user_filter(key) {
- const $filter = $('#dropdown-query'),
- $filters = $filter.siblings('.dropdown-menu').find('.item.query a');
+ const filter = document.getElementById('dropdown-query'),
+ filters = filter.parentElement.querySelectorAll('.dropdown-menu > .query > a');
if (typeof key === 'undefined') {
- if (!$filters.length) {
+ if (!filters.length) {
return;
}
// Display the filter div
- location.hash = $filters.attr('id');
+ location.hash = filter.id;
// Force scrolling to the filter div
const scroll = needsScroll(document.querySelector('.header'));
if (scroll !== 0) {
document.documentElement.scrollTop = scroll;
}
// Force the key value if there is only one action, so we can trigger it automatically
- if ($filters.length === 1) {
+ if (filters.length === 1) {
key = 1;
} else {
return;
@@ -474,8 +497,8 @@ function user_filter(key) {
}
// Trigger selected share action
key = parseInt(key);
- if (key <= $filters.length) {
- $filters[key - 1].click();
+ if (key <= filters.length) {
+ filters[key - 1].click();
}
}
@@ -576,384 +599,378 @@ function init_column_categories() {
return;
}
- $('#aside_feed').on('click', '.tree-folder>.tree-folder-title>a.dropdown-toggle', function () {
- $(this).children().each(function () {
- if (this.alt === '▽') {
- this.src = this.src.replace('/icons/down.', '/icons/up.');
- this.alt = '△';
+ document.getElementById('aside_feed').onclick = function (ev) {
+ let a = ev.target.closest('.tree-folder > .tree-folder-title > a.dropdown-toggle');
+ if (a) {
+ const img = a.querySelector('img');
+ if (img.alt === '▽') {
+ img.src = img.src.replace('/icons/down.', '/icons/up.');
+ img.alt = '△';
} else {
- this.src = this.src.replace('/icons/up.', '/icons/down.');
- this.alt = '▽';
+ img.src = img.src.replace('/icons/up.', '/icons/down.');
+ img.alt = '▽';
}
- });
- $(this).parent().next('.tree-folder-items').slideToggle(300, function () {
- //Workaround for Gecko bug 1514498 in Firefox 64
- const sidebar = document.getElementById('sidebar');
- if (sidebar && sidebar.scrollHeight > sidebar.clientHeight && //if needs scrollbar
- sidebar.scrollWidth >= sidebar.offsetWidth) { //but no scrollbar
- sidebar.style['overflow-y'] = 'scroll'; //then force scrollbar
- setTimeout(function () { sidebar.style['overflow-y'] = ''; }, 0);
+
+ const ul = a.closest('li').querySelector('.tree-folder-items');
+ let nbVisibleItems = 0;
+ for (let i = ul.children.length - 1; i >= 0; i--) {
+ if (ul.children[i].offsetHeight) {
+ nbVisibleItems++;
+ }
}
- });
- return false;
- });
+ ul.classList.toggle('active');
+ //CSS transition does not work on max-height:auto
+ ul.style.maxHeight = ul.classList.contains('active') ? (nbVisibleItems * 4) + 'em' : 0;
+ return false;
+ }
- $('#aside_feed').on('click', '.tree-folder-items .feed .dropdown-toggle', function () {
- const itemId = $(this).closest('.item').attr('id'),
- templateId = itemId.substring(0, 2) === 't_' ? 'tag_config_template' : 'feed_config_template',
- id = itemId.substr(2),
- feed_web = $(this).data('fweb'),
- template = $('#' + templateId)
- .html().replace(/------/g, id).replace('http://example.net/', feed_web);
- if ($(this).next('.dropdown-menu').length === 0) {
- $(this).attr('href', '#dropdown-' + id).prev('.dropdown-target').attr('id', 'dropdown-' + id).parent()
- .append(template).find('button.confirm').removeAttr('disabled');
- } else {
- if ($(this).next('.dropdown-menu').css('display') === 'none') {
- const id2 = $(this).closest('.item').attr('id').substr(2);
- $(this).attr('href', '#dropdown-' + id2);
+ a = ev.target.closest('.tree-folder-items > .feed .dropdown-toggle');
+ if (a) {
+ const itemId = a.closest('.item').id,
+ templateId = itemId.substring(0, 2) === 't_' ? 'tag_config_template' : 'feed_config_template',
+ id = itemId.substr(2),
+ feed_web = a.getAttribute('data-fweb'),
+ div = a.parentElement,
+ dropdownMenu = div.querySelector('.dropdown-menu'),
+ template = document.getElementById(templateId)
+ .innerHTML.replace(/------/g, id).replace('http://example.net/', feed_web);
+ if (!dropdownMenu) {
+ a.href = '#dropdown-' + id;
+ div.querySelector('.dropdown-target').id = 'dropdown-' + id;
+ div.insertAdjacentHTML('beforeend', template);
+ div.querySelector('button.confirm').disabled = false;
+ } else if (getComputedStyle(dropdownMenu).display === 'none') {
+ const id2 = div.closest('.item').id.substr(2);
+ a.href = '#dropdown-' + id2;
} else {
- $(this).attr('href', '#close');
+ a.href = '#close';
}
+ return true;
}
- });
+
+ return true;
+ };
}
function init_shortcuts() {
- if (!(window.shortcut)) {
- if (window.console) {
- console.log('FreshRSS waiting for shortcut.js…');
- }
- setTimeout(init_shortcuts, 200);
- return;
- }
- // Manipulation shortcuts
- shortcut.add(shortcuts.mark_read, function () {
- // Toggle the read state
- mark_read(document.querySelector('.flux.current'), false);
- }, {
- 'disable_in_input': true
- });
- shortcut.add('shift+' + shortcuts.mark_read, function () {
- // Mark everything as read
- $('.nav_menu .read_all').click();
- }, {
- 'disable_in_input': true
- });
- shortcut.add(shortcuts.mark_favorite, function () {
- // Toggle the favorite state
- mark_favorite(document.querySelector('.flux.current'));
- }, {
- 'disable_in_input': true
- });
- shortcut.add(shortcuts.collapse_entry, function () {
- // Toggle the collapse state
- collapse_entry();
- }, {
- 'disable_in_input': true
- });
- shortcut.add(shortcuts.auto_share, function () {
- // Display the share options
- auto_share();
- }, {
- 'disable_in_input': true
- });
+ Object.keys(context.shortcuts).forEach(function (k) {
+ context.shortcuts[k] = (context.shortcuts[k] || '').toUpperCase();
+ });
- shortcut.add(shortcuts.user_filter, function () {
- // Display the user filters
- user_filter();
- }, {
- 'disable_in_input': true
- });
+ document.body.onkeydown = function (ev) {
+ if (ev.target.closest('input, textarea') ||
+ ev.ctrlKey || ev.metaKey || (ev.altKey && ev.shiftKey)) {
+ return true;
+ }
- function addShortcut(evt) {
- if ($('#dropdown-query').siblings('.dropdown-menu').is(':visible')) {
- user_filter(String.fromCharCode(evt.keyCode));
- } else {
- auto_share(String.fromCharCode(evt.keyCode));
- }
- }
- for (let i = 1; i < 10; i++) {
- shortcut.add(i.toString(), addShortcut, {
- 'disable_in_input': true
- });
- }
+ const s = context.shortcuts,
+ k = ev.key.toUpperCase();
+ if (location.hash.match(/^#dropdown-/)) {
+ const n = parseInt(k);
+ if (n) {
+ if (location.hash === '#dropdown-query') {
+ user_filter(n);
+ } else {
+ auto_share(n);
+ }
+ return false;
+ }
+ }
+ if (k === s.next_entry) {
+ if (ev.altKey) {
+ next_category();
+ } else if (ev.shiftKey) {
+ next_feed();
+ } else {
+ next_entry(false);
+ }
+ return false;
+ }
+ if (k === s.prev_entry) {
+ if (ev.altKey) {
+ prev_category();
+ } else if (ev.shiftKey) {
+ prev_feed();
+ } else {
+ prev_entry(false);
+ }
+ return false;
+ }
+ if (k === s.mark_read) {
+ if (ev.altKey) {
+ return true;
+ } else if (ev.shiftKey) { // Mark everything as read
+ document.querySelector('.nav_menu .read_all').click();
+ } else { // Toggle the read state
+ mark_read(document.querySelector('.flux.current'), false);
+ }
+ return false;
+ }
+ if (k === s.first_entry) {
+ if (ev.altKey) {
+ first_category();
+ } else if (ev.shiftKey) {
+ first_feed();
+ } else {
+ const old_active = document.querySelector('.flux.current'),
+ first = document.querySelector('.flux');
+ if (first.classList.contains('flux')) {
+ toggleContent(first, old_active, false);
+ }
+ }
+ return false;
+ }
+ if (k === s.last_entry) {
+ if (ev.altKey) {
+ last_category();
+ } else if (ev.shiftKey) {
+ last_feed();
+ } else {
+ const old_active = document.querySelector('.flux.current'),
+ last = document.querySelector('.flux:last-of-type');
+ if (last.classList.contains('flux')) {
+ toggleContent(last, old_active, false);
+ }
+ }
+ return false;
+ }
- // Entry navigation shortcuts
- shortcut.add(shortcuts.prev_entry, function () { prev_entry(false); }, {
- 'disable_in_input': true
- });
- shortcut.add(shortcuts.skip_prev_entry, function () { prev_entry(true); }, {
- 'disable_in_input': true
- });
- shortcut.add(shortcuts.first_entry, function () {
- const $old_active = $('.flux.current'),
- $first = $('.flux:first');
+ if (ev.altKey || ev.shiftKey) {
+ return true;
+ }
+ if (k === s.mark_favorite) { // Toggle the favorite state
+ mark_favorite(document.querySelector('.flux.current'));
+ return false;
+ }
+ if (k === s.go_website) {
+ if (context.auto_mark_site) {
+ mark_read(document.querySelector('.flux.current'), true);
+ }
+ window.open(document.querySelector('.flux.current a.go_website').href);
+ return false;
+ }
+ if (k === s.skip_next_entry) { next_entry(true); return false; }
+ if (k === s.skip_prev_entry) { prev_entry(true); return false; }
+ if (k === s.collapse_entry) { collapse_entry(); return false; }
+ if (k === s.auto_share) { auto_share(); return false; }
+ if (k === s.user_filter) { user_filter(); return false; }
+ if (k === s.load_more) { load_more_posts(); return false; }
+ if (k === s.close_dropdown) { location.hash = null; return false; }
+ if (k === s.help) { window.open(context.urls.help); return false; }
+ if (k === s.focus_search) { document.getElementById('search').focus(); return false; }
+ if (k === s.normal_view) { document.querySelector('#nav_menu_views .view-normal').click(); return false; }
+ if (k === s.reading_view) { document.querySelector('#nav_menu_views .view-reader').click(); return false; }
+ if (k === s.global_view) { document.querySelector('#nav_menu_views .view-global').click(); return false; }
+ if (k === s.rss_view) { document.querySelector('#nav_menu_views .view-rss').click(); return false; }
+ return true;
+ };
+}
- if ($first.hasClass('flux')) {
- toggleContent($first, $old_active, false);
+function init_stream(stream) {
+ stream.onclick = function (ev) {
+ let el = ev.target.closest('.flux a.read');
+ if (el) {
+ mark_read(el.closest('.flux'), false);
+ return false;
}
- }, {
- 'disable_in_input': true
- });
- shortcut.add(shortcuts.next_entry, function () { next_entry(false); }, {
- 'disable_in_input': true
- });
- shortcut.add(shortcuts.skip_next_entry, function () { next_entry(true); }, {
- 'disable_in_input': true
- });
- shortcut.add(shortcuts.last_entry, function () {
- const $old_active = $('.flux.current'),
- $last = $('.flux:last');
- if ($last.hasClass('flux')) {
- toggleContent($last, $old_active, false);
+ el = ev.target.closest('.flux a.bookmark');
+ if (el) {
+ mark_favorite(el.closest('.flux'));
+ return false;
}
- }, {
- 'disable_in_input': true
- });
- // Feed navigation shortcuts
- shortcut.add('shift+' + shortcuts.prev_entry, prev_feed, {
- 'disable_in_input': true
- });
- shortcut.add('shift+' + shortcuts.next_entry, next_feed, {
- 'disable_in_input': true
- });
- shortcut.add('shift+' + shortcuts.first_entry, first_feed, {
- 'disable_in_input': true
- });
- shortcut.add('shift+' + shortcuts.last_entry, last_feed, {
- 'disable_in_input': true
- });
- // Category navigation shortcuts
- shortcut.add('alt+' + shortcuts.prev_entry, prev_category, {
- 'disable_in_input': true
- });
- shortcut.add('alt+' + shortcuts.next_entry, next_category, {
- 'disable_in_input': true
- });
- shortcut.add('alt+' + shortcuts.first_entry, first_category, {
- 'disable_in_input': true
- });
- shortcut.add('alt+' + shortcuts.last_entry, last_category, {
- 'disable_in_input': true
- });
-
- shortcut.add(shortcuts.go_website, function () {
- const url_website = $('.flux.current a.go_website').attr('href');
- if (context.auto_mark_site) {
- $('.flux.current').each(function () {
- mark_read(this, true);
- });
+ el = ev.target.closest('.dynamictags');
+ if (el) {
+ loadDynamicTags(el);
+ return true;
}
- redirect(url_website, true);
- }, {
- 'disable_in_input': true
- });
-
- shortcut.add(shortcuts.load_more, load_more_posts, {
- 'disable_in_input': true
- });
-
- shortcut.add(shortcuts.focus_search, focus_search, {
- 'disable_in_input': true
- });
-
- shortcut.add(shortcuts.help, function () {
- redirect(urls.help, true);
- }, {
- 'disable_in_input': true
- });
-
- shortcut.add(shortcuts.close_dropdown, function () {
- location.hash = null;
- }, {
- 'disable_in_input': true
- });
-
- shortcut.add(shortcuts.normal_view, function () {
- $('#nav_menu_views .view-normal').get(0).click();
- }, {
- 'disable_in_input': true
- });
-
- shortcut.add(shortcuts.global_view, function () {
- $('#nav_menu_views .view-global').get(0).click();
- }, {
- 'disable_in_input': true
- });
-
- shortcut.add(shortcuts.reading_view, function () {
- $('#nav_menu_views .view-reader').get(0).click();
- }, {
- 'disable_in_input': true
- });
-
- shortcut.add(shortcuts.rss_view, function () {
- $('#nav_menu_views .view-rss').get(0).click();
- }, {
- 'disable_in_input': true
- });
-}
-
-function init_stream(divStream) {
- divStream.on('click', '.flux_header,.flux_content', function (e) { //flux_toggle
- if ($(e.target).closest('.content, .item.website, .item.link, .dropdown-menu').length > 0) {
- return;
- }
- if (!context.sides_close_article && $(e.target).is('div.flux_content')) {
- // setting for not-closing after clicking outside article area
- return;
+ el = ev.target.closest('.item.title > a');
+ if (el) { // Allow default control-click behaviour such as open in backround-tab
+ return ev.ctrlKey;
}
- const old_active = document.querySelector('.flux.current'),
- new_active = this.parentNode;
- if (e.target.tagName.toUpperCase() === 'A') { //Leave real links alone
- if (context.auto_mark_article) {
- mark_read(new_active, true);
+
+ el = ev.target.closest('.flux .content a');
+ if (el) {
+ if (!el.closest('div').classList.contains('author')) {
+ el.target = '_blank';
+ el.rel = 'noreferrer';
}
return true;
}
- toggleContent(new_active, old_active, false);
- });
-
- divStream.on('click', '.flux a.read', function () {
- mark_read(this.closest('.flux'), false);
- return false;
- });
- divStream.on('click', '.flux a.bookmark', function () {
- mark_favorite(this.closest('.flux'));
- return false;
- });
+ el = ev.target.closest('.item.share > a[href="#"]');
+ if (el) { //Print
+ const content = '<html><head><style>' +
+ 'body { font-family: Serif; text-align: justify; }' +
+ 'a { color: #000; text-decoration: none; }' +
+ 'a:after { content: " [" attr(href) "]"}' +
+ '</style></head><body>' +
+ el.closest('.flux_content').querySelector('.content').innerHTML +
+ '</body></html>';
+ const tmp_window = window.open();
+ tmp_window.document.writeln(content);
+ tmp_window.document.close();
+ tmp_window.focus();
+ tmp_window.print();
+ tmp_window.close();
+ return false;
+ }
- divStream.on('click', '.item.title > a', function (e) {
- // Allow default control-click behaviour such as open in backround-tab.
- return e.ctrlKey;
- });
- divStream.on('mouseup', '.item.title > a', function (e) {
- // Mouseup enables us to catch middle click.
- if (e.ctrlKey) {
- // CTRL+click, it will be manage by previous rule.
- return;
+ el = ev.target.closest('.item.share > a[href="POST"]');
+ if (el) { //Share by POST
+ const f = el.parentElement.querySelector('form');
+ f.disabled = false;
+ f.submit();
+ return false;
}
- if (e.which == 2) {
- // If middle click, we want same behaviour as CTRL+click.
- const ev = jQuery.Event('click');
- ev.ctrlKey = true;
- $(this).trigger(ev);
- } else if (e.which == 1) {
- // Normal click, just toggle article.
- $(this).parent().click();
+ el = ev.target.closest('.flux_header, .flux_content');
+ if (el) { //flux_toggle
+ if (ev.target.closest('.content, .item.website, .item.link, .dropdown-menu')) {
+ return true;
+ }
+ if (!context.sides_close_article && ev.target.matches('div.flux_content')) {
+ // setting for not-closing after clicking outside article area
+ return false;
+ }
+ const old_active = document.querySelector('.flux.current'),
+ new_active = el.parentNode;
+ if (ev.target.tagName.toUpperCase() === 'A') { //Leave real links alone
+ if (context.auto_mark_article) {
+ mark_read(new_active, true);
+ }
+ return true;
+ }
+ toggleContent(new_active, old_active, false);
+ return false;
}
- });
+ };
- divStream.on('click', '.flux .content a', function () {
- if (!$(this).closest('div').hasClass('author')) {
- $(this).attr('target', '_blank').attr('rel', 'noreferrer');
+ stream.onmouseup = function (ev) { // Mouseup enables us to catch middle click
+ let el = ev.target.closest('.item.title > a');
+ if (el) {
+ if (ev.ctrlKey) {
+ return; // CTRL+click, it will be manage by previous rule.
+ }
+ if (ev.which == 2) {
+ // If middle click, we want same behaviour as CTRL+click.
+ const evc = document.createEvent('click');
+ evc.ctrlKey = true;
+ el.dispatchEvent(evc);
+ } else if (ev.which == 1) {
+ // Normal click, just toggle article.
+ el.parentElement.click();
+ }
}
- });
- if (context.auto_mark_site) {
- // catch mouseup instead of click so we can have the correct behaviour
- // with middle button click (scroll button).
- divStream.on('mouseup', '.flux .link > a', function (e) {
- if (e.which == 3) {
- return;
+ if (context.auto_mark_site) {
+ // catch mouseup instead of click so we can have the correct behaviour
+ // with middle button click (scroll button).
+ el = ev.target.closest('.flux .link > a');
+ if (el) {
+ if (ev.which == 3) {
+ return;
+ }
+ mark_read(el.closest('.flux'), true);
}
+ }
+ };
- mark_read(this.closest('.flux'), true);
- });
- }
+ stream.onchange = function (ev) {
+ const checkboxTag = ev.target.closest('.checkboxTag');
+ if (checkboxTag) { //Dynamic tags
+ ev.stopPropagation();
+ const isChecked = checkboxTag.checked,
+ tagId = checkboxTag.name.replace(/^t_/, ''),
+ tagName = checkboxTag.nextElementSibling ? checkboxTag.nextElementSibling.value : '',
+ entry = checkboxTag.closest('div.flux'),
+ entryId = entry.id.replace(/^flux_/, '');
+ checkboxTag.disabled = true;
+
+ const req = new XMLHttpRequest();
+ req.open('POST', './?c=tag&a=tagEntry', true);
+ req.responseType = 'json';
+ req.onerror = function (e) {
+ checkboxTag.checked = !isChecked;
+ };
+ req.onload = function (e) {
+ if (this.status != 200) {
+ return req.onerror(e);
+ }
+ if (entry.classList.contains('not_read')) {
+ incUnreadsTag('t_' + tagId, isChecked ? 1 : -1);
+ }
+ };
+ req.onloadend = function (e) {
+ checkboxTag.disabled = false;
+ if (tagId == 0) {
+ loadDynamicTags(checkboxTag.closest('div.dropdown'));
+ }
+ };
+ req.setRequestHeader('Content-Type', 'application/json');
+ req.send(JSON.stringify({
+ _csrf: context.csrf,
+ id_tag: tagId,
+ name_tag: tagId == 0 ? tagName : '',
+ id_entry: entryId,
+ checked: isChecked,
+ }));
+ }
+ };
}
function init_nav_entries() {
- $nav_entries = $('#nav_entries');
- $nav_entries.find('.previous_entry').click(function () {
- prev_entry(false);
- return false;
- });
- $nav_entries.find('.next_entry').click(function () {
- next_entry(false);
- return false;
- });
- $nav_entries.find('.up').click(function () {
- const $active_item = $('.flux.current'),
- windowTop = $(window).scrollTop(),
- item_top = $active_item.offset().top;
+ const nav_entries = document.getElementById('nav_entries');
+ if (nav_entries) {
+ nav_entries.querySelector('.previous_entry').onclick = function (e) {
+ prev_entry(false);
+ return false;
+ };
+ nav_entries.querySelector('.next_entry').onclick = function (e) {
+ next_entry(false);
+ return false;
+ };
+ nav_entries.querySelector('.up').onclick = function (e) {
+ const active_item = document.querySelector('.flux.current'),
+ windowTop = document.documentElement.scrollTop,
+ item_top = active_item.offsetParent.offsetTop + active_item.offsetTop;
- if (windowTop > item_top) {
- $('html,body').scrollTop(item_top);
- } else {
- $('html,body').scrollTop(0);
- }
- return false;
- });
+ document.documentElement.scrollTop = windowTop > item_top ? item_top : 0;
+ return false;
+ };
+ }
}
-function loadDynamicTags($div) {
- $div.removeClass('dynamictags');
- $div.find('li.item').remove();
- const entryId = $div.closest('div.flux').attr('id').replace(/^flux_/, '');
- $.getJSON('./?c=tag&a=getTagsForEntry&id_entry=' + entryId)
- .done(function (data) {
- const $ul = $div.find('.dropdown-menu');
- $ul.append('<li class="item"><label><input class="checkboxTag" name="t_0" type="checkbox" /> <input type="text" name="newTag" /></label></li>');
- if (data && data.length) {
- for (let i = 0; i < data.length; i++) {
- const tag = data[i];
- $ul.append('<li class="item"><label><input class="checkboxTag" name="t_' + tag.id + '" type="checkbox"' +
- (tag.checked ? ' checked="checked"' : '') + '> ' + tag.name + '</label></li>');
- }
+function loadDynamicTags(div) {
+ div.classList.remove('dynamictags');
+ div.querySelectorAll('li.item').forEach(function (li) { li.remove(); });
+ const entryId = div.closest('div.flux').id.replace(/^flux_/, '');
+
+ const req = new XMLHttpRequest();
+ req.open('GET', './?c=tag&a=getTagsForEntry&id_entry=' + entryId, true);
+ req.responseType = 'json';
+ req.onerror = function (e) {
+ div.querySelectorAll('li.item').forEach(function (li) { li.remove(); });
+ div.classList.add('dynamictags');
+ };
+ req.onload = function (e) {
+ if (this.status != 200) {
+ return req.onerror(e);
}
- })
- .fail(function () {
- $div.find('li.item').remove();
- $div.addClass('dynamictags');
- });
-}
-
-function init_dynamic_tags() {
- $stream.on('click', '.dynamictags', function () {
- loadDynamicTags($(this));
- });
-
- $stream.on('change', '.checkboxTag', function (ev) {
- const $checkbox = $(this),
- isChecked = $checkbox.prop('checked'),
- tagId = $checkbox.attr('name').replace(/^t_/, ''),
- tagName = $checkbox.siblings('input[name]').val(),
- $entry = $checkbox.closest('div.flux'),
- entryId = $entry.attr('id').replace(/^flux_/, '');
- $checkbox.prop('disabled', true);
- $.ajax({
- type: 'POST',
- url: './?c=tag&a=tagEntry',
- data: {
- _csrf: context.csrf,
- id_tag: tagId,
- name_tag: tagId == 0 ? tagName : '',
- id_entry: entryId,
- checked: isChecked,
- },
- })
- .done(function () {
- if ($entry.hasClass('not_read')) {
- incUnreadsTag('t_' + tagId, isChecked ? 1 : -1);
- }
- })
- .fail(function () {
- $checkbox.prop('checked', !isChecked);
- })
- .always(function () {
- $checkbox.prop('disabled', false);
- if (tagId == 0) {
- loadDynamicTags($checkbox.closest('div.dropdown'));
+ const json = xmlHttpRequestJson(this);
+ let html = '<li class="item"><label><input class="checkboxTag" name="t_0" type="checkbox" /> <input type="text" name="newTag" /></label></li>';
+ if (json && json.length) {
+ for (let i = 0; i < json.length; i++) {
+ const tag = json[i];
+ html += '<li class="item"><label><input class="checkboxTag" name="t_' + tag.id + '" type="checkbox"' +
+ (tag.checked ? ' checked="checked"' : '') + '> ' + tag.name + '</label></li>';
}
- });
- });
+ }
+ div.querySelector('.dropdown-menu').insertAdjacentHTML('beforeend', html);
+ };
+ req.send();
}
// <actualize>
@@ -964,86 +981,93 @@ function updateFeed(feeds, feeds_count) {
if (!feed) {
return;
}
- $.ajax({
- type: 'POST',
- url: feed.url,
- data: {
- _csrf: context.csrf,
- noCommit: 1,
- },
- }).always(function (data) {
- feed_processed++;
- $('#actualizeProgress .progress').html(feed_processed + ' / ' + feeds_count);
- $('#actualizeProgress .title').html(feed.title);
-
- if (feed_processed === feeds_count) {
- $.ajax({ //Empty request to commit new articles
- type: 'POST',
- url: './?c=feed&a=actualize&id=-1&ajax=1',
- data: {
+ const req = new XMLHttpRequest();
+ req.open('POST', feed.url, true);
+ req.onloadend = function (e) {
+ feed_processed++;
+ const div = document.getElementById('actualizeProgress');
+ div.querySelector('.progress').innerHTML = feed_processed + ' / ' + feeds_count;
+ div.querySelector('.title').innerHTML = feed.title;
+ if (feed_processed === feeds_count) {
+ //Empty request to commit new articles
+ const req2 = new XMLHttpRequest();
+ req2.open('POST', './?c=feed&a=actualize&id=-1&ajax=1', true);
+ req2.onloadend = function (e) {
+ location.reload();
+ };
+ req2.setRequestHeader('Content-Type', 'application/json');
+ req2.send(JSON.stringify({
_csrf: context.csrf,
noCommit: 0,
- },
- }).always(function (data) {
- location.reload();
- });
- } else {
- updateFeed(feeds, feeds_count);
- }
- });
+ }));
+ } else {
+ updateFeed(feeds, feeds_count);
+ }
+ };
+ req.setRequestHeader('Content-Type', 'application/json');
+ req.send(JSON.stringify({
+ _csrf: context.csrf,
+ noCommit: 1,
+ }));
}
function init_actualize() {
let auto = false;
- $('#actualize').click(function () {
- if (ajax_loading) {
+ document.getElementById('actualize').onclick = function () {
+ if (context.ajax_loading) {
return false;
}
- ajax_loading = true;
-
- $.getJSON('./?c=javascript&a=actualize').done(function (data) {
- if (auto && data.feeds.length < 1) {
- auto = false;
- ajax_loading = false;
- return false;
- }
- if (data.feeds.length === 0) {
- openNotification(data.feedback_no_refresh, 'good');
- $.ajax({ //Empty request to force refresh server database cache
- type: 'POST',
- url: './?c=feed&a=actualize&id=-1&ajax=1',
- data: {
- _csrf: context.csrf,
- noCommit: 0,
- },
- }).always(function (data) {
- ajax_loading = false;
- });
- return;
- }
- //Progress bar
- const feeds_count = data.feeds.length;
- $('body').after('<div id="actualizeProgress" class="notification good">' + data.feedback_actualize +
- '<br /><span class="title">/</span><br /><span class="progress">0 / ' + feeds_count +
- '</span></div>');
- for (let i = 10; i > 0; i--) {
- updateFeed(data.feeds, feeds_count);
- }
- });
+ context.ajax_loading = true;
+
+ const req = new XMLHttpRequest();
+ req.open('GET', './?c=javascript&a=actualize', true);
+ req.responseType = 'json';
+ req.onload = function (e) {
+ const json = xmlHttpRequestJson(this);
+ if (auto && json.feeds.length < 1) {
+ auto = false;
+ context.ajax_loading = false;
+ return false;
+ }
+ if (json.feeds.length === 0) {
+ openNotification(json.feedback_no_refresh, 'good');
+ //Empty request to commit new articles
+ const req2 = new XMLHttpRequest();
+ req2.open('POST', './?c=feed&a=actualize&id=-1&ajax=1', true);
+ req2.onloadend = function (e) {
+ context.ajax_loading = false;
+ };
+ req2.setRequestHeader('Content-Type', 'application/json');
+ req2.send(JSON.stringify({
+ _csrf: context.csrf,
+ noCommit: 0,
+ }));
+ return;
+ }
+ //Progress bar
+ const feeds_count = json.feeds.length;
+ document.body.insertAdjacentHTML('beforeend', '<div id="actualizeProgress" class="notification good">' +
+ json.feedback_actualize + '<br /><span class="title">/</span><br /><span class="progress">0 / ' +
+ feeds_count + '</span></div>');
+ for (let i = 10; i > 0; i--) {
+ updateFeed(json.feeds, feeds_count);
+ }
+ };
+ req.send();
return false;
- });
+ };
if (context.auto_actualize_feeds) {
auto = true;
- $('#actualize').click();
+ document.getElementById('actualize').click();
}
}
// </actualize>
// <notification>
-var $notification = null,
+var notification = null,
notification_interval = null,
notification_working = false;
@@ -1051,37 +1075,29 @@ function openNotification(msg, status) {
if (notification_working === true) {
return false;
}
-
notification_working = true;
-
- $notification.removeClass();
- $notification.addClass('notification');
- $notification.addClass(status);
- $notification.find('.msg').html(msg);
- $notification.fadeIn(300);
+ notification.querySelector('.msg').innerHTML = msg;
+ notification.className = 'notification';
+ notification.classList.add(status);
notification_interval = setTimeout(closeNotification, 4000);
}
function closeNotification() {
- $notification.fadeOut(600, function () {
- $notification.removeClass();
- $notification.addClass('closed');
-
- clearInterval(notification_interval);
- notification_working = false;
- });
+ notification.classList.add('closed');
+ clearInterval(notification_interval);
+ notification_working = false;
}
function init_notifications() {
- $notification = $('#notification');
+ notification = document.getElementById('notification');
- $notification.find('a.close').click(function () {
- closeNotification();
- return false;
- });
+ notification.querySelector('a.close').onclick = function () {
+ closeNotification();
+ return false;
+ };
- if ($notification.find('.msg').html().length > 0) {
+ if (notification.querySelector('.msg').innerHTML.length > 0) {
notification_working = true;
notification_interval = setTimeout(closeNotification, 4000);
}
@@ -1106,9 +1122,9 @@ function notifs_html5_show(nb) {
return;
}
- const notification = new window.Notification(i18n.notif_title_articles, {
+ const notification = new window.Notification(context.i18n.notif_title_articles, {
icon: '../themes/icons/favicon-256.png',
- body: i18n.notif_body_articles.replace('%d', nb),
+ body: context.i18n.notif_body_articles.replace('%d', nb),
tag: 'freshRssNewArticles',
});
@@ -1135,40 +1151,56 @@ function init_notifs_html5() {
// </notifs html5>
function refreshUnreads() {
- $.getJSON('./?c=javascript&a=nbUnreadsPerFeed').done(function (data) {
- const isAll = document.querySelector('.category.all.active');
- let new_articles = false;
-
- $.each(data.feeds, function (feed_id, nbUnreads) {
- feed_id = 'f_' + feed_id;
- const elem = document.getElementById(feed_id),
- feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0;
-
- if ((incUnreadsFeed(null, feed_id, nbUnreads - feed_unreads) || isAll) && //Update of current view?
- (nbUnreads - feed_unreads > 0)) {
- $('#new-article').attr('aria-hidden', 'false').show();
- new_articles = true;
- }
- });
+ const req = new XMLHttpRequest();
+ req.open('GET', './?c=javascript&a=nbUnreadsPerFeed', true);
+ req.responseType = 'json';
+ req.onload = function (e) {
+ const json = xmlHttpRequestJson(this);
+ const isAll = document.querySelector('.category.all.active');
+ let new_articles = false;
+
+ Object.keys(json.feeds).forEach(function (feed_id) {
+ const nbUnreads = json.feeds[feed_id];
+ feed_id = 'f_' + feed_id;
+ const elem = document.getElementById(feed_id),
+ feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0;
+
+ if ((incUnreadsFeed(null, feed_id, nbUnreads - feed_unreads) || isAll) && //Update of current view?
+ (nbUnreads - feed_unreads > 0)) {
+ const newArticle = document.getElementById('new-article');
+ newArticle.setAttribute('aria-hidden', 'false');
+ newArticle.style.display = 'block';
+ new_articles = true;
+ }
+ });
- let nbUnreadTags = 0;
+ let nbUnreadTags = 0;
- $.each(data.tags, function (tag_id, nbUnreads) {
- nbUnreadTags += nbUnreads;
- $('#t_' + tag_id).attr('data-unread', nbUnreads)
- .children('.item-title').attr('data-unread', numberFormat(nbUnreads));
- });
+ Object.keys(json.tags).forEach(function (tag_id) {
+ const nbUnreads = json.tags[tag_id];
+ nbUnreadTags += nbUnreads;
+ const tag = document.getElementById('t_' + tag_id);
+ if (tag) {
+ tag.setAttribute('data-unread', nbUnreads);
+ tag.querySelector('.item-title').setAttribute('data-unread', numberFormat(nbUnreads));
+ }
+ });
- $('.category.tags').attr('data-unread', nbUnreadTags)
- .find('.title').attr('data-unread', numberFormat(nbUnreadTags));
+ const tags = document.querySelector('.category.tags');
+ if (tags) {
+ tags.setAttribute('data-unread', nbUnreadTags);
+ tags.querySelector('.title').setAttribute('data-unread', numberFormat(nbUnreadTags));
+ }
- const nb_unreads = str2int($('.category.all .title').attr('data-unread'));
+ const title = document.querySelector('.category.all .title'),
+ nb_unreads = title ? str2int(title.getAttribute('data-unread')) : 0;
- if (nb_unreads > 0 && new_articles) {
- faviconNbUnread(nb_unreads);
- notifs_html5_show(nb_unreads);
- }
- });
+ if (nb_unreads > 0 && new_articles) {
+ faviconNbUnread(nb_unreads);
+ notifs_html5_show(nb_unreads);
+ }
+ };
+ req.send();
}
//<endless_mode>
@@ -1177,49 +1209,59 @@ var url_load_more = '',
box_load_more = null;
function load_more_posts() {
- if (load_more || url_load_more === '' || box_load_more === null) {
+ if (load_more || !url_load_more || !box_load_more) {
return;
}
-
load_more = true;
document.getElementById('load_more').classList.add('loading');
- $.get(url_load_more, function (data) {
- box_load_more.children('.flux:last').after($('#stream', data).children('.flux, .day'));
- $('.pagination').replaceWith($('.pagination', data));
- if (context.display_order === 'ASC') {
- $('#nav_menu_read_all .read_all').attr(
- 'formaction', $('#bigMarkAsRead').attr('formaction')
- );
- } else {
- $('#bigMarkAsRead').attr(
- 'formaction', $('#nav_menu_read_all .read_all').attr('formaction')
- );
- }
- $('[id^=day_]').each(function (i) {
- const ids = $('[id="' + this.id + '"]');
- if (ids.length > 1) {
- $('[id="' + this.id + '"]:gt(0)').remove();
+ const req = new XMLHttpRequest();
+ req.open('GET', url_load_more, true);
+ req.responseType = 'document';
+ req.onload = function (e) {
+ const html = this.response,
+ formPagination = document.getElementById('mark-read-pagination');
+
+ const streamAdopted = document.adoptNode(html.getElementById('stream'));
+ streamAdopted.querySelectorAll('.flux, .day').forEach(function (div) {
+ box_load_more.insertBefore(div, formPagination);
+ });
+
+ const paginationOld = formPagination.querySelector('.pagination'),
+ paginationNew = streamAdopted.querySelector('.pagination');
+ formPagination.replaceChild(paginationNew, paginationOld);
+
+ if (context.display_order === 'ASC') {
+ document.querySelector('#nav_menu_read_all .read_all').formAction =
+ document.getElementById('bigMarkAsRead').formAction;
+ } else {
+ const bigMarkAsRead = document.getElementById('bigMarkAsRead');
+ if (bigMarkAsRead) {
+ bigMarkAsRead.formAction = document.querySelector('#nav_menu_read_all .read_all').formAction;
+ }
}
- });
- init_load_more(box_load_more);
+ document.querySelectorAll('[id^=day_]').forEach(function (div) {
+ const ids = document.querySelectorAll('[id="' + div.id + '"]');
+ for (let i = ids.length - 1; i > 0; i--) { //Keep only the first
+ ids[i].remove();
+ }
+ });
- const bigMarkAsRead = document.getElementById('bigMarkAsRead'),
- div_load_more = document.getElementById('load_more');
- if (bigMarkAsRead) {
- bigMarkAsRead.removeAttribute('disabled');
- }
- if (div_load_more) {
- div_load_more.classList.remove('loading');
- }
+ init_load_more(box_load_more);
- load_more = false;
- });
-}
+ const bigMarkAsRead = document.getElementById('bigMarkAsRead'),
+ div_load_more = document.getElementById('load_more');
+ if (bigMarkAsRead) {
+ bigMarkAsRead.removeAttribute('disabled');
+ }
+ if (div_load_more) {
+ div_load_more.classList.remove('loading');
+ }
-function focus_search() {
- $('#search').focus();
+ load_more = false;
+ };
+ req.send();
}
var freshrssLoadMoreEvent = document.createEvent('Event');
@@ -1229,193 +1271,40 @@ function init_load_more(box) {
box_load_more = box;
document.body.dispatchEvent(freshrssLoadMoreEvent);
- const $next_link = $('#load_more');
- if (!$next_link.length) {
+ const next_link = document.getElementById('load_more');
+ if (!next_link) {
// no more article to load
url_load_more = '';
return;
}
- url_load_more = $next_link.attr('href');
+ url_load_more = next_link.href;
- $next_link.click(function () {
- load_more_posts();
- return false;
- });
+ next_link.onclick = function (e) {
+ load_more_posts();
+ return false;
+ };
}
//</endless_mode>
-//<crypto form (Web login)>
-function poormanSalt() { //If crypto.getRandomValues is not available
- const base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz';
- let text = '$2a$04$';
- for (let i = 22; i > 0; i--) {
- text += base.charAt(Math.floor(Math.random() * 64));
- }
- return text;
-}
-
-function init_crypto_form() {
- /* globals dcodeIO */
- const $crypto_form = $('#crypto-form');
- if ($crypto_form.length === 0) {
- return;
- }
-
- if (!(window.dcodeIO)) {
- if (window.console) {
- console.log('FreshRSS waiting for bcrypt.js…');
- }
- setTimeout(init_crypto_form, 100);
- return;
- }
-
- $crypto_form.on('submit', function () {
- const $submit_button = $(this).find('button[type="submit"]');
- $submit_button.attr('disabled', '');
-
- let success = false;
- $.ajax({
- url: './?c=javascript&a=nonce&user=' + $('#username').val(),
- dataType: 'json',
- async: false
- }).done(function (data) {
- if (!data.salt1 || !data.nonce) {
- openNotification('Invalid user!', 'bad');
- } else {
- try {
- const strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'),
- s = dcodeIO.bcrypt.hashSync($('#passwordPlain').val(), data.salt1),
- c = dcodeIO.bcrypt.hashSync(data.nonce + s, strong ? dcodeIO.bcrypt.genSaltSync(4) : poormanSalt());
- $('#challenge').val(c);
- if (!s || !c) {
- openNotification('Crypto error!', 'bad');
- } else {
- success = true;
- }
- } catch (e) {
- openNotification('Crypto exception! ' + e, 'bad');
+function init_confirm_action() {
+ document.body.onclick = function (ev) {
+ const b = ev.target.closest('.confirm');
+ if (b) {
+ let str_confirmation = this.getAttribute('data-str-confirm');
+ if (!str_confirmation) {
+ str_confirmation = context.i18n.confirmation_default;
}
+ return confirm(str_confirmation);
}
- }).fail(function () {
- openNotification('Communication error!', 'bad');
- });
-
- $submit_button.removeAttr('disabled');
- return success;
- });
-}
-//</crypto form (Web login)>
-
-function init_confirm_action() {
- $('body').on('click', '.confirm', function () {
- let str_confirmation = $(this).attr('data-str-confirm');
- if (!str_confirmation) {
- str_confirmation = i18n.confirmation_default;
- }
-
- return confirm(str_confirmation);
- });
- $('button.confirm').removeAttr('disabled');
-}
-
-function init_print_action() {
- $('.item.share > a[href="#"]').click(function (e) {
- const content = '<html><head><style>' +
- 'body { font-family: Serif; text-align: justify; }' +
- 'a { color: #000; text-decoration: none; }' +
- 'a:after { content: " [" attr(href) "]"}' +
- '</style></head><body>' +
- $(e.target).closest('.flux_content').find('.content').html() +
- '</body></html>';
-
- const tmp_window = window.open();
- tmp_window.document.writeln(content);
- tmp_window.document.close();
- tmp_window.focus();
- tmp_window.print();
- tmp_window.close();
-
- return false;
- });
-}
-
-function init_post_action() {
- $('.item.share > a[href="POST"]').click(function (e) {
- e.preventDefault();
- const $form = $(this).next('form');
- $.post($form.data('url'), $form.serialize());
- });
-}
-
-var shares = 0;
-
-function init_share_observers() {
- shares = $('.group-share').length;
-
- $('.share.add').on('click', function (e) {
- const $opt = $(this).siblings('select').find(':selected');
- let row = $(this).parents('form').data($opt.data('form'));
- row = row.replace(/##label##/g, $opt.html().trim());
- row = row.replace(/##type##/g, $opt.val());
- row = row.replace(/##help##/g, $opt.data('help'));
- row = row.replace(/##key##/g, shares);
- row = row.replace(/##method##/g, $opt.data('method'));
- row = row.replace(/##field##/g, $opt.data('field'));
- $(this).parents('.form-group').before(row);
- shares++;
-
- return false;
- });
-}
-
-function init_stats_observers() {
- $('.select-change').on('change', function (e) {
- redirect($(this).find(':selected').data('url'));
- });
-}
-
-function init_remove_observers() {
- $('.post').on('click', 'a.remove', function (e) {
- const remove_what = $(this).attr('data-remove');
- if (remove_what !== undefined) {
- $('#' + remove_what).remove();
- }
- return false;
- });
-}
-
-function init_feed_observers() {
- $('select[id="category"]').on('change', function () {
- const $detail = $('#new_category_name').parent();
- if ($(this).val() === 'nc') {
- $detail.attr('aria-hidden', 'false').show();
- $detail.find('input').focus();
- } else {
- $detail.attr('aria-hidden', 'true').hide();
- }
- });
-}
-
-function init_password_observers() {
- $('.toggle-password').on('mousedown', function (e) {
- const $button = $(this),
- $passwordField = $('#' + $button.attr('data-toggle'));
- $passwordField.attr('type', 'text');
- $button.addClass('active');
- return false;
- }).on('mouseup', function (e) {
- const $button = $(this),
- $passwordField = $('#' + $button.attr('data-toggle'));
- $passwordField.attr('type', 'password');
- $button.removeClass('active');
- return false;
- });
+ };
+ document.querySelectorAll('button.confirm').forEach(function (b) { b.disabled = false; });
}
function faviconNbUnread(n) {
if (typeof n === 'undefined') {
- n = str2int($('.category.all .title').attr('data-unread'));
+ const t = document.querySelector('.category.all .title');
+ n = t ? str2int(t.getAttribute('data-unread')) : 0;
}
//http://remysharp.com/2010/08/24/dynamic-favicons/
const canvas = document.createElement('canvas'),
@@ -1442,79 +1331,16 @@ function faviconNbUnread(n) {
ctx.fillText(text, 0, canvas.height - 1);
}
link.href = canvas.toDataURL('image/png');
- $('link[rel~=icon]').remove();
+ document.querySelector('link[rel~=icon]').remove();
document.head.appendChild(link);
};
img.src = '../favicon.ico';
}
}
-function init_slider_observers() {
- const $slider = $('#slider'),
- $closer = $('#close-slider');
- if ($slider.length < 1) {
- return;
- }
-
- $('.post').on('click', '.open-slider', function () {
- if (ajax_loading) {
- return false;
- }
-
- ajax_loading = true;
-
- $.ajax({
- type: 'GET',
- url: $(this).attr('href'),
- data: { ajax: true }
- }).done(function (data) {
- $slider.html(data);
- $closer.addClass('active');
- $slider.addClass('active');
- ajax_loading = false;
- });
-
- return false;
- });
-
- $closer.on('click', function () {
- $closer.removeClass('active');
- $slider.removeClass('active');
- return false;
- });
-}
-
-function init_configuration_alert() {
- $(window).on('submit', function (e) {
- window.hasSubmit = true;
- });
- $(window).on('beforeunload', function (e) {
- if (window.hasSubmit) {
- return;
- }
- const inputs = document.querySelectorAll('[data-leave-validation]');
- for (let i = inputs.length - 1; i >= 0; i--) {
- const input = inputs[i];
- if (input.type === 'checkbox' || input.type === 'radio') {
- if (input.checked != input.getAttribute('data-leave-validation')) {
- return false;
- }
- } else if (input.value != input.getAttribute('data-leave-validation')) {
- return false;
- }
- }
- });
-}
-
-function init_subscription() {
- $('body').on('click', '.bookmarkClick', function (e) {
- return false;
- });
-}
-
function init_normal() {
- $stream = $('#stream');
- if ($stream.length < 1) {
+ const stream = document.getElementById('stream');
+ if (!stream) {
if (window.console) {
console.log('FreshRSS waiting for content…');
}
@@ -1522,59 +1348,32 @@ function init_normal() {
return;
}
init_column_categories();
- init_stream($stream);
+ init_stream(stream);
init_shortcuts();
init_actualize();
faviconNbUnread();
}
function init_beforeDOM() {
- if (!window.$) {
- if (window.console) {
- console.log('FreshRSS waiting for jQuery…');
- }
- setTimeout(init_beforeDOM, 100);
- return;
- }
if (['normal', 'reader', 'global'].indexOf(context.current_view) >= 0) {
init_normal();
}
}
function init_afterDOM() {
- if (!window.$) {
- if (window.console) {
- console.log('FreshRSS waiting again for jQuery…');
- }
- setTimeout(init_afterDOM, 100);
- return;
- }
init_notifications();
init_confirm_action();
- $stream = $('#stream');
- if ($stream.length > 0) {
- init_load_more($stream);
+ const stream = document.getElementById('stream');
+ if (stream) {
+ init_load_more(stream);
init_posts();
init_nav_entries();
- init_dynamic_tags();
- init_print_action();
- init_post_action();
init_notifs_html5();
setInterval(refreshUnreads, 120000);
- } else {
- init_subscription();
- init_crypto_form();
- init_share_observers();
- init_remove_observers();
- init_feed_observers();
- init_password_observers();
- init_stats_observers();
- init_slider_observers();
- init_configuration_alert();
}
if (window.console) {
- console.log('FreshRSS init done.');
+ console.log('FreshRSS main init done.');
}
}
@@ -1582,11 +1381,11 @@ init_beforeDOM(); //Can be called before DOM is fully loaded
if (document.readyState && document.readyState !== 'loading') {
init_afterDOM();
-} else if (document.addEventListener) {
+} else {
document.addEventListener('DOMContentLoaded', function () {
- if (window.console) {
- console.log('FreshRSS waiting for DOMContentLoaded…');
- }
- init_afterDOM();
- }, false);
+ if (window.console) {
+ console.log('FreshRSS waiting for DOMContentLoaded…');
+ }
+ init_afterDOM();
+ }, false);
}
diff --git a/p/scripts/repartition.js b/p/scripts/repartition.js
index be70456fa..e71fa71c4 100644
--- a/p/scripts/repartition.js
+++ b/p/scripts/repartition.js
@@ -1,6 +1,6 @@
"use strict";
/* globals Flotr, numberFormat */
-/* jshint globalstrict: true */
+/* jshint esversion:6, strict:global */
function initStats() {
if (!window.Flotr) {
@@ -10,7 +10,7 @@ function initStats() {
window.setTimeout(initStats, 50);
return;
}
- var jsonRepartition = document.getElementById('jsonRepartition'),
+ const jsonRepartition = document.getElementById('jsonRepartition'),
stats = JSON.parse(jsonRepartition.innerHTML);
jsonRepartition.outerHTML = '';
// Entry per hour
diff --git a/p/scripts/shortcut.js b/p/scripts/shortcut.js
deleted file mode 100644
index e78cf6f5e..000000000
--- a/p/scripts/shortcut.js
+++ /dev/null
@@ -1,225 +0,0 @@
-/**
- * http://www.openjs.com/scripts/events/keyboard_shortcuts/
- * Version : 2.01.B
- * By Binny V A
- * License : BSD
- */
-shortcut = {
- 'all_shortcuts':{},//All the shortcuts are stored in this array
- 'add': function(shortcut_combination,callback,opt) {
- //Provide a set of default options
- var default_options = {
- 'type':'keydown',
- 'propagate':false,
- 'disable_in_input':false,
- 'target':document,
- 'keycode':false
- }
- if(!opt) opt = default_options;
- else {
- for(var dfo in default_options) {
- if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
- }
- }
-
- var ele = opt.target;
- if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
- var ths = this;
- shortcut_combination = shortcut_combination.toLowerCase();
-
- //The function to be called at keypress
- var func = function(e) {
- e = e || window.event;
-
- if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
- var element;
- if(e.target) element=e.target;
- else if(e.srcElement) element=e.srcElement;
- if(element.nodeType==3) element=element.parentNode;
-
- if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
- }
-
- //Find Which key is pressed
- if (e.keyCode) code = e.keyCode;
- else if (e.which) code = e.which;
- if (code == 32 || (code >= 48 && code <= 90) || (code >= 96 && code <= 111) || (code >= 186 && code <= 192) || (code >= 219 && code <= 222)) { //FreshRSS
- var character = String.fromCharCode(code).toLowerCase();
- }
-
- if(code == 188) character=","; //If the user presses , when the type is onkeydown
- if(code == 190) character="."; //If the user presses , when the type is onkeydown
-
- var keys = shortcut_combination.split("+");
- //Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
- var kp = 0;
-
- //Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
- var shift_nums = {
- "`":"~",
- "1":"!",
- "2":"@",
- "3":"#",
- "4":"$",
- "5":"%",
- "6":"^",
- "7":"&",
- "8":"*",
- "9":"(",
- "0":")",
- "-":"_",
- "=":"+",
- ";":":",
- "'":"\"",
- ",":"<",
- ".":">",
- "/":"?",
- "\\":"|"
- }
- //Special Keys - and their codes
- var special_keys = {
- 'esc':27,
- 'escape':27,
- 'tab':9,
- 'space':32,
- 'return':13,
- 'enter':13,
- 'backspace':8,
-
- 'scrolllock':145,
- 'scroll_lock':145,
- 'scroll':145,
- 'capslock':20,
- 'caps_lock':20,
- 'caps':20,
- 'numlock':144,
- 'num_lock':144,
- 'num':144,
-
- 'pause':19,
- 'break':19,
-
- 'insert':45,
- 'home':36,
- 'delete':46,
- 'end':35,
-
- 'pageup':33,
- 'page_up':33,
- 'pu':33,
-
- 'pagedown':34,
- 'page_down':34,
- 'pd':34,
-
- 'left':37,
- 'up':38,
- 'right':39,
- 'down':40,
-
- 'f1':112,
- 'f2':113,
- 'f3':114,
- 'f4':115,
- 'f5':116,
- 'f6':117,
- 'f7':118,
- 'f8':119,
- 'f9':120,
- 'f10':121,
- 'f11':122,
- 'f12':123
- }
-
- var modifiers = {
- shift: { wanted:false, pressed:false},
- ctrl : { wanted:false, pressed:false},
- alt : { wanted:false, pressed:false},
- meta : { wanted:false, pressed:false} //Meta is Mac specific
- };
-
- if(e.ctrlKey) modifiers.ctrl.pressed = true;
- if(e.shiftKey) modifiers.shift.pressed = true;
- if(e.altKey) modifiers.alt.pressed = true;
- if(e.metaKey) modifiers.meta.pressed = true;
-
- for(var i=0; k=keys[i],i<keys.length; i++) {
- //Modifiers
- if(k == 'ctrl' || k == 'control') {
- kp++;
- modifiers.ctrl.wanted = true;
-
- } else if(k == 'shift') {
- kp++;
- modifiers.shift.wanted = true;
-
- } else if(k == 'alt') {
- kp++;
- modifiers.alt.wanted = true;
- } else if(k == 'meta') {
- kp++;
- modifiers.meta.wanted = true;
- } else if(k.length > 1) { //If it is a special key
- if(special_keys[k] == code) kp++;
-
- } else if(opt['keycode']) {
- if(opt['keycode'] == code) kp++;
-
- } else { //The special keys did not match
- if(character == k) kp++;
- else {
- if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
- character = shift_nums[character];
- if(character == k) kp++;
- }
- }
- }
- }
-
- if(kp == keys.length &&
- modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
- modifiers.shift.pressed == modifiers.shift.wanted &&
- modifiers.alt.pressed == modifiers.alt.wanted &&
- modifiers.meta.pressed == modifiers.meta.wanted) {
- callback(e);
-
- if(!opt['propagate']) { //Stop the event
- //e.cancelBubble is supported by IE - this will kill the bubbling process.
- e.cancelBubble = true;
- e.returnValue = false;
-
- //e.stopPropagation works in Firefox.
- if (e.stopPropagation) {
- e.stopPropagation();
- e.preventDefault();
- }
- return false;
- }
- }
- }
- this.all_shortcuts[shortcut_combination] = {
- 'callback':func,
- 'target':ele,
- 'event': opt['type']
- };
- //Attach the function with the event
- if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
- else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
- else ele['on'+opt['type']] = func;
- },
-
- //Remove the shortcut - just specify the shortcut and I will remove the binding
- 'remove':function(shortcut_combination) {
- shortcut_combination = shortcut_combination.toLowerCase();
- var binding = this.all_shortcuts[shortcut_combination];
- delete(this.all_shortcuts[shortcut_combination])
- if(!binding) return;
- var type = binding['event'];
- var ele = binding['target'];
- var callback = binding['callback'];
-
- if(ele.detachEvent) ele.detachEvent('on'+type, callback);
- else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
- else ele['on'+type] = false;
- }
-} \ No newline at end of file
diff --git a/p/scripts/stats.js b/p/scripts/stats.js
index 9cd14721c..b47188d77 100644
--- a/p/scripts/stats.js
+++ b/p/scripts/stats.js
@@ -1,6 +1,6 @@
"use strict";
/* globals Flotr, numberFormat */
-/* jshint globalstrict: true */
+/* jshint esversion:6, strict:global */
function initStats() {
if (!window.Flotr) {
@@ -10,12 +10,12 @@ function initStats() {
window.setTimeout(initStats, 50);
return;
}
- var jsonStats = document.getElementById('jsonStats'),
+ const jsonStats = document.getElementById('jsonStats'),
stats = JSON.parse(jsonStats.innerHTML);
jsonStats.outerHTML = '';
// Entry per day
- var avg = [];
- for (var i = -31; i <= 0; i++) {
+ const avg = [];
+ for (let i = -31; i <= 0; i++) {
avg.push([i, stats.average]);
}
Flotr.draw(document.getElementById('statsEntryPerDay'),
diff --git a/p/themes/base-theme/template.css b/p/themes/base-theme/template.css
index 099aee916..2ea058786 100644
--- a/p/themes/base-theme/template.css
+++ b/p/themes/base-theme/template.css
@@ -280,7 +280,7 @@ a.btn {
left: 0; right: 0;
display: block;
z-index: -10;
- cursor: default;
+ cursor: default;
}
.separator {
display: block;
@@ -418,8 +418,10 @@ a.btn {
}
.tree-folder-items {
- padding: 0;
list-style: none;
+ max-height: 200em;
+ padding: 0;
+ transition: max-height .3s linear;
}
.tree-folder-title {
display: block;
@@ -502,7 +504,8 @@ a.btn {
padding: 0px 15px;
}
.aside_feed .tree-folder-items:not(.active) {
- display: none;
+ max-height: 0;
+ overflow: hidden;
}
.aside_feed .tree-folder-items .dropdown {
vertical-align: top;
@@ -632,9 +635,13 @@ br + br + br {
z-index: 10;
background: #fff;
border: 1px solid #aaa;
+ opacity: 1;
+ visibility: visible;
+ transition: visibility 0s, opacity .3s linear;
}
.notification.closed {
- display: none;
+ opacity: 0;
+ visibility: hidden;
}
.notification a.close {
position: absolute;
@@ -710,15 +717,15 @@ br + br + br {
/*=== LOGIN VIEW */
/*================*/
.formLogin .header > .item {
- padding: 10px 30px;
+ padding: 10px 30px;
}
.formLogin .header > .item.title {
- text-align: left;
+ text-align: left;
}
.formLogin .header > .item.configure {
- text-align: right;
+ text-align: right;
}
@@ -731,14 +738,29 @@ br + br + br {
#stream.global .box {
text-align: left;
}
-
+#global > #panel {
+ bottom: 99vh;
+ display: block;
+ transition: visibility .3s, bottom .3s;
+ visibility: hidden;
+}
+#global > #panel.visible {
+ bottom: 1em;
+ visibility: visible;
+}
/*=== Panel */
#overlay {
- display: none;
position: fixed;
top: 0; bottom: 0;
left: 0; right: 0;
background: rgba(0, 0, 0, 0.9);
+ opacity: 0;
+ transition: visibility .3s, opacity .3s;
+ visibility: hidden;
+}
+#overlay.visible {
+ opacity: 1;
+ visibility: visible;
}
#panel {
display: none;