redeyed-jekyll-theme 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +52 -0
  4. data/_config.yml +328 -0
  5. data/_data/assets/cross-origin.yml +65 -0
  6. data/_data/assets/self-host.yml +51 -0
  7. data/_data/authors.yml +12 -0
  8. data/_data/contact.yml +16 -0
  9. data/_data/locale/en.yml +78 -0
  10. data/_data/share.yml +16 -0
  11. data/_includes/back-to-top.html +2 -0
  12. data/_includes/breadcrumbs.html +1 -0
  13. data/_includes/comments.html +0 -0
  14. data/_includes/copyright.html +1 -0
  15. data/_includes/datetime.html +0 -0
  16. data/_includes/favicons.html +14 -0
  17. data/_includes/footer.html +1 -0
  18. data/_includes/ga-site-tag.html +8 -0
  19. data/_includes/google-tags-body.html +4 -0
  20. data/_includes/google-tags-head.html +6 -0
  21. data/_includes/head.html +72 -0
  22. data/_includes/header-banner.html +1 -0
  23. data/_includes/header-wide.html +1 -0
  24. data/_includes/header.html +1 -0
  25. data/_includes/javascript.html +89 -0
  26. data/_includes/jsdelivr-combine.html +18 -0
  27. data/_includes/lang.html +6 -0
  28. data/_includes/mermaid.html +1 -0
  29. data/_includes/mode-toggle.html +138 -0
  30. data/_includes/navbar.html +6 -0
  31. data/_includes/no-linenos.html +0 -0
  32. data/_includes/options.html +13 -0
  33. data/_includes/pagination.html +0 -0
  34. data/_includes/panel-bottom.html +1 -0
  35. data/_includes/panel-foot-2.html +1 -0
  36. data/_includes/panel-foot-3.html +1 -0
  37. data/_includes/panel-foot-4.html +1 -0
  38. data/_includes/panel-foot-full.html +1 -0
  39. data/_includes/panel-top-2.html +1 -0
  40. data/_includes/panel-top-3.html +1 -0
  41. data/_includes/panel-top-4.html +1 -0
  42. data/_includes/panel-top-full.html +1 -0
  43. data/_includes/post-nav.html +0 -0
  44. data/_includes/post-share.html +0 -0
  45. data/_includes/readtime.html +0 -0
  46. data/_includes/related-posts.html +0 -0
  47. data/_includes/search-loader.html +46 -0
  48. data/_includes/search-results.html +1 -0
  49. data/_includes/sidebar.html +0 -0
  50. data/_includes/toc.html +0 -0
  51. data/_javascript/common/back-to-top.js +20 -0
  52. data/_javascript/common/mode-toggle.js +13 -0
  53. data/_javascript/common/scroll-helper.js +36 -0
  54. data/_javascript/common/search-display.js +129 -0
  55. data/_javascript/common/sidebar.js +30 -0
  56. data/_javascript/common/tooltip-loader.js +6 -0
  57. data/_javascript/common/topbar-switcher.js +90 -0
  58. data/_javascript/common/topbar-title.js +67 -0
  59. data/_javascript/utils/category-collapse.js +30 -0
  60. data/_javascript/utils/checkbox.js +12 -0
  61. data/_javascript/utils/clipboard.js +133 -0
  62. data/_javascript/utils/img-extra.js +47 -0
  63. data/_javascript/utils/locale-datetime.js +45 -0
  64. data/_javascript/utils/pageviews.js +250 -0
  65. data/_javascript/utils/smooth-scroll.js +96 -0
  66. data/_layouts/archive.html +0 -0
  67. data/_layouts/categories.html +0 -0
  68. data/_layouts/category.html +0 -0
  69. data/_layouts/compress.html +10 -0
  70. data/_layouts/default.html +5 -0
  71. data/_layouts/page.html +5 -0
  72. data/_layouts/panel-both.html +14 -0
  73. data/_layouts/panel-left.html +14 -0
  74. data/_layouts/panel-none.html +81 -0
  75. data/_layouts/panel-right.html +14 -0
  76. data/_layouts/post.html +5 -0
  77. data/_layouts/tag.html +0 -0
  78. data/_layouts/tags.html +0 -0
  79. data/_sass/addon/common.scss +1630 -0
  80. data/_sass/addon/mixins.scss +144 -0
  81. data/_sass/addon/syntax.scss +0 -0
  82. data/_sass/addon/variables.scss +31 -0
  83. data/_sass/colors/brew-scheme.scss +7 -0
  84. data/_sass/colors/dark-scheme.scss +21 -0
  85. data/_sass/colors/dark-syntax.scss +88 -0
  86. data/_sass/colors/light-scheme.scss +7 -0
  87. data/_sass/colors/light-syntax.scss +84 -0
  88. data/_sass/layout/page.scss +0 -0
  89. data/_sass/layout/panel-both.scss +0 -0
  90. data/_sass/layout/panel-left.scss +0 -0
  91. data/_sass/layout/panel-none.scss +146 -0
  92. data/_sass/layout/panel-right.scss +0 -0
  93. data/_sass/layout/post.scss +0 -0
  94. data/_sass/redeyed-jekyll-theme.scss +26 -0
  95. data/_sass/variables-hook.scss +0 -0
  96. data/assets/css/style.scss +8 -0
  97. data/assets/img/d20R/d20R-128.png +0 -0
  98. data/assets/img/d20R/d20R-16.png +0 -0
  99. data/assets/img/d20R/d20R-200.png +0 -0
  100. data/assets/img/d20R/d20R-240.png +0 -0
  101. data/assets/img/d20R/d20R-32.png +0 -0
  102. data/assets/img/d20R/d20R-320.png +0 -0
  103. data/assets/img/d20R/d20R-48.png +0 -0
  104. data/assets/img/d20R/d20R-500.png +0 -0
  105. data/assets/img/d20R/d20R-600.png +0 -0
  106. data/assets/img/d20R/d20R-64.png +0 -0
  107. data/assets/img/d20R/d20R.svg +98 -0
  108. data/assets/img/d20RB/d20RB-128.png +0 -0
  109. data/assets/img/d20RB/d20RB-16.png +0 -0
  110. data/assets/img/d20RB/d20RB-200.png +0 -0
  111. data/assets/img/d20RB/d20RB-240.png +0 -0
  112. data/assets/img/d20RB/d20RB-32.png +0 -0
  113. data/assets/img/d20RB/d20RB-320.png +0 -0
  114. data/assets/img/d20RB/d20RB-48.png +0 -0
  115. data/assets/img/d20RB/d20RB-500.png +0 -0
  116. data/assets/img/d20RB/d20RB-600.png +0 -0
  117. data/assets/img/d20RB/d20RB-64.png +0 -0
  118. data/assets/img/d20RB/d20RB.svg +103 -0
  119. data/assets/img/favicons/android-chrome-192x192.png +0 -0
  120. data/assets/img/favicons/android-chrome-512x512.png +0 -0
  121. data/assets/img/favicons/apple-touch-icon.png +0 -0
  122. data/assets/img/favicons/browserconfig.xml +12 -0
  123. data/assets/img/favicons/favicon-16x16.png +0 -0
  124. data/assets/img/favicons/favicon-32x32.png +0 -0
  125. data/assets/img/favicons/favicon.ico +0 -0
  126. data/assets/img/favicons/mstile-150x150.png +0 -0
  127. data/assets/img/favicons/mstile-310x150.png +0 -0
  128. data/assets/img/favicons/mstile-310x310.png +0 -0
  129. data/assets/img/favicons/mstile-70x70.png +0 -0
  130. data/assets/img/favicons/safari-pinned-tab.svg +112 -0
  131. data/assets/img/favicons/site.webmanifest +19 -0
  132. data/assets/js/data/search.json +19 -0
  133. data/assets/js/data/swcache.js +54 -0
  134. data/assets/js/fetch.js +458 -0
  135. data/assets/js/pwa/app.js +47 -0
  136. data/assets/js/pwa/sw.js +89 -0
  137. data/assets/js/pwa/unregister.js +12 -0
  138. data/src/404.html +10 -0
  139. data/src/favicon.ico +0 -0
  140. data/src/feed.xml +61 -0
  141. data/src/index.md +22 -0
  142. data/src/robots.txt +10 -0
  143. data/src/style/html.md +7 -0
  144. data/src/style/index.md +11 -0
  145. data/src/style/markdown.md +108 -0
  146. metadata +375 -0
