From 2bfcbbc82d829490205bead6f69da0f6658cdc77 Mon Sep 17 00:00:00 2001 From: Damillora Date: Sat, 9 Nov 2019 04:22:18 +0700 Subject: [PATCH] add inf scrolling --- .gitignore | 1 + assets/built/yuika.js | 2 +- assets/built/yuika.js.map | 2 +- assets/js/infinite-scroll.js | 112 +++++++++++++++++++++++++++++++++++ author.hbs | 2 +- index.hbs | 2 +- partials/post-card.hbs | 2 +- tag.hbs | 2 +- 8 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 assets/js/infinite-scroll.js diff --git a/.gitignore b/.gitignore index 5fe23e9..c1a286d 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ dist/ config.json changelog.md changelog.md.bk +assets/built diff --git a/assets/built/yuika.js b/assets/built/yuika.js index 5993ac4..d2bbf51 100644 --- a/assets/built/yuika.js +++ b/assets/built/yuika.js @@ -1,2 +1,2 @@ -!function(d){"use strict";d.fn.fitVids=function(t){var i={customSelector:null,ignore:null};if(!document.getElementById("fit-vids-style")){var e=document.head||document.getElementsByTagName("head")[0],r=document.createElement("div");r.innerHTML='

x

',e.appendChild(r.childNodes[1])}return t&&d.extend(i,t),this.each(function(){var t=['iframe[src*="player.vimeo.com"]','iframe[src*="youtube.com"]','iframe[src*="youtube-nocookie.com"]','iframe[src*="kickstarter.com"][src*="video.html"]',"object","embed"];i.customSelector&&t.push(i.customSelector);var r=".fitvidsignore";i.ignore&&(r=r+", "+i.ignore);var e=d(this).find(t.join(","));(e=(e=e.not("object object")).not(r)).each(function(){var t=d(this);if(!(0
').parent(".fluid-width-video-wrapper").css("padding-top",100*e+"%"),t.removeAttr("height").removeAttr("width")}})})},d.fn.fitVids._count=0}(window.jQuery||window.Zepto); +!function(t,i){var r=i.querySelector("link[rel=next]");if(r){var n=i.querySelector(".post-feed");if(n){var o=300,d=!1,s=!1,a=t.scrollY,c=t.innerHeight,l=i.documentElement.scrollHeight;t.addEventListener("scroll",f,{passive:!0}),t.addEventListener("resize",v),u()}}function h(){if(404===this.status)return t.removeEventListener("scroll",f),void t.removeEventListener("resize",v);this.response.querySelectorAll(".post-card").forEach(function(e){n.appendChild(i.importNode(e,!0))});var e=this.response.querySelector("link[rel=next]");e?r.href=e.href:(t.removeEventListener("scroll",f),t.removeEventListener("resize",v)),l=i.documentElement.scrollHeight,s=d=!1}function e(){if(!s)if(a+c<=l-o)d=!1;else{s=!0;var e=new t.XMLHttpRequest;e.responseType="document",e.addEventListener("load",h),e.open("GET",r.href),e.send(null)}}function u(){d||t.requestAnimationFrame(e),d=!0}function f(){a=t.scrollY,u()}function v(){c=t.innerHeight,l=i.documentElement.scrollHeight,u()}}(window,document),function(n){"use strict";n.fn.fitVids=function(e){var i={customSelector:null,ignore:null};if(!document.getElementById("fit-vids-style")){var t=document.head||document.getElementsByTagName("head")[0],r=document.createElement("div");r.innerHTML='

x

',t.appendChild(r.childNodes[1])}return e&&n.extend(i,e),this.each(function(){var e=['iframe[src*="player.vimeo.com"]','iframe[src*="youtube.com"]','iframe[src*="youtube-nocookie.com"]','iframe[src*="kickstarter.com"][src*="video.html"]',"object","embed"];i.customSelector&&e.push(i.customSelector);var r=".fitvidsignore";i.ignore&&(r=r+", "+i.ignore);var t=n(this).find(e.join(","));(t=(t=t.not("object object")).not(r)).each(function(){var e=n(this);if(!(0
').parent(".fluid-width-video-wrapper").css("padding-top",100*t+"%"),e.removeAttr("height").removeAttr("width")}})})},n.fn.fitVids._count=0}(window.jQuery||window.Zepto); //# sourceMappingURL=yuika.js.map \ No newline at end of file diff --git a/assets/built/yuika.js.map b/assets/built/yuika.js.map index f6d9da7..4d17522 100644 --- a/assets/built/yuika.js.map +++ b/assets/built/yuika.js.map @@ -1 +1 @@ -{"version":3,"sources":["jquery.fitvids.js"],"names":["$","fn","fitVids","options","settings","customSelector","ignore","document","getElementById","head","getElementsByTagName","div","createElement","innerHTML","appendChild","childNodes","extend","this","each","selectors","push","ignoreList","$allVideos","find","join","not","$this","parents","length","tagName","toLowerCase","parent","css","isNaN","attr","aspectRatio","parseInt","height","width","videoName","_count","wrap","removeAttr","window","jQuery","Zepto"],"mappings":"CAYA,SAAAA,GAEA,aAEAA,EAAAC,GAAAC,QAAA,SAAAC,GACA,IAAAC,EAAA,CACAC,eAAA,KACAC,OAAA,MAGA,IAAAC,SAAAC,eAAA,kBAAA,CAEA,IAAAC,EAAAF,SAAAE,MAAAF,SAAAG,qBAAA,QAAA,GAEAC,EAAAJ,SAAAK,cAAA,OACAD,EAAAE,UAAA,oUACAJ,EAAAK,YAAAH,EAAAI,WAAA,IAOA,OAJAZ,GACAH,EAAAgB,OAAAZ,EAAAD,GAGAc,KAAAC,KAAA,WACA,IAAAC,EAAA,CACA,kCACA,6BACA,sCACA,oDACA,SACA,SAGAf,EAAAC,gBACAc,EAAAC,KAAAhB,EAAAC,gBAGA,IAAAgB,EAAA,iBAEAjB,EAAAE,SACAe,EAAAA,EAAA,KAAAjB,EAAAE,QAGA,IAAAgB,EAAAtB,EAAAiB,MAAAM,KAAAJ,EAAAK,KAAA,OAEAF,GADAA,EAAAA,EAAAG,IAAA,kBACAA,IAAAJ,IAEAH,KAAA,WACA,IAAAQ,EAAA1B,EAAAiB,MACA,KAAA,EAAAS,EAAAC,QAAAN,GAAAO,QAGA,UAAAX,KAAAY,QAAAC,eAAAJ,EAAAK,OAAA,UAAAH,QAAAF,EAAAK,OAAA,8BAAAH,QAAA,CACAF,EAAAM,IAAA,WAAAN,EAAAM,IAAA,WAAAC,MAAAP,EAAAQ,KAAA,aAAAD,MAAAP,EAAAQ,KAAA,YAEAR,EAAAQ,KAAA,SAAA,GACAR,EAAAQ,KAAA,QAAA,KAEA,IAEAC,GAFA,WAAAlB,KAAAY,QAAAC,eAAAJ,EAAAQ,KAAA,YAAAD,MAAAG,SAAAV,EAAAQ,KAAA,UAAA,KAAAE,SAAAV,EAAAQ,KAAA,UAAA,IAAAR,EAAAW,WACAJ,MAAAG,SAAAV,EAAAQ,KAAA,SAAA,KAAAR,EAAAY,QAAAF,SAAAV,EAAAQ,KAAA,SAAA,KAEA,IAAAR,EAAAQ,KAAA,QAAA,CACA,IAAAK,EAAA,SAAAvC,EAAAC,GAAAC,QAAAsC,OACAd,EAAAQ,KAAA,OAAAK,GACAvC,EAAAC,GAAAC,QAAAsC,SAEAd,EAAAe,KAAA,gGAAAV,OAAA,8BAAAC,IAAA,cAAA,IAAAG,EAAA,KACAT,EAAAgB,WAAA,UAAAA,WAAA,eAMA1C,EAAAC,GAAAC,QAAAsC,OAAA,EAzEA,CA4EAG,OAAAC,QAAAD,OAAAE","file":"yuika.js","sourcesContent":["/*jshint browser:true */\n/*!\n* FitVids 1.3\n*\n*\n* Copyright 2017, Chris Coyier + Dave Rupert + Ghost Foundation\n* This is an unofficial release, ported by John O'Nolan\n* Credit to Thierry Koblentz - http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/\n* Released under the MIT license\n*\n*/\n\n;(function( $ ){\n\n 'use strict';\n\n $.fn.fitVids = function( options ) {\n var settings = {\n customSelector: null,\n ignore: null\n };\n\n if(!document.getElementById('fit-vids-style')) {\n // appendStyles: https://github.com/toddmotto/fluidvids/blob/master/dist/fluidvids.js\n var head = document.head || document.getElementsByTagName('head')[0];\n var css = '.fluid-width-video-container{flex-grow: 1;width:100%;}.fluid-width-video-wrapper{width:100%;position:relative;padding:0;}.fluid-width-video-wrapper iframe,.fluid-width-video-wrapper object,.fluid-width-video-wrapper embed {position:absolute;top:0;left:0;width:100%;height:100%;}';\n var div = document.createElement(\"div\");\n div.innerHTML = '

x

';\n head.appendChild(div.childNodes[1]);\n }\n\n if ( options ) {\n $.extend( settings, options );\n }\n\n return this.each(function(){\n var selectors = [\n 'iframe[src*=\"player.vimeo.com\"]',\n 'iframe[src*=\"youtube.com\"]',\n 'iframe[src*=\"youtube-nocookie.com\"]',\n 'iframe[src*=\"kickstarter.com\"][src*=\"video.html\"]',\n 'object',\n 'embed'\n ];\n\n if (settings.customSelector) {\n selectors.push(settings.customSelector);\n }\n\n var ignoreList = '.fitvidsignore';\n\n if(settings.ignore) {\n ignoreList = ignoreList + ', ' + settings.ignore;\n }\n\n var $allVideos = $(this).find(selectors.join(','));\n $allVideos = $allVideos.not('object object'); // SwfObj conflict patch\n $allVideos = $allVideos.not(ignoreList); // Disable FitVids on this video.\n\n $allVideos.each(function(){\n var $this = $(this);\n if($this.parents(ignoreList).length > 0) {\n return; // Disable FitVids on this video.\n }\n if (this.tagName.toLowerCase() === 'embed' && $this.parent('object').length || $this.parent('.fluid-width-video-wrapper').length) { return; }\n if ((!$this.css('height') && !$this.css('width')) && (isNaN($this.attr('height')) || isNaN($this.attr('width'))))\n {\n $this.attr('height', 9);\n $this.attr('width', 16);\n }\n var height = ( this.tagName.toLowerCase() === 'object' || ($this.attr('height') && !isNaN(parseInt($this.attr('height'), 10))) ) ? parseInt($this.attr('height'), 10) : $this.height(),\n width = !isNaN(parseInt($this.attr('width'), 10)) ? parseInt($this.attr('width'), 10) : $this.width(),\n aspectRatio = height / width;\n if(!$this.attr('name')){\n var videoName = 'fitvid' + $.fn.fitVids._count;\n $this.attr('name', videoName);\n $.fn.fitVids._count++;\n }\n $this.wrap('
').parent('.fluid-width-video-wrapper').css('padding-top', (aspectRatio * 100)+'%');\n $this.removeAttr('height').removeAttr('width');\n });\n });\n };\n\n // Internal counter for unique video names.\n $.fn.fitVids._count = 0;\n\n// Works with either jQuery or Zepto\n})( window.jQuery || window.Zepto );\n"]} \ No newline at end of file +{"version":3,"sources":["infinite-scroll.js","jquery.fitvids.js"],"names":["window","document","nextElement","querySelector","feedElement","buffer","ticking","loading","lastScrollY","scrollY","lastWindowHeight","innerHeight","lastDocumentHeight","documentElement","scrollHeight","addEventListener","onScroll","passive","onResize","requestTick","onPageLoad","this","status","removeEventListener","response","querySelectorAll","forEach","item","appendChild","importNode","resNextElement","href","onUpdate","xhr","XMLHttpRequest","responseType","open","send","requestAnimationFrame","$","fn","fitVids","options","settings","customSelector","ignore","getElementById","head","getElementsByTagName","div","createElement","innerHTML","childNodes","extend","each","selectors","push","ignoreList","$allVideos","find","join","not","$this","parents","length","tagName","toLowerCase","parent","css","isNaN","attr","aspectRatio","parseInt","height","width","videoName","_count","wrap","removeAttr","jQuery","Zepto"],"mappings":"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","file":"yuika.js","sourcesContent":["/* eslint-env browser */\n\n/**\n * Infinite Scroll\n * Used on all pages where there is a list of posts (homepage, tag index, etc).\n *\n * When the page is scrolled to 300px from the bottom, the next page of posts\n * is fetched by following the the that is output\n * by {{ghost_head}}.\n *\n * The individual post items are extracted from the fetched pages by looking for\n * a wrapper element with the class \"post-card\". Any found elements are appended\n * to the element with the class \"post-feed\" in the currently viewed page.\n */\n\n(function (window, document) {\n // next link element\n var nextElement = document.querySelector('link[rel=next]');\n if (!nextElement) {\n return;\n }\n\n // post feed element\n var feedElement = document.querySelector('.post-feed');\n if (!feedElement) {\n return;\n }\n\n var buffer = 300;\n\n var ticking = false;\n var loading = false;\n\n var lastScrollY = window.scrollY;\n var lastWindowHeight = window.innerHeight;\n var lastDocumentHeight = document.documentElement.scrollHeight;\n\n function onPageLoad() {\n if (this.status === 404) {\n window.removeEventListener('scroll', onScroll);\n window.removeEventListener('resize', onResize);\n return;\n }\n\n // append contents\n var postElements = this.response.querySelectorAll('.post-card');\n postElements.forEach(function (item) {\n // document.importNode is important, without it the item's owner\n // document will be different which can break resizing of\n // `object-fit: cover` images in Safari\n feedElement.appendChild(document.importNode(item, true));\n });\n\n // set next link\n var resNextElement = this.response.querySelector('link[rel=next]');\n if (resNextElement) {\n nextElement.href = resNextElement.href;\n } else {\n window.removeEventListener('scroll', onScroll);\n window.removeEventListener('resize', onResize);\n }\n\n // sync status\n lastDocumentHeight = document.documentElement.scrollHeight;\n ticking = false;\n loading = false;\n }\n\n function onUpdate() {\n // return if already loading\n if (loading) {\n return;\n }\n\n // return if not scroll to the bottom\n if (lastScrollY + lastWindowHeight <= lastDocumentHeight - buffer) {\n ticking = false;\n return;\n }\n\n loading = true;\n\n var xhr = new window.XMLHttpRequest();\n xhr.responseType = 'document';\n\n xhr.addEventListener('load', onPageLoad);\n\n xhr.open('GET', nextElement.href);\n xhr.send(null);\n }\n\n function requestTick() {\n ticking || window.requestAnimationFrame(onUpdate);\n ticking = true;\n }\n\n function onScroll() {\n lastScrollY = window.scrollY;\n requestTick();\n }\n\n function onResize() {\n lastWindowHeight = window.innerHeight;\n lastDocumentHeight = document.documentElement.scrollHeight;\n requestTick();\n }\n\n window.addEventListener('scroll', onScroll, {passive: true});\n window.addEventListener('resize', onResize);\n\n requestTick();\n})(window, document);\n","/*jshint browser:true */\n/*!\n* FitVids 1.3\n*\n*\n* Copyright 2017, Chris Coyier + Dave Rupert + Ghost Foundation\n* This is an unofficial release, ported by John O'Nolan\n* Credit to Thierry Koblentz - http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/\n* Released under the MIT license\n*\n*/\n\n;(function( $ ){\n\n 'use strict';\n\n $.fn.fitVids = function( options ) {\n var settings = {\n customSelector: null,\n ignore: null\n };\n\n if(!document.getElementById('fit-vids-style')) {\n // appendStyles: https://github.com/toddmotto/fluidvids/blob/master/dist/fluidvids.js\n var head = document.head || document.getElementsByTagName('head')[0];\n var css = '.fluid-width-video-container{flex-grow: 1;width:100%;}.fluid-width-video-wrapper{width:100%;position:relative;padding:0;}.fluid-width-video-wrapper iframe,.fluid-width-video-wrapper object,.fluid-width-video-wrapper embed {position:absolute;top:0;left:0;width:100%;height:100%;}';\n var div = document.createElement(\"div\");\n div.innerHTML = '

x

';\n head.appendChild(div.childNodes[1]);\n }\n\n if ( options ) {\n $.extend( settings, options );\n }\n\n return this.each(function(){\n var selectors = [\n 'iframe[src*=\"player.vimeo.com\"]',\n 'iframe[src*=\"youtube.com\"]',\n 'iframe[src*=\"youtube-nocookie.com\"]',\n 'iframe[src*=\"kickstarter.com\"][src*=\"video.html\"]',\n 'object',\n 'embed'\n ];\n\n if (settings.customSelector) {\n selectors.push(settings.customSelector);\n }\n\n var ignoreList = '.fitvidsignore';\n\n if(settings.ignore) {\n ignoreList = ignoreList + ', ' + settings.ignore;\n }\n\n var $allVideos = $(this).find(selectors.join(','));\n $allVideos = $allVideos.not('object object'); // SwfObj conflict patch\n $allVideos = $allVideos.not(ignoreList); // Disable FitVids on this video.\n\n $allVideos.each(function(){\n var $this = $(this);\n if($this.parents(ignoreList).length > 0) {\n return; // Disable FitVids on this video.\n }\n if (this.tagName.toLowerCase() === 'embed' && $this.parent('object').length || $this.parent('.fluid-width-video-wrapper').length) { return; }\n if ((!$this.css('height') && !$this.css('width')) && (isNaN($this.attr('height')) || isNaN($this.attr('width'))))\n {\n $this.attr('height', 9);\n $this.attr('width', 16);\n }\n var height = ( this.tagName.toLowerCase() === 'object' || ($this.attr('height') && !isNaN(parseInt($this.attr('height'), 10))) ) ? parseInt($this.attr('height'), 10) : $this.height(),\n width = !isNaN(parseInt($this.attr('width'), 10)) ? parseInt($this.attr('width'), 10) : $this.width(),\n aspectRatio = height / width;\n if(!$this.attr('name')){\n var videoName = 'fitvid' + $.fn.fitVids._count;\n $this.attr('name', videoName);\n $.fn.fitVids._count++;\n }\n $this.wrap('
').parent('.fluid-width-video-wrapper').css('padding-top', (aspectRatio * 100)+'%');\n $this.removeAttr('height').removeAttr('width');\n });\n });\n };\n\n // Internal counter for unique video names.\n $.fn.fitVids._count = 0;\n\n// Works with either jQuery or Zepto\n})( window.jQuery || window.Zepto );\n"]} \ No newline at end of file diff --git a/assets/js/infinite-scroll.js b/assets/js/infinite-scroll.js new file mode 100644 index 0000000..dd34d41 --- /dev/null +++ b/assets/js/infinite-scroll.js @@ -0,0 +1,112 @@ +/* eslint-env browser */ + +/** + * Infinite Scroll + * Used on all pages where there is a list of posts (homepage, tag index, etc). + * + * When the page is scrolled to 300px from the bottom, the next page of posts + * is fetched by following the the that is output + * by {{ghost_head}}. + * + * The individual post items are extracted from the fetched pages by looking for + * a wrapper element with the class "post-card". Any found elements are appended + * to the element with the class "post-feed" in the currently viewed page. + */ + +(function (window, document) { + // next link element + var nextElement = document.querySelector('link[rel=next]'); + if (!nextElement) { + return; + } + + // post feed element + var feedElement = document.querySelector('.post-feed'); + if (!feedElement) { + return; + } + + var buffer = 300; + + var ticking = false; + var loading = false; + + var lastScrollY = window.scrollY; + var lastWindowHeight = window.innerHeight; + var lastDocumentHeight = document.documentElement.scrollHeight; + + function onPageLoad() { + if (this.status === 404) { + window.removeEventListener('scroll', onScroll); + window.removeEventListener('resize', onResize); + return; + } + + // append contents + var postElements = this.response.querySelectorAll('.post-card'); + postElements.forEach(function (item) { + // document.importNode is important, without it the item's owner + // document will be different which can break resizing of + // `object-fit: cover` images in Safari + feedElement.appendChild(document.importNode(item, true)); + }); + + // set next link + var resNextElement = this.response.querySelector('link[rel=next]'); + if (resNextElement) { + nextElement.href = resNextElement.href; + } else { + window.removeEventListener('scroll', onScroll); + window.removeEventListener('resize', onResize); + } + + // sync status + lastDocumentHeight = document.documentElement.scrollHeight; + ticking = false; + loading = false; + } + + function onUpdate() { + // return if already loading + if (loading) { + return; + } + + // return if not scroll to the bottom + if (lastScrollY + lastWindowHeight <= lastDocumentHeight - buffer) { + ticking = false; + return; + } + + loading = true; + + var xhr = new window.XMLHttpRequest(); + xhr.responseType = 'document'; + + xhr.addEventListener('load', onPageLoad); + + xhr.open('GET', nextElement.href); + xhr.send(null); + } + + function requestTick() { + ticking || window.requestAnimationFrame(onUpdate); + ticking = true; + } + + function onScroll() { + lastScrollY = window.scrollY; + requestTick(); + } + + function onResize() { + lastWindowHeight = window.innerHeight; + lastDocumentHeight = document.documentElement.scrollHeight; + requestTick(); + } + + window.addEventListener('scroll', onScroll, {passive: true}); + window.addEventListener('resize', onResize); + + requestTick(); +})(window, document); diff --git a/author.hbs b/author.hbs index 38fcfb5..2eda35b 100644 --- a/author.hbs +++ b/author.hbs @@ -46,7 +46,7 @@ {{/author}} -
+
{{#foreach posts}} {{> "post-card"}} {{/foreach}} diff --git a/index.hbs b/index.hbs index 85bb83b..cd8b28b 100644 --- a/index.hbs +++ b/index.hbs @@ -9,7 +9,7 @@
-
+
{{#foreach posts}} {{> "post-card"}} {{/foreach}} diff --git a/partials/post-card.hbs b/partials/post-card.hbs index fc14fb6..b67519f 100644 --- a/partials/post-card.hbs +++ b/partials/post-card.hbs @@ -1,4 +1,4 @@ -
+
{{> post-background}} {{!--Special header-image.hbs partial to generate the background image--}}
diff --git a/tag.hbs b/tag.hbs index 29d965c..c9ec127 100644 --- a/tag.hbs +++ b/tag.hbs @@ -23,7 +23,7 @@
{{/tag}} -
+
{{#foreach posts}} {{> "post-card"}} {{/foreach}}