@@ -0,0 +1,129 @@
1
+ /*
2
+ * This script make #search-result-wrapper switch to unloaded or shown automatically.
3
+ */
4
+
5
+ $(function() {
6
+ const btnSbTrigger = $("#sidebar-trigger");
7
+ const btnSearchTrigger = $("#search-trigger");
8
+ const btnCancel = $("#search-cancel");
9
+ const main = $("#main");
10
+ const topbarTitle = $("#topbar-title");
11
+ const searchWrapper = $("#search-wrapper");
12
+ const resultWrapper = $("#search-result-wrapper");
13
+ const results = $("#search-results");
14
+ const input = $("#search-input");
15
+ const hints = $("#search-hints");
16
+
17
+ const scrollBlocker = (function () {
18
+ let offset = 0;
19
+ return {
20
+ block() {
21
+ offset = window.scrollY;
22
+ $("html,body").scrollTop(0);
23
+ },
24
+ release() {
25
+ $("html,body").scrollTop(offset);
26
+ },
27
+ getOffset() {
28
+ return offset;
29
+ }
30
+ };
31
+ }());
32
+
33
+ /*--- Actions in mobile screens (Sidebar hidden) ---*/
34
+
35
+ const mobileSearchBar = (function () {
36
+ return {
37
+ on() {
38
+ btnSbTrigger.addClass("unloaded");
39
+ topbarTitle.addClass("unloaded");
40
+ btnSearchTrigger.addClass("unloaded");
41
+ searchWrapper.addClass("d-flex");
42
+ btnCancel.addClass("loaded");
43
+ },
44
+ off() {
45
+ btnCancel.removeClass("loaded");
46
+ searchWrapper.removeClass("d-flex");
47
+ btnSbTrigger.removeClass("unloaded");
48
+ topbarTitle.removeClass("unloaded");
49
+ btnSearchTrigger.removeClass("unloaded");
50
+ }
51
+ };
52
+ }());
53
+
54
+ const resultSwitch = (function () {
55
+ let visible = false;
56
+
57
+ return {
58
+ on() {
59
+ if (!visible) {
60
+ // the block method must be called before $(#main) unloaded.
61
+ scrollBlocker.block();
62
+ resultWrapper.removeClass("unloaded");
63
+ main.addClass("unloaded");
64
+ visible = true;
65
+ }
66
+ },
67
+ off() {
68
+ if (visible) {
69
+ results.empty();
70
+ if (hints.hasClass("unloaded")) {
71
+ hints.removeClass("unloaded");
72
+ }
73
+ resultWrapper.addClass("unloaded");
74
+ main.removeClass("unloaded");
75
+
76
+ // now the release method must be called after $(#main) display
77
+ scrollBlocker.release();
78
+
79
+ input.val("");
80
+ visible = false;
81
+ }
82
+ },
83
+ isVisible() {
84
+ return visible;
85
+ }
86
+ };
87
+
88
+ }());
89
+
90
+ function isMobileView() {
91
+ return btnCancel.hasClass("loaded");
92
+ }
93
+
94
+ btnSearchTrigger.click(function() {
95
+ mobileSearchBar.on();
96
+ resultSwitch.on();
97
+ input.focus();
98
+ });
99
+
100
+ btnCancel.click(function() {
101
+ mobileSearchBar.off();
102
+ resultSwitch.off();
103
+ });
104
+
105
+ input.focus(function() {
106
+ searchWrapper.addClass("input-focus");
107
+ });
108
+
109
+ input.focusout(function() {
110
+ searchWrapper.removeClass("input-focus");
111
+ });
112
+
113
+ input.on("input", () => {
114
+ if (input.val() === "") {
115
+ if (isMobileView()) {
116
+ hints.removeClass("unloaded");
117
+ } else {
118
+ resultSwitch.off();
119
+ }
120
+
121
+ } else {
122
+ resultSwitch.on();
123
+ if (isMobileView()) {
124
+ hints.addClass("unloaded");
125
+ }
126
+ }
127
+ });
128
+
129
+ });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Expand or close the sidebar in mobile screens.
3
+ */
4
+
5
+ $(function() {
6
+
7
+ const sidebarUtil = (function () {
8
+ const ATTR_DISPLAY = "sidebar-display";
9
+ let isExpanded = false;
10
+ const body = $("body");
11
+
12
+ return {
13
+ toggle() {
14
+ if (isExpanded === false) {
15
+ body.attr(ATTR_DISPLAY, "");
16
+ } else {
17
+ body.removeAttr(ATTR_DISPLAY);
18
+ }
19
+
20
+ isExpanded = !isExpanded;
21
+ }
22
+ };
23
+
24
+ }());
25
+
26
+ $("#sidebar-trigger").click(sidebarUtil.toggle);
27
+
28
+ $("#mask").click(sidebarUtil.toggle);
29
+
30
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Initial Bootstrap Tooltip.
3
+ */
4
+ $(function () {
5
+ $("[data-toggle=\"tooltip\"]").tooltip();
6
+ });
@@ -0,0 +1,90 @@
1
+ /*
2
+ * Hide Header on scroll down
3
+ */
4
+
5
+ $(function() {
6
+ const $searchInput = $("#search-input");
7
+ const delta = ScrollHelper.getTopbarHeight();
8
+
9
+ let didScroll;
10
+ let lastScrollTop = 0;
11
+
12
+ function hasScrolled() {
13
+ let st = $(this).scrollTop();
14
+
15
+ /* Make sure they scroll more than delta */
16
+ if (Math.abs(lastScrollTop - st) <= delta) {
17
+ return;
18
+ }
19
+
20
+ if (st > lastScrollTop ) { // Scroll Down
21
+ ScrollHelper.hideTopbar();
22
+
23
+ if ($searchInput.is(":focus")) {
24
+ $searchInput.blur(); /* remove focus */
25
+ }
26
+
27
+ } else { // Scroll up
28
+ // has not yet scrolled to the bottom of the screen, that is, there is still space for scrolling
29
+ if (st + $(window).height() < $(document).height()) {
30
+
31
+ if (ScrollHelper.hasScrollUpTask()) {
32
+ return;
33
+ }
34
+
35
+ if (ScrollHelper.topbarLocked()) { // avoid redundant scroll up event from smooth scrolling
36
+ ScrollHelper.unlockTopbar();
37
+ } else {
38
+ if (ScrollHelper.orientationLocked()) { // avoid device auto scroll up on orientation change
39
+ ScrollHelper.unLockOrientation();
40
+ } else {
41
+ ScrollHelper.showTopbar();
42
+ }
43
+ }
44
+ }
45
+ }
46
+
47
+ lastScrollTop = st;
48
+
49
+ } // hasScrolled()
50
+
51
+ function handleLandscape() {
52
+ if ($(window).scrollTop() === 0) {
53
+ return;
54
+ }
55
+ ScrollHelper.lockOrientation();
56
+ ScrollHelper.hideTopbar();
57
+ }
58
+
59
+ if (screen.orientation) {
60
+ screen.orientation.onchange = () => {
61
+ const type = screen.orientation.type;
62
+ if (type === "landscape-primary" || type === "landscape-secondary") {
63
+ handleLandscape();
64
+ }
65
+ };
66
+
67
+ } else {
68
+ // for the browsers that not support `window.screen.orientation` API
69
+ $(window).on("orientationchange",() => {
70
+ if ($(window).width() < $(window).height()) { // before rotating, it is still in portrait mode.
71
+ handleLandscape();
72
+ }
73
+ });
74
+ }
75
+
76
+ $(window).scroll(() => {
77
+ if (didScroll) {
78
+ return;
79
+ }
80
+ didScroll = true;
81
+ });
82
+
83
+ setInterval(() => {
84
+ if (didScroll) {
85
+ hasScrolled();
86
+ didScroll = false;
87
+ }
88
+ }, 250);
89
+
90
+ });
@@ -0,0 +1,67 @@
1
+ /*
2
+ * Top bar title auto change while scrolling up/down in mobile screens.
3
+ */
4
+
5
+ $(function() {
6
+ const titleSelector = "div.post>h1:first-of-type";
7
+ const $pageTitle = $(titleSelector);
8
+ const $topbarTitle = $("#topbar-title");
9
+
10
+ if ($pageTitle.length === 0 /* on Home page */
11
+ || $pageTitle.hasClass("dynamic-title")
12
+ || $topbarTitle.is(":hidden")) {/* not in mobile views */
13
+ return;
14
+ }
15
+
16
+ const defaultTitleText = $topbarTitle.text().trim();
17
+ let pageTitleText = $pageTitle.text().trim();
18
+ let hasScrolled = false;
19
+ let lastScrollTop = 0;
20
+
21
+ if ($("#page-category").length || $("#page-tag").length) {
22
+ /* The title in Category or Tag page will be "<title> <count_of_posts>" */
23
+ if (/\s/.test(pageTitleText)) {
24
+ pageTitleText = pageTitleText.replace(/[0-9]/g, "").trim();
25
+ }
26
+ }
27
+
28
+ // When the page is scrolled down and then refreshed, the topbar title needs to be initialized
29
+ if ($pageTitle.offset().top < $(window).scrollTop()) {
30
+ $topbarTitle.text(pageTitleText);
31
+ }
32
+
33
+ let options = {
34
+ rootMargin: '-48px 0px 0px 0px', // 48px equals to the topbar height (3rem)
35
+ threshold: [0, 1]
36
+ };
37
+
38
+ let observer = new IntersectionObserver((entries) => {
39
+ if (!hasScrolled) {
40
+ hasScrolled = true;
41
+ return;
42
+ }
43
+
44
+ let curScrollTop = $(window).scrollTop();
45
+ let isScrollDown = lastScrollTop < curScrollTop;
46
+ lastScrollTop = curScrollTop;
47
+ let heading = entries[0];
48
+
49
+ if (isScrollDown) {
50
+ if (heading.intersectionRatio === 0) {
51
+ $topbarTitle.text(pageTitleText);
52
+ }
53
+ } else {
54
+ if (heading.intersectionRatio === 1) {
55
+ $topbarTitle.text(defaultTitleText);
56
+ }
57
+ }
58
+ }, options);
59
+
60
+ observer.observe(document.querySelector(titleSelector));
61
+
62
+ /* Click title will scroll to top */
63
+ $topbarTitle.click(function() {
64
+ $("body,html").animate({scrollTop: 0}, 800);
65
+ });
66
+
67
+ });
@@ -0,0 +1,30 @@
1
+ /*
2
+ * Tab 'Categories' expand/close effect.
3
+ */
4
+
5
+ $(function() {
6
+ const childPrefix = "l_";
7
+ const parentPrefix = "h_";
8
+ const collapse = $(".collapse");
9
+
10
+ /* close up top-category */
11
+ collapse.on("hide.bs.collapse", function () { /* Bootstrap collapse events. */
12
+ const parentId = parentPrefix + $(this).attr("id").substring(childPrefix.length);
13
+ if (parentId) {
14
+ $(`#${parentId} .far.fa-folder-open`).attr("class", "far fa-folder fa-fw");
15
+ $(`#${parentId} i.fas`).addClass("rotate");
16
+ $(`#${parentId}`).removeClass("hide-border-bottom");
17
+ }
18
+ });
19
+
20
+ /* expand the top category */
21
+ collapse.on("show.bs.collapse", function() {
22
+ const parentId = parentPrefix + $(this).attr("id").substring(childPrefix.length);
23
+ if (parentId) {
24
+ $(`#${parentId} .far.fa-folder`).attr("class", "far fa-folder-open fa-fw");
25
+ $(`#${parentId} i.fas`).removeClass("rotate");
26
+ $(`#${parentId}`).addClass("hide-border-bottom");
27
+ }
28
+ });
29
+
30
+ });
@@ -0,0 +1,12 @@
1
+ /*
2
+ * Create a more beautiful checkbox
3
+ */
4
+
5
+ $(function() {
6
+ /* hide browser default checkbox */
7
+ $("input[type=checkbox]").addClass("unloaded");
8
+ /* create checked checkbox */
9
+ $("input[type=checkbox][checked]").before("<i class=\"fas fa-check-circle checked\"></i>");
10
+ /* create normal checkbox */
11
+ $("input[type=checkbox]:not([checked])").before("<i class=\"far fa-circle\"></i>");
12
+ });
@@ -0,0 +1,133 @@
1
+ /*
2
+ * Clipboard functions
3
+ *
4
+ * Dependencies:
5
+ * - popper.js (https://github.com/popperjs/popper-core)
6
+ * - clipboard.js (https://github.com/zenorocha/clipboard.js)
7
+ */
8
+
9
+ $(function() {
10
+ const btnSelector = '.code-header>button';
11
+ const ICON_SUCCESS = 'fas fa-check';
12
+ const ATTR_TIMEOUT = 'timeout';
13
+ const ATTR_TITLE_SUCCEED = 'data-title-succeed';
14
+ const ATTR_TITLE_ORIGIN = 'data-original-title';
15
+ const TIMEOUT = 2000; // in milliseconds
16
+
17
+ function isLocked(node) {
18
+ if ($(node)[0].hasAttribute(ATTR_TIMEOUT)) {
19
+ let timeout = $(node).attr(ATTR_TIMEOUT);
20
+ if (Number(timeout) > Date.now()) {
21
+ return true;
22
+ }
23
+ }
24
+ return false;
25
+ }
26
+
27
+ function lock(node) {
28
+ $(node).attr(ATTR_TIMEOUT, Date.now() + TIMEOUT);
29
+ }
30
+
31
+ function unlock(node) {
32
+ $(node).removeAttr(ATTR_TIMEOUT);
33
+ }
34
+
35
+ /* --- Copy code block --- */
36
+
37
+ // Initial the clipboard.js object
38
+ const clipboard = new ClipboardJS(btnSelector, {
39
+ target(trigger) {
40
+ let codeBlock = trigger.parentNode.nextElementSibling;
41
+ return codeBlock.querySelector('code .rouge-code');
42
+ }
43
+ });
44
+
45
+ $(btnSelector).tooltip({
46
+ trigger: 'hover',
47
+ placement: 'left'
48
+ });
49
+
50
+ function getIcon(btn) {
51
+ let iconNode = $(btn).children();
52
+ return iconNode.attr('class');
53
+ }
54
+
55
+ const ICON_DEFAULT = getIcon(btnSelector);
56
+
57
+ function showTooltip(btn) {
58
+ const succeedTitle = $(btn).attr(ATTR_TITLE_SUCCEED);
59
+ $(btn).attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
60
+ }
61
+
62
+ function hideTooltip(btn) {
63
+ $(btn).tooltip('hide').removeAttr(ATTR_TITLE_ORIGIN);
64
+ }
65
+
66
+ function setSuccessIcon(btn) {
67
+ let btnNode = $(btn);
68
+ let iconNode = btnNode.children();
69
+ iconNode.attr('class', ICON_SUCCESS);
70
+ }
71
+
72
+ function resumeIcon(btn) {
73
+ let btnNode = $(btn);
74
+ let iconNode = btnNode.children();
75
+ iconNode.attr('class', ICON_DEFAULT);
76
+ }
77
+
78
+ clipboard.on('success', (e) => {
79
+ e.clearSelection();
80
+
81
+ const trigger = e.trigger;
82
+ if (isLocked(trigger)) {
83
+ return;
84
+ }
85
+
86
+ setSuccessIcon(trigger);
87
+ showTooltip(trigger);
88
+ lock(trigger);
89
+
90
+ setTimeout(() => {
91
+ hideTooltip(trigger);
92
+ resumeIcon(trigger);
93
+ unlock(trigger);
94
+ }, TIMEOUT);
95
+
96
+ });
97
+
98
+ /* --- Post link sharing --- */
99
+
100
+ $('#copy-link').click((e) => {
101
+
102
+ let target = $(e.target);
103
+
104
+ if (isLocked(target)) {
105
+ return;
106
+ }
107
+
108
+ // Copy URL to clipboard
109
+
110
+ const url = window.location.href;
111
+ const $temp = $("<input>");
112
+
113
+ $("body").append($temp);
114
+ $temp.val(url).select();
115
+ document.execCommand("copy");
116
+ $temp.remove();
117
+
118
+ // Switch tooltip title
119
+
120
+ const defaultTitle = target.attr(ATTR_TITLE_ORIGIN);
121
+ const succeedTitle = target.attr(ATTR_TITLE_SUCCEED);
122
+
123
+ target.attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
124
+ lock(target);
125
+
126
+ setTimeout(() => {
127
+ target.attr(ATTR_TITLE_ORIGIN, defaultTitle);
128
+ unlock(target);
129
+ }, TIMEOUT);
130
+
131
+ });
132
+
133
+ });
@@ -0,0 +1,47 @@
1
+ /**
2
+ Lazy load images (https://github.com/ApoorvSaxena/lozad.js)
3
+ and popup when clicked (https://github.com/dimsemenov/Magnific-Popup)
4
+ */
5
+
6
+ $(function() {
7
+
8
+ const IMG_SCOPE = '#main > div.row:first-child > div:first-child';
9
+
10
+ if ($(`${IMG_SCOPE} img`).length <= 0 ) {
11
+ return;
12
+ }
13
+
14
+ /* lazy loading */
15
+
16
+ const imgList = document.querySelectorAll(`${IMG_SCOPE} img[data-src]`);
17
+ const observer = lozad(imgList);
18
+ observer.observe();
19
+
20
+ /* popup */
21
+
22
+ $(`${IMG_SCOPE} p > img[data-src],${IMG_SCOPE} img[data-src].preview-img`).each(
23
+ function() {
24
+ let nextTag = $(this).next();
25
+ const title = nextTag.prop('tagName') === 'EM' ? nextTag.text() : '';
26
+ const src = $(this).attr('data-src'); // created by lozad.js
27
+
28
+ $(this).wrap(`<a href="${src}" title="${title}" class="popup"></a>`);
29
+ }
30
+ );
31
+
32
+ $('.popup').magnificPopup({
33
+ type: 'image',
34
+ closeOnContentClick: true,
35
+ showCloseBtn: false,
36
+ zoom: {
37
+ enabled: true,
38
+ duration: 300,
39
+ easing: 'ease-in-out'
40
+ }
41
+ });
42
+
43
+ /* markup the image links */
44
+
45
+ $(`${IMG_SCOPE} a`).has('img').addClass('img-link');
46
+
47
+ });
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Update month/day to locale datetime
3
+ *
4
+ * Requirement: <https://github.com/iamkun/dayjs>
5
+ */
6
+
7
+ /* A tool for locale datetime */
8
+ const LocaleHelper = (function () {
9
+ const $preferLocale = $('meta[name="prefer-datetime-locale"]');
10
+ const locale = $preferLocale.length > 0 ?
11
+ $preferLocale.attr('content').toLowerCase() : $('html').attr('lang').substr(0, 2);
12
+ const attrTimestamp = 'data-ts';
13
+ const attrDateFormat = 'data-df';
14
+
15
+ return {
16
+ locale: () => locale,
17
+ attrTimestamp: () => attrTimestamp,
18
+ attrDateFormat: () => attrDateFormat,
19
+ getTimestamp: ($elem) => Number($elem.attr(attrTimestamp)), // unix timestamp
20
+ getDateFormat: ($elem) => $elem.attr(attrDateFormat)
21
+ };
22
+
23
+ }());
24
+
25
+ $(function() {
26
+ dayjs.locale(LocaleHelper.locale());
27
+ dayjs.extend(window.dayjs_plugin_localizedFormat);
28
+
29
+ $(`[${LocaleHelper.attrTimestamp()}]`).each(function () {
30
+ const date = dayjs.unix(LocaleHelper.getTimestamp($(this)));
31
+ const text = date.format(LocaleHelper.getDateFormat($(this)));
32
+ $(this).text(text);
33
+ $(this).removeAttr(LocaleHelper.attrTimestamp());
34
+ $(this).removeAttr(LocaleHelper.attrDateFormat());
35
+
36
+ // setup tooltips
37
+ const tooltip = $(this).attr('data-toggle');
38
+ if (typeof tooltip === 'undefined' || tooltip !== 'tooltip') {
39
+ return;
40
+ }
41
+
42
+ const tooltipText = date.format('llll'); // see: https://day.js.org/docs/en/display/format#list-of-localized-formats
43
+ $(this).attr('data-original-title', tooltipText);
44
+ });
45
+ });