Compare commits

...

4 Commits

Author SHA1 Message Date
Ceda EI 70a9c2ae38 Change codeblock theme to Tomorrow 2020-10-09 23:49:00 +05:30
Ceda EI df4428297a Replace terminal theme with paperesque 2020-10-09 23:48:11 +05:30
Ceda EI 242b00f28f Merge commit 'e186c5d6f740bbdb62d2c6ea304977603a55e59a' as 'themes/paperesque' 2020-10-09 23:41:57 +05:30
Ceda EI e186c5d6f7 Squashed 'themes/paperesque/' content from commit 228903d
git-subtree-dir: themes/paperesque
git-subtree-split: 228903d2bad09f92d4de8a2922806fafd24d3966
2020-10-09 23:41:57 +05:30
111 changed files with 4113 additions and 5 deletions

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "themes/terminal"]
path = themes/terminal
url = https://github.com/panr/hugo-theme-terminal.git

View File

@ -1,8 +1,9 @@
baseURL = "https://cedaei.com/"
languageCode = "en-us"
title = "Ceda EI's Blog"
theme = "terminal"
theme = "paperesque"
paginate = 5
pygmentsUseClasses = true
[params]
# dir name of your blog content (default is `content/posts`)
@ -73,3 +74,28 @@ paginate = 5
identifier = "source"
name = "Source"
url = "https://git.webionite.com/ceda_ei/cedaei.com"
# Paperesque {{{
[[params.menu]]
name = "Blog Posts"
url = "posts/"
[[params.menu]]
name = "Tags"
url = "tags/"
[[params.menu]]
name = "About"
url = "https://webionite.com/"
[[params.menu]]
name = "RSS"
url = "/index.xml"
[[params.menu]]
name = "Telegram Updates"
url = "https://t.me/cedaei"
# }}}
# vim: set foldmethod=marker foldlevelstart=0 foldlevel=0:

3
themes/paperesque/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.cache/
*.js.map
.DS_Store

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Fabian Tamp
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

160
themes/paperesque/README.md Normal file
View File

@ -0,0 +1,160 @@
# Paperesque
A lightweight [Hugo](https://gohugo.io) theme with a couple of neat tricks.
You can see it in action on [capnfabs.net](https://capnfabs.net), or on the [Hugo Themes Example Site](https://themes.gohugo.io/theme/paperesque/).
Here's what makes it special:
- Has a shortcode for resizing images to fit the page, _and_ tools for removing originals from the output
- Visual differentiation for drafts
- Footnotes turn into margin notes when there's enough space.
## Install
### git subtree (easiest!)
Copy the files into your repo using `git subtree` (this is way easier to use than submodules; [here's an explainer](https://www.atlassian.com/git/tutorials/git-subtree)):
```sh
git subtree add --prefix themes/paperesque https://github.com/capnfabs/paperesque mainline --squash
```
This will add a commit to your repo with everything ready to go. You'll probably want to modify parts of this theme for your own usage! Subtree makes that easy, because you've just copied the code into your repo ✨
### git submodules
If you're sure you want to use git submodules:
```sh
git submodule add -b mainline https://github.com/capnfabs/paperesque themes/paperesque
```
### Select the theme in your `config.toml`
Add / Modify the `theme` field in your `config.toml` for your hugo site:
```toml
theme = "paperesque"
```
## Using Features
### FYI: the Home Page is Config-driven
When you first install and switch to the theme, you might find that your homepage is blank. That's because all the links on the homepage are specified in your `config.toml`. Set it up like this:
```toml
[[params.menu]]
name = "blog"
url = "posts/"
[[params.menu]]
name = "tags"
url = "tags/"
[[params.menu]]
name = "about"
url = "about/"
[[params.menu]]
name = "contact"
url = "contact/"
```
### Links in the top-right corner
These are also config driven! Add this to your `config.toml` (for example):
```toml
[[params.topmenu]]
name = "about"
url = "about/"
[[params.topmenu]]
name = "contact"
url = "contact/"
[[params.topmenu]]
name = "rss"
url = "posts/index.xml"
```
### Removing original images after resizing
The `fitfigure` shortcode is exactly the same as the `figure` shortcode, but it automatically resizes your images to fit the container, _and_ provides different resolutions for different DPIs (1x, 2x).
Whenever you use this shortcode, the theme makes a mental note of the resource you specified.
Now, you need to do some configuration if you want the originals to be removed from the output.
First, add this to your site's `config.toml`:
```toml
[outputs]
page = ["HTML", "droplist"]
```
Now, as part of your build process, run:
```sh
./themes/paperesque/buildscripts/drop-resources.py [hugo-output-directory]
```
(the Hugo output directory is usually `./public`).
That's it! Resized resources will be removed.
This is _off by default_ because it peppers your build output with `.droplist` files, and if you're not expecting them, it's going to be an unpleasant surprise.
### Visual differentiation for drafts
This one's on, and can't be switched off. Drafts have an orange stripey background everywhere. You can't miss them.
### Footnotes turn into margin notes
This is _on by default_.
You can switch it off site-wide by adding `disableMarginNotes = true` to your `params` in your `config.toml`, i.e.
```toml
[params]
disableMarginNotes = true
```
Alternatively, you can turn it off per-page by adding the `disableMarginNotes = true` to your front-matter for the page.
## Testing against the example site
You can build the example site with this theme with:
```
cd exampleSite
hugo serve --themesDir=../..
```
## Hacking / Modifying the JS
The javascript is built from the `./assets/js/` directory using [Hugo Pipes JS Build functionality](https://gohugo.io/hugo-pipes/js/). This means you should be able to just modify the JS in the theme and expect your changes to be reflected, using the same workflow as you'd use for anything else in your Hugo site.
Note that this is only available since Hugo 0.74.0; if you're stuck on an old version of Hugo, try grabbing this theme at commit dfcf8b8e802e500e883c4f291b34d65d2e65a7bf instead, which uses a different bundling mechanism.
### Dependencies
Note that dependencies are checked into version control, so unless you're adding new ones / upgrading existing packages you should be fine.
To install / update dependencies, you need to [install the `yarn` package manager](https://yarnpkg.com/getting-started/install) (you might need to install [NPM / NodeJS](https://www.npmjs.com/get-npm) too!).
Then, run as required:
```sh
yarn install
yarn add [package]
yarn upgrade [package]
```
### Other resources
- The explanation for how a lot of this works is in [this blog post](https://capnfabs.net/posts/hugo-theme-exclude-processed-images/), so take a look there if you get stuck or want to borrow some of the ideas without grabbing all of them.
- You can see who else is using this theme by [searching Github for `paperesque filename:config.toml`](https://github.com/search?q=paperesque+filename%3Aconfig.toml&type=Code) (requires login).

View File

@ -0,0 +1,3 @@
{
"esversion": 6
}

View File

@ -0,0 +1,33 @@
import { docReady } from "./utils.js";
// Borrowed from https://github.com/gohugoio/gohugoioTheme/blob/2e7250ca437d4666329d3ca96708dd3a4ff59818/assets/js/anchorforid.js
function anchorForId(id) {
const anchor = document.createElement("a");
anchor.className = "header-link";
anchor.title = "Link to this section";
anchor.href = "#" + id;
// Icon from https://useiconic.com/open#icons
anchor.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path d="M5.88.03c-.18.01-.36.03-.53.09-.27.1-.53.25-.75.47a.5.5 0 1 0 .69.69c.11-.11.24-.17.38-.22.35-.12.78-.07 1.06.22.39.39.39 1.04 0 1.44l-1.5 1.5c-.44.44-.8.48-1.06.47-.26-.01-.41-.13-.41-.13a.5.5 0 1 0-.5.88s.34.22.84.25c.5.03 1.2-.16 1.81-.78l1.5-1.5c.78-.78.78-2.04 0-2.81-.28-.28-.61-.45-.97-.53-.18-.04-.38-.04-.56-.03zm-2 2.31c-.5-.02-1.19.15-1.78.75l-1.5 1.5c-.78.78-.78 2.04 0 2.81.56.56 1.36.72 2.06.47.27-.1.53-.25.75-.47a.5.5 0 1 0-.69-.69c-.11.11-.24.17-.38.22-.35.12-.78.07-1.06-.22-.39-.39-.39-1.04 0-1.44l1.5-1.5c.4-.4.75-.45 1.03-.44.28.01.47.09.47.09a.5.5 0 1 0 .44-.88s-.34-.2-.84-.22z" /></svg>';
return anchor;
}
function anchorizeHeadings() {
// If we've found more than 1 article, then abort. It probably means I've
// messed something up if this is the case, but I don't have enough
// confidence in the way I've set everything up to _not_ do this safety
// check.
const articles = document.querySelectorAll('article#main');
if (articles.length != 1) {
return;
}
// Keep this list of header classes in sync with style.css
const headers = articles[0].querySelectorAll('h2, h3, h4');
Array.prototype.forEach.call(headers, function (el, i) {
var link = anchorForId(el.id);
el.appendChild(link);
});
}
export default function anchorizeOnReady() {
docReady(anchorizeHeadings);
}

View File

@ -0,0 +1,123 @@
import { docReady, onWindowResize } from "./utils.js";
import { ResizeObserver } from '@juggle/resize-observer';
const ARTICLE_CONTENT_SELECTOR = "article#main";
const FOOTNOTE_SECTION_SELECTOR = "section.footnotes[role=doc-endnotes]";
const FLOATING_FOOTNOTE_MIN_WIDTH = 1260;
// Computes an offset such that setting `top` on elemToAlign will put it
// in vertical alignment with targetAlignment.
function computeOffsetForAlignment(elemToAlign, targetAlignment) {
const offsetParentTop = elemToAlign.offsetParent.getBoundingClientRect().top;
// Distance between the top of the offset parent and the top of the target alignment
return targetAlignment.getBoundingClientRect().top - offsetParentTop;
}
function setFootnoteOffsets(footnotes) {
// Keep track of the bottom of the last element, because we don't want to
// overlap footnotes.
let bottomOfLastElem = 0;
Array.prototype.forEach.call(footnotes, function (footnote, i) {
// In theory, don't need to escape this because IDs can't contain
// quotes, in practice, not sure. ¯\_(ツ)_/¯
// Get the thing that refers to the footnote
const intextLink = document.querySelector("a.footnote-ref[href='#" + footnote.id + "']");
// Find its "content parent"; nearest paragraph or list item or
// whatever. We use this for alignment because it looks much cleaner.
// If it doesn't, your paragraphs are too long :P
// Fallback - use the same height as the link.
const verticalAlignmentTarget = intextLink.closest('p,li') || intextLink;
let offset = computeOffsetForAlignment(footnote, verticalAlignmentTarget);
if (offset < bottomOfLastElem) {
offset = bottomOfLastElem;
}
// computedStyle values are always in pixels, but have the suffix 'px'.
// offsetHeight doesn't include margins, but we want it to use them so
// we retain the style / visual fidelity when all the footnotes are
// crammed together.
bottomOfLastElem =
offset +
footnote.offsetHeight +
parseInt(window.getComputedStyle(footnote).marginBottom) +
parseInt(window.getComputedStyle(footnote).marginTop);
footnote.style.top = offset + 'px';
footnote.style.position = 'absolute';
});
}
function clearFootnoteOffsets(footnotes) {
// Reset all
Array.prototype.forEach.call(footnotes, function (fn, i) {
fn.style.top = null;
fn.style.position = null;
});
}
// contract: this is idempotent; i.e. it won't wreck anything if you call it
// with the same value over and over again. Though maybe it'll wreck performance
// lol.
function updateFootnoteFloat(shouldFloat) {
const footnoteSection = document.querySelector(FOOTNOTE_SECTION_SELECTOR);
const footnotes = footnoteSection.querySelectorAll(
"li[role=doc-endnote]");
if (shouldFloat) {
// Do this first because we need styles applied before doing other
// calculations
footnoteSection.classList.add('floating-footnotes');
setFootnoteOffsets(footnotes);
subscribeToUpdates();
} else {
unsubscribeFromUpdates();
clearFootnoteOffsets(footnotes);
footnoteSection.classList.remove('floating-footnotes');
}
}
function subscribeToUpdates() {
const article = document.querySelector(ARTICLE_CONTENT_SELECTOR);
// Watch for dimension changes on the thing that holds all the footnotes so
// we can reposition as required
resizeObserver.observe(article);
}
function unsubscribeFromUpdates() {
resizeObserver.disconnect();
}
const notifySizeChange = function() {
// Default state, not expanded.
let bigEnough = false;
return function () {
// Pixel width at which this looks good
let nowBigEnough = window.innerWidth >= FLOATING_FOOTNOTE_MIN_WIDTH;
if (nowBigEnough !== bigEnough) {
updateFootnoteFloat(nowBigEnough);
bigEnough = nowBigEnough;
}
};
}();
const resizeObserver = new ResizeObserver((_entries, observer) => {
// By virtue of the fact that we're subscribed, we know this is true.
updateFootnoteFloat(true);
});
export default function enableFloatingFootnotes() {
docReady(() => {
const footnoteSection = document.querySelector(FOOTNOTE_SECTION_SELECTOR);
const article = document.querySelector(ARTICLE_CONTENT_SELECTOR);
const allowFloatingFootnotes = article && !article.classList.contains('no-floating-footnotes');
// only set it all up if there's actually a footnote section and
// we haven't explicitly disabled floating footnotes.
if (footnoteSection && allowFloatingFootnotes) {
onWindowResize(notifySizeChange);
}
});
}

View File

@ -0,0 +1,5 @@
import anchorizeHeadings from "./anchorizeHeadings.js";
import enableFloatingFootnotes from "./floatingFootnotes.js";
enableFloatingFootnotes();
anchorizeHeadings();

View File

@ -0,0 +1,29 @@
// borrowed from https://stackoverflow.com/questions/9899372/pure-javascript-equivalent-of-jquerys-ready-how-to-call-a-function-when-t
function docReady(fn) {
// see if DOM is already available
if (document.readyState === "complete" || document.readyState === "interactive") {
// call on next available tick
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
}
}
function windowLoaded(fn) {
// see if we're already loaded
if (document.readyState === "complete") {
// call on next available tick
setTimeout(fn, 1);
} else {
window.addEventListener("load", fn);
}
}
function onWindowResize(fn) {
windowLoaded(function () {
window.addEventListener('resize', fn);
setTimeout(fn, 1);
});
}
export { docReady, windowLoaded, onWindowResize};

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python3
import sys
import os
import pathlib
import typing
def die(msg):
print(msg)
exit(1)
def line_to_path(base_path, line):
path = pathlib.Path(base_path, line.lstrip('/'))
return path
def get_filenames_from_droplist(base_path, path):
lines = path.read_text().strip().splitlines()
return [line_to_path(base_path, line) for line in lines if line]
def drop_paths(paths, output_dir):
for f in paths:
assert f.is_file()
f.unlink()
print(f"Removed {f.relative_to(output_dir)}")
def main():
if len(sys.argv) != 2:
die(f'Usage: {sys.argv[0]} [hugo-output-directory]')
output_dir = pathlib.Path(sys.argv[1])
if not output_dir.is_dir():
die(f'{output_dir} must be a hugo output directory')
all_droplists = list(output_dir.rglob('*.droplist'))
all_files_to_drop = [f for droplist in all_droplists for f in get_filenames_from_droplist(output_dir, droplist)]
drop_paths(all_files_to_drop, output_dir)
drop_paths(all_droplists, output_dir)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,7 @@
[mediaTypes]
[mediaTypes."text/droplist"]
suffixes = ["droplist"]
[outputFormats.droplist]
mediatype = "text/droplist"
isPlainText = true

View File

@ -0,0 +1,6 @@
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

View File

@ -0,0 +1,64 @@
baseURL = "http://example.com/"
languageCode = "en-us"
title = "Paperesque theme example"
theme = "paperesque"
copyright = "© 2011-2020 Fabian Tamp"
# We're building drafts in config so that you can see them in the example on
# Hugo Themes; you almost certainly don't want this on your production site
buildDrafts = true
[markup]
# This is the default from Hugo 0.60.0 onwards, but we're setting config
# for it below, so make it explicit
defaultMarkdownHandler = "goldmark"
# Required to render HTML tags in the hugoExampleSite examples.
[markup.goldmark.renderer]
unsafe = true
# I can't remember if this is required for syntax highlighting or not.
pygmentsUseClasses = true
# Uncomment these lines for droplist / resource removal support! See the
# README for more details
# [outputs]
# page = ["HTML", "droplist"]
[params]
# Turn this on if you want to disable margin notes everywhere!
# disableMarginNotes = true
# Customise your home page menu here:
[[params.menu]]
name = "Recent Posts"
url = "posts/"
[[params.menu]]
name = "Posts by Tag"
url = "tags/"
[[params.menu]]
name = "About the contents of this example"
url = "about/"
[[params.menu]]
name = "Hugo Themes default example pages"
url = "tags/hugobasicsite/"
[[params.menu]]
name = "Link to theme on Github"
url = "https://github.com/capnfabs/paperesque"
# And your top-right menu links here.
[[params.topmenu]]
name = "my favourite bird"
url = "posts/kookaburra/"
[[params.topmenu]]
name = "rss"
url = "posts/index.xml"

View File

@ -0,0 +1,10 @@
---
date: 2020-01-05
title: "About the contents of this example site"
---
All content on this example site is taken from [_The Birds of Australia_, volume 2 of 7, by John Gould](http://www.gutenberg.org/ebooks/60302).
That book's available in full on Project ~~Gutenbird~~ Gutenberg. The illustrations are beautiful.
Note that many of the names of the birds have since changed, and there's [efforts underway in parts of Australia](https://www.dpaw.wa.gov.au/images/documents/about/science/cswa/articles/14.pdf) to start identifying these birds by the names used by local Aboriginal Australians.

View File

@ -0,0 +1,47 @@
+++
author = "Hugo Authors"
title = "Emoji Support"
date = "2019-03-05"
description = "Guide to emoji usage in Hugo"
tags = [
"hugoBasicSite",
]
+++
Emoji can be enabled in a Hugo project in a number of ways.
<!--more-->
The [`emojify`](https://gohugo.io/functions/emojify/) function can be called directly in templates or [Inline Shortcodes](https://gohugo.io/templates/shortcode-templates/#inline-shortcodes).
To enable emoji globally, set `enableEmoji` to `true` in your sites [configuration](https://gohugo.io/getting-started/configuration/) and then you can type emoji shorthand codes directly in content files; e.g.
<p><span class="nowrap"><span class="emojify">🙈</span> <code>:see_no_evil:</code></span> <span class="nowrap"><span class="emojify">🙉</span> <code>:hear_no_evil:</code></span> <span class="nowrap"><span class="emojify">🙊</span> <code>:speak_no_evil:</code></span></p>
<br>
The [Emoji cheat sheet](http://www.emoji-cheat-sheet.com/) is a useful reference for emoji shorthand codes.
***
**N.B.** The above steps enable Unicode Standard emoji characters and sequences in Hugo, however the rendering of these glyphs depends on the browser and the platform. To style the emoji you can either use a third party emoji font or a font stack; e.g.
{{< highlight html >}}
.emoji {
font-family: Apple Color Emoji,Segoe UI Emoji,NotoColorEmoji,Segoe UI Symbol,Android Emoji,EmojiSymbols;
}
{{< /highlight >}}
{{< css.inline >}}
<style>
.emojify {
font-family: Apple Color Emoji,Segoe UI Emoji,NotoColorEmoji,Segoe UI Symbol,Android Emoji,EmojiSymbols;
font-size: 2rem;
vertical-align: middle;
}
@media screen and (max-width:650px) {
.nowrap {
display: block;
margin: 25px 0;
}
}
</style>
{{< /css.inline >}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -0,0 +1,34 @@
---
date: 2020-01-03
title: "Great Brown Kingfisher"
tags:
- New South Wales
- Large birds
---
{{< figure
src="featured.jpg"
caption="The Great Brown Kingfisher"
attr="-- Project Gutenberg"
attrlink="http://www.gutenberg.org/files/60302/60302-h/60302-h.htm"
>}}
The _Dacelo gigantea_ is a bird with which every resident and traveller in New South Wales is more or less familiar, for independently of its large size, which in itself would tend to attract attention, its voice is so extraordinary as to be unlike that of any other living creature. In its disposition it is by no means shy, and when any new objects are presented to its notice, such as a party traversing the bush or pitching their tent in the vicinity of its retreat, it becomes very prying and inquisitive, often perching on the dead branch of some neighbouring tree, and watching with the greatest curiosity the kindling of the fire and the preparation of the meal; its presence, however, owing to the quietude with which it passes through the forest, and the almost noiseless manner in which it settles, is seldom detected until it emits its extraordinary gurgling, laughing note, which generally calls forth some exclamation according with the temper of the hearer, such as “There is our old friend the Laughing Jackass,” or an epithet of a less friendly character: not unfrequently does its life pay the penalty of its temerity; for if, as is often the case, the travellers larder be ill-provided and his appetite keen, but a few minutes elapse before it is roasting over the fire it was lately surveying with so much curiosity. So remarkable are the sounds emitted by the bird that they have been noted by nearly every writer on New South Wales and its productions. Mr. Caley states that its “loud noise, somewhat like laughing, may be heard at a considerable distance, from which circumstance, and its uncouth appearance, it probably received the extraordinary appellation given to it by the settlers on their first arrival in the colony.” Captain Sturt says, “Its cry, which resembles a chorus of wild spirits, is apt to startle the traveller who may be in jeopardy, as if laughing and mocking at his misfortune;” and Mr. Bennett, in his Wanderings, says, “Its peculiar gurgling laugh, commencing in a low and gradually rising to a high and loud tone, is often heard in all parts of the colony; the deafening noise being poured forth while the bird remains perched upon a neighbouring tree; it rises with the dawn, when the woods re-echo with its gurgling laugh; at sunset it is again heard; and as that glorious orb sinks in the west, a last good night is given in its peculiar tones to all within hearing.”
The Great Brown Kingfisher does not inhabit Van Diemens Land, nor has it yet been met with in Western Australia; it may be said to be almost solely confined to that portion of Australia lying between Spencers Gulf and Moreton Bay, the south-eastern corner, as it were, of the continent.[^location] Unlike most other species, it frequents every variety of situation; the luxuriant brushes stretching along the coast, the more thinly-timbered forest, the belts of trees studding the parched plains and the brushes of the higher ranges being alike favoured with its presence; over all these localities it is rather thinly dispersed being nowhere very numerous.
[^location]: The plate in the Pl. Enl., quoted above, has been considered by all previous writers to have reference to this bird, and while I coincide in this opinion, I think that some mistake must have arisen as to the locality, and that it never visits New Guinea nor even the northern coast of Australia, where its place is supplied by the Dacelo cervina and D. Leachii.
I believe that this bird seldom, if ever, drinks; consequently the most arid plains are as suitable to its habits as the shrouded river sides and the flat brushes near the coast.
Its food, which is of a mixed character, consists exclusively of animal substances; reptiles, insects and crabs, however, appear to be its favourite diet, upon which it is destined by nature to subsist: it devours lizards with avidity, and it is not an unfrequent sight to see it bearing off a snake in its bill to be eaten at leisure; it also preys on small mammalia.[^the-rat] It breeds during the months of August and September, and generally selects a hole in a large gum-tree for the purpose; making no nest, but depositing its beautiful pearl-white eggs, which are one inch and nine lines long by one inch and five lines broad, on the decomposed wood at the bottom of the hole. When there are young ones in it, it defends its breeding-place with great courage and daring, darting down upon any intruder who may attempt to ascend the tree, and inflicting severe and dangerous blows with its pointed bill.
[^the-rat]: I recollect shooting a Great Brown Kingfisher in South Australia in order to secure a fine rat I saw hanging from its bill, and which proved to be a rare species inhabiting the plains of that part of the country.
The sexes present so little difference in the colouring of their plumage, that they are scarcely distinguishable from each other; neither do the young at a month old exhibit any great variation from the adult, the only difference being that the markings are somewhat darker and the brown more generally diffused.
It bears confinement remarkably well, and is one of the most amusing birds for the aviary with which I am acquainted: examples have been brought alive to England; one lived for several years in the Gardens of the Zoological Society of London, and at the moment I am writing (April 1843) a fine individual brought from New South Wales by Mr. Yaldwyn, is now living at his seat at Blackdown in Sussex, where it attracts the attention of every one by its singular actions and extraordinary notes, which are poured forth as freely as in its native wilds.
Forehead brown, each feather with a stripe of blackish brown down the centre; crown of the head, lores, ear-coverts, and a broad band passing round the occiput blackish brown; space between the crown of the head and the band encircling the occiput, and the back of the neck buff, crossed by fine irregular lines of dark brown; back and wings brownish black; the wing-coverts and rump tipped with verditer green; primaries white at the base, black for the remainder of their length, and stained with green on their outer margins immediately behind the white; upper tail-coverts blackish brown, crossed by several broad irregular bands of rusty red; tail brownish black, tipped with white, the white increasing in extent as the feathers recede from the centre; the central feathers crossed near the tip with rusty red; the lateral feathers with brownish black, the bands being very narrow near the tip, and gradually increasing in breadth as they approach the base, where the white interspaces also become tinged with rusty red; under surface pale buffy white, crossed by fine irregular freckled markings of dark brown; upper mandible brownish black; under mandible pale buff; feet olive; irides dark brown; eyelash olive-brown.
The figures represent a male and two young of the natural size.

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@ -0,0 +1,32 @@
---
date: 2020-01-07
title: "Leach's Kingfisher"
tags:
- New South Wales
- South Australia
- Small birds
---
Specimens of this fine Kingsfisher are contained in the British Museum, the Linnean Society, and my own collections, all of which were procured on the north-east coast of Australia, where it evidently replaces the Dacelo gigantea of New South Wales and South Australia.
{{< figure
src="featured-i078.jpg"
class="smaller"
caption="Leach's Kingfisher"
attr="-- Project Gutenberg"
attrlink="http://www.gutenberg.org/files/60302/60302-h/60302-h.htm"
>}}
The specimen in the Linnean Societys museum was presented by Dr. Brown[^1], who procured it in Keppel Bay on the east coast; and it was subsequently seen at Shoalwater Bay[^2] and Broad Sound on the same coast; my own specimens were obtained at Cape York, the north-eastern extremity of Australia.
[^1]: Here's an example of a footnote that's been converted to a margin-note on sufficiently wide screens. They align to the paragraph that they are referenced in.
[^2]: If you use a lot of footnotes close together, they are still spaced nicely and retain their distance from each other.
The habits, actions, food, and indeed the whole of the economy, are so precisely like those of the Dacelo gigantea that a separate description of them is entirely unnecessary.
The male has the head and back of the neck striated with brown and white; sides of the neck and under surface white, crossed with very narrow irregular markings of brown, these markings becoming much broader and conspicuous on the under surface of the shoulder; back brownish black; wing-coverts and rump shining azure-blue; wings deep blue; primaries white at the base, black on their inner webs and blue on the outer; tail rich deep blue, all but the two centre feathers irregularly barred near the extremity and largely tipped with white; upper mandible brownish black, under mandible pale buff; irides dark brown; feet olive.
The female differs but little from the male in the colouring of the plumage, except that the tail-feathers, instead of being of a rich blue barred and tipped with white, are of a light chestnut-brown conspicuously barred with bluish black.
The Plate represents the two sexes about the natural size.

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -0,0 +1,34 @@
---
date: 2020-01-20
title: "Pied Crow Shrike"
tags:
- New South Wales
- Large Birds
draft: true
disableMarginNotes: true
---
_Editor's note_: This page is marked as a draft, which is why everything is orange and stripey.
{{< figure
src="featured-i170.jpg"
caption="The Pied Crow-Shrike"
attr="-- Project Gutenberg"
attrlink="http://www.gutenberg.org/files/60302/60302-h/60302-h.htm"
>}}
This species was originally described and figured in Whites Voyage to New South Wales: it is consequently the oldest and most familiarly known member of the group to which it belongs. It is very generally distributed over the colony of New South Wales, inhabiting alike the brushes near the coast, those of the mountain ranges, and also the forests of Eucalypti which clothe the plains and more open country. As a great part of its food consists of seeds, berries and fruits, it is more arboreal in its habits than some of the other species of its group, whose structure better adapts them for progression on the ground, and whose food principally consists of insects and their larvæ. The habitat of the present bird appears to be confined to the south-eastern portions of the continent, where, as is the case with all birds whose range is so limited, it is a stationary species, merely moving from one district to another according to the season; at one time being more numerous on the open coast, and at another among the brushes, as each may offer it a greater variety or more abundant supply of food: the hilly portions of the country intersected with deep ravines are, however, decidedly its most congenial localities. Like the other members of the genus it is mostly seen in small companies, varying from four to six in number, seldom either singly or in pairs: I am not, however, inclined to consider them as gregarious birds in the strict sense of the word, believing as I do that each of these small companies is composed of a pair and their progeny, which appear to keep together from the birth of the latter until the natural impulse for pairing prompts them to separate.[^unimportant]
[^unimportant]: Here's a footnote I added for no good reason, except to demonstrate that you can turn off margin notes on a per-article basis.
Their flight is very different from that of the Crow, (which they much resemble in outward appearance) being much less protracted, and never of an elevated character; its utmost extent is from one part of the forest to another, or across a gully, in effecting which they sometimes pass over the tops of the trees, while at others they accomplish the distance by flitting from tree to tree. It is during flight that the markings of this bird are displayed to the greatest advantage, the strong contrast of its colours then rendering it a conspicuous object in the bush: while on the wing also it frequently causes the woods to ring with its peculiar noisy cry, by which its presence is often indicated when otherwise it would not be seen. On the ground it hops over the surface with the greatest facility.
The nest, which is usually constructed on the branches of low trees, sometimes even on those of the Casuarinæ, is of a large size, round, open, and cup-shaped, built of sticks and lined with moss and grasses; the eggs, which I was not so fortunate as to procure, are generally three or four in number.
The flesh of this species is frequently eaten by the colonists, and is by some considered a delicacy.
Of all the species of this singular and well-defined genus, the present, although not the largest in stature, is by far the handsomest, its markings being more clearly defined and the tints of its plumage more rich and contrasted than those of any of its congeners, the black being as deep as jet, and the white pure and unspotted; it differs also from all its allies yet discovered in having the basal half of the primaries and the basal half and the tips of the tail-feathers together with those portions of the shafts pure white.
The plumage of both sexes at all ages is so precisely similar, that by dissection alone can we distinguish the male from his mate, or the young from the adult; the female is, however, always a trifle less in all her admeasurements, and the young birds have the corners of the mouth more fleshy and of a brighter yellow than the adults.
All the plumage fine bluish black with the exception of the basal half of the primaries, the basal half and the tips of the tail-feathers, including those portions of their shafts and the under tail-coverts which are snow-white; irides beautiful yellow; bill and feet black.

View File

@ -0,0 +1,137 @@
+++
author = "Hugo Authors"
title = "Markdown Syntax Guide"
date = "2019-03-11"
description = "Sample article showcasing basic Markdown syntax and formatting for HTML elements."
tags = [
"hugoBasicSite",
]
+++
This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.
<!--more-->
## Headings
The following HTML `<h1>`—`<h6>` elements represent six levels of section headings. `<h1>` is the highest section level while `<h6>` is the lowest.
# H1
## H2
### H3
#### H4
##### H5
###### H6
## Paragraph
Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.
Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.
## Blockquotes
The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a `footer` or `cite` element, and optionally with in-line changes such as annotations and abbreviations.
#### Blockquote without attribution
> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
> **Note** that you can use *Markdown syntax* within a blockquote.
#### Blockquote with attribution
> Don't communicate by sharing memory, share memory by communicating.</p>
> — <cite>Rob Pike[^1]</cite>
[^1]: The above quote is excerpted from Rob Pike's [talk](https://www.youtube.com/watch?v=PAAkCSZUG1c) during Gopherfest, November 18, 2015.
## Tables
Tables aren't part of the core Markdown spec, but Hugo supports supports them out-of-the-box.
Name | Age
--------|------
Bob | 27
Alice | 23
#### Inline Markdown within tables
| Inline&nbsp;&nbsp;&nbsp; | Markdown&nbsp;&nbsp;&nbsp; | In&nbsp;&nbsp;&nbsp; | Table |
| ---------- | --------- | ----------------- | ---------- |
| *italics* | **bold** | ~~strikethrough~~&nbsp;&nbsp;&nbsp; | `code` |
## Code Blocks
#### Code block with backticks
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example HTML5 Document</title>
</head>
<body>
<p>Test</p>
</body>
</html>
```
#### Code block indented with four spaces
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example HTML5 Document</title>
</head>
<body>
<p>Test</p>
</body>
</html>
#### Code block with Hugo's internal highlight shortcode
{{< highlight html >}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example HTML5 Document</title>
</head>
<body>
<p>Test</p>
</body>
</html>
{{< /highlight >}}
## List Types
#### Ordered List
1. First item
2. Second item
3. Third item
#### Unordered List
* List item
* Another item
* And another item
#### Nested list
* Item
1. First Sub-item
2. Second Sub-item
## Other Elements — abbr, sub, sup, kbd, mark
<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
H<sub>2</sub>O
X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
Press <kbd><kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd></kbd> to end the session.
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.

View File

@ -0,0 +1,57 @@
+++
author = "Hugo Authors"
title = "Placeholder Text"
date = "2019-03-09"
description = "Lorem Ipsum Dolor Si Amet"
tags = [
"hugoBasicSite",
]
+++
Lorem est tota propiore conpellat pectoribus de
pectora summo. <!--more-->Redit teque digerit hominumque toris verebor lumina non cervice
subde tollit usus habet Arctonque, furores quas nec ferunt. Quoque montibus nunc
caluere tempus inhospita parcite confusaque translucet patri vestro qui optatis
lumine cognoscere flos nubis! Fronde ipsamque patulos Dryopen deorum.
1. Exierant elisi ambit vivere dedere
2. Duce pollice
3. Eris modo
4. Spargitque ferrea quos palude
Rursus nulli murmur; hastile inridet ut ab gravi sententia! Nomine potitus
silentia flumen, sustinet placuit petis in dilapsa erat sunt. Atria
tractus malis.
1. Comas hunc haec pietate fetum procerum dixit
2. Post torum vates letum Tiresia
3. Flumen querellas
4. Arcanaque montibus omnes
5. Quidem et
# Vagus elidunt
<svg class="canon" xmlns="http://www.w3.org/2000/svg" overflow="visible" viewBox="0 0 496 373" height="373" width="496"><g fill="none"><path stroke="#000" stroke-width=".75" d="M.599 372.348L495.263 1.206M.312.633l494.95 370.853M.312 372.633L247.643.92M248.502.92l246.76 370.566M330.828 123.869V1.134M330.396 1.134L165.104 124.515"></path><path stroke="#ED1C24" stroke-width=".75" d="M275.73 41.616h166.224v249.05H275.73zM54.478 41.616h166.225v249.052H54.478z"></path><path stroke="#000" stroke-width=".75" d="M.479.375h495v372h-495zM247.979.875v372"></path><ellipse cx="498.729" cy="177.625" rx=".75" ry="1.25"></ellipse><ellipse cx="247.229" cy="377.375" rx=".75" ry="1.25"></ellipse></g></svg>
[The Van de Graaf Canon](https://en.wikipedia.org/wiki/Canons_of_page_construction#Van_de_Graaf_canon)
## Mane refeci capiebant unda mulcebat
Victa caducifer, malo vulnere contra
dicere aurato, ludit regale, voca! Retorsit colit est profanae esse virescere
furit nec; iaculi matertera et visa est, viribus. Divesque creatis, tecta novat collumque vulnus est, parvas. **Faces illo pepulere** tempus adest. Tendit flamma, ab opes virum sustinet, sidus sequendo urbis.
Iubar proles corpore raptos vero auctor imperium; sed et huic: manus caeli
Lelegas tu lux. Verbis obstitit intus oblectamina fixis linguisque ausus sperare
Echionides cornuaque tenent clausit possit. Omnia putatur. Praeteritae refert
ausus; ferebant e primus lora nutat, vici quae mea ipse. Et iter nil spectatae
vulnus haerentia iuste et exercebat, sui et.
Eurytus Hector, materna ipsumque ut Politen, nec, nate, ignari, vernum cohaesit sequitur. Vel **mitis temploque** vocatus, inque alis, *oculos nomen* non silvis corpore coniunx ne displicet illa. Crescunt non unus, vidit visa quantum inmiti flumina mortis facto sic: undique a alios vincula sunt iactata abdita! Suspenderat ego fuit tendit: luna, ante urbem
Propoetides **parte**.
{{< css.inline >}}
<style>
.canon { background: white; width: 100%; height: auto;}
</style>
{{< /css.inline >}}

View File

@ -0,0 +1,41 @@
+++
author = "Hugo Authors"
title = "Rich Content"
date = "2019-03-10"
description = "A brief description of Hugo Shortcodes"
tags = [
"hugoBasicSite",
]
+++
Hugo ships with several [Built-in Shortcodes](https://gohugo.io/content-management/shortcodes/#use-hugo-s-built-in-shortcodes) for rich content, along with a [Privacy Config](https://gohugo.io/about/hugo-and-gdpr/) and a set of Simple Shortcodes that enable static and no-JS versions of various social media embeds.
<!--more-->
---
## Instagram Simple Shortcode
{{< instagram_simple BGvuInzyFAe hidecaption >}}
<br>
---
## YouTube Privacy Enhanced Shortcode
{{< youtube ZJthWmvUzzc >}}
<br>
---
## Twitter Simple Shortcode
{{< twitter_simple 1085870671291310081 >}}
<br>
---
## Vimeo Simple Shortcode
{{< vimeo_simple 48912912 >}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -0,0 +1,47 @@
---
date: 2020-01-26
title: "The Wood Swallow"
tags:
- New South Wales
- Small birds
---
{{< figure
src="i110.jpg"
caption="The Wood Swallow"
attr="-- Project Gutenberg"
attrlink="http://www.gutenberg.org/files/60302/60302-h/60302-h.htm"
>}}
This Wood Swallow has been long known to ornithologists[^1], but unfortunately under so many generic and specific appellations, that it may be cited as an instance of the manner in which our science has been burthened with useless names, thereby producing an inextricable confusion[^2], and which in this instance, by a reference to Lathams accurate description, and the slightest care on the part of other writers, might have been avoided.
[^1]: This is an _intentional_ stress test of the margin-notes system. When we add lots of margin notes in nearby paragraphs with lots of content, they automatically flow down the page.
[^2]: If you are worried about having too much margin text, you can always set `disableMarginNotes` on the page, and you'll get regular footnotes at the bottom of the page.
No other species of the Australian Artami[^3] with which I am acquainted possesses so wide a range from east to west as the present; the whole of the southern portion of the continent, as well as the island of Van Diemens Land, being alike favoured with its presence. The extent of its range northward has not yet been satisfactorily ascertained, beyond the certainty that it has not hitherto been received in any collection from the north coast.
[^3]: Here's another footnote, just to make sure it works :)
It may be regarded as strictly migratory in Van Diemens Land, where it arrives in October, the beginning of the Australian summer, and after rearing at least two broods departs again northwards in November. On the continent a scattered few remain throughout the year in all the localities favourable to its habits, the number being regulated by the supply of insect food necessary for their subsistence. I may here observe, that specimens from Swan River, South Australia and New South Wales present no difference either in size or colouring, while those from Van Diemens Land are invariably larger in all their admeasurements, and are also of a deeper colour; I regard them, however, as mere varieties of each other, the greater size of the latter being doubtless caused by the superabundance of food which this more southern and humid climate affords.
This Wood Swallow[^4], besides being the commonest species of the genus, must I think be rendered a general favourite with the Australians, not only from its singular and pleasing actions, but by its often taking up its abode and incubating near the houses, particularly such as are surrounded by paddocks and open pasture-lands skirted by large trees. It was in such situations as these in Van Diemens Land, at the commencement of spring, that I first had an opportunity of observing this species; it was then very numerous on all the cleared estates on the north side of the Derwent, about eight or ten being seen on a single tree, and half as many crowding one against another on the same dead branch, but never in such numbers as to deserve the appellation of flocks: each bird appeared to act independently of the other; each, as the desire for food prompted it, sallying forth from the branch to capture a passing insect, or to soar round the tree and return again to the same spot; on alighting it repeatedly throws up and closes one wing at a time, and spreads the tail obliquely prior to settling. At other times a few were seen perched on the fence surrounding the paddock, on which they frequently descended, like Starlings, in search of coleoptera and other insects. It is not, however, in this state of comparative quiescence that this graceful bird is seen to the greatest advantage, neither is it that kind of existence for which its form is especially adapted; for although its structure is more equally suited for terrestrial, arboreal and aërial habits than that of any other species I have examined, the form of its wing at once points out the air as its peculiar province: hence it is, that when engaged in pursuit of the insects which the serene and warm weather has enticed from their lurking-places among the foliage to sport in higher regions, this beautiful species in these aërial flights displays its greatest beauty, while soaring above, in a variety of easy positions, with white-tipped tail widely spread. Another very extraordinary and singular habit of the bird is that of clustering like bees on the dead branch of a tree, as represented in the Plate; this feature was not seen by me, but by my assistant Mr. Gilbert, during his residence at Swan River, and I have here given his account in his own words. “The greatest peculiarity in the habits of this bird is its manner of suspending itself in perfect clusters, like a swarm of bees; a few birds suspending themselves on the under side of a dead branch, while others of the flock attach themselves one to the other, in such numbers that they have been observed nearly of the size of a bushel measure.”
[^4]: I think the margin notes really shine when you see them spaced out down the page like this. They're super useful for technical writing, but note that they align to the top of the paragraph they're referenced from; so they work best if you use short paragraphs.
It was very numerous in the town of Perth until about the middle of April, when I missed it suddenly, nor did I observe it again until near the end of May, when I saw it in countless numbers flying in company with the Common Swallows and Martens over a lake about ten miles north of the town; so numerous, in fact, were they, that they darkened the water as they flew over it.
Its voice greatly resembles that of the Common Swallow in character, but is much more harsh.
The stomach is muscular and capacious, and the food consists of insects generally.
The season of incubation is from September to December. The situation of the nest is much varied; I have seen one placed in a thickly foliaged bough near the ground, while others were in a naked fork, on the side of the hole of a tree, in a niche formed by a portion of the bark having been separated from the trunk, &c. The nest is rather shallow, of a rounded form, about five inches in diameter, and composed of fine twigs neatly lined with fibrous roots. I observed that the nests found in Van Diemens Land were larger, more compact and more neatly formed than those on the continent of Australia; and one which was shown me by Mr. Justice Montague on his picturesque estate at Kangaroo Point, near Hobart Town, was placed at the extremity of a small leafy branch, as represented in the Plate.
The eggs are generally four in number; they differ much in the disposition of their markings; their ground-colour is dull white, spotted and dashed with dark umber-brown; in some a second series of greyish spots appear as if beneath the surface of the shell; their medium length is eleven lines, and breadth eight lines.
Head, neck, and the whole of the body fuliginous grey; wings dark bluish black, the external edges of the second, third and fourth primaries white; tail bluish black, all the feathers except the two middle ones largely tipped with white; irides dark brown; bill blue with a black tip; feet mealy lead-colour.
The sexes are alike in the colouring of their plumage, and are only to be distinguished by the female being somewhat smaller in size.
The young have an irregular stripe of dirty white down the centre of each feather of the upper surface, and are mottled with the same on the under surface.
The Plate represents a male and female of the natural size.

View File

@ -0,0 +1 @@
../..

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html
lang="{{ .Site.LanguageCode }}"
prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#"
>
<head>
<meta charset="utf-8" />
{{ partial "meta.html" . }}
<title>{{ .Title }}{{ if ne $.Site.Title .Title }} || {{ $.Site.Title }}{{ end }}</title>
<link rel="canonical" href="{{ .Permalink }}" />
{{ with .Site.Params.RSSCanonicalLink }}
<link rel="alternate" type="application/rss+xml" href="{{ . | absURL }}" title="{{$.Site.Title}}" />
{{ else }}
{{- with .OutputFormats.Get "rss" -}}
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
{{- end -}}
{{ end }}
{{ partial "head_includes.html" . }}
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
</head>
<body
class="{{ block "body-classes" . }}{{ end }}"
lang="{{ .Site.LanguageCode }}">
<div class="nav-bkg">
<nav class="content-container pagewide-bar-padding">
<span class="divider">/ </span>
<a href="{{ .Site.Home.RelPermalink }}" >{{ .Site.Title }}</a>
{{ block "breadcrumbs" . }}
{{- with .Parent -}}
{{- if not .IsHome -}}
<span class="divider">/ </span>
<a href="{{ .RelPermalink }}">
{{- with .Params.breadcrumb_label -}}
{{- . -}}
{{- else -}}
{{- .Title -}}
{{- end -}}
</a>
{{- end -}}
{{- end -}}
{{ end }}
{{ partial "right-links" . }}
</nav>
</div>
{{ block "content" . }}{{ end }}
{{ block "footer" .}}{{ end }}
</body>
</html>

View File

@ -0,0 +1,24 @@
{{ define "content" }}
<section id="main" class="content-container article-pad-v">
<div>
<h1 id="title" style="margin-bottom: 0.75em">{{.Title}}</h1>
<ul id="list" class="list-unstyled list-of-titles">
{{ range .Data.Pages.ByDate.Reverse }}
<li class="{{if .Draft }}draft{{ end }}">
<a href="{{ .RelPermalink}}">
<span class="post-title">{{ .Title }}</span>
<br>
<span class="post-meta">{{ .Date.Format "January 2, 2006" }}</span>
<br>
<span class="post-meta">
{{ range .Params.tags }}
#{{ . }}&nbsp;
{{ end }}
</span>
</a>
</li>
{{ end }}
</ul>
</div>
</section>
{{ end }}

View File

@ -0,0 +1,3 @@
{{ with (.Page.Scratch.Get "droplist") }}
{{- range (. | sort | uniq) }}{{.}}
{{end -}}{{end}}

View File

@ -0,0 +1,37 @@
{{ define "body-classes" -}}
{{ if .Draft }}draft{{ end }} look-sheet-bkg
{{- end }}
{{ define "content" }}
{{- partial "single-article" . -}}
{{ end }}
{{ define "footer" }}
<!-- TODO: right now, this gets weird when there's a big title on the right.
There might be better info here: https://stackoverflow.com/questions/34995740/css-when-inline-block-elements-line-break-parent-wrapper-does-not-fit-new-width
and you can test on the android-development-developer-productivity article.
Would also be cool to make this fold on mobile so there's two links on separate lines.
-->
{{if (and (not .Parent.IsHome) (or .PrevInSection .NextInSection)) }}
<div class="nav-bkg-50 content-container-narrow-pad bottom-links text-0p75">
<nav class="flex-row">
{{if .PrevInSection}}
<a href="{{.PrevInSection.Permalink}}" class="flex-row v-center no-underline" style="max-width:45%;">
<span class="text-1p5"></span>&nbsp;<span class="re-underline">Previous: {{ .PrevInSection.Title }}</span>
</a>
{{else}}
<span class="flex-row v-center"></span>
{{end}}
{{if .NextInSection}}
<a href="{{.NextInSection.Permalink}}" class="flex-row v-center no-underline" style="max-width: 45%;">
<span class="re-underline">Next: {{.NextInSection.Title}}</span>&nbsp;<span class="text-1p5"></span>
</a>
{{else}}
<span class="flex-row v-center"></span>
{{end}}
</nav>
</div>
{{ end }}
{{ end }}

View File

@ -0,0 +1,16 @@
{{ define "content" }}
<section id="main" class="content-container article-pad-v">
<div>
<h1 id="title" style="margin-bottom: 0.75em">{{.Title}}</h1>
<ul id="list" class="list-unstyled list-of-titles">
{{ range .Site.Params.Menu }}
<li>
<a href="{{ .url | relURL }}">
<span class="post-title">{{ .name }}</span>
</a>
</li>
{{ end }}
</ul>
</div>
</section>
{{ end }}

View File

@ -0,0 +1,15 @@
{{/* The featured image, using the same rules as what the twitter cards use. */}}
{{- with $.Params.images -}}
{{- index . 0 | absURL -}}
{{- else -}}
{{- $images := $.Resources.ByType "image" -}}
{{- $featured := $images.GetMatch "*feature*" -}}
{{- $featured := cond (ne $featured nil) $featured ($images.GetMatch "{*cover*,*thumbnail*}") -}}
{{- with $featured -}}
{{- $featured.Permalink -}}
{{- else -}}
{{- with $.Site.Params.images -}}
{{- index . 0 | absURL -}}
{{- end -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,9 @@
<link rel="stylesheet" href="{{ relURL "/css/reboot.css" }}" />
<link rel="stylesheet" href="{{ relURL "/css/style.css" }}" />
<link rel="stylesheet" href="{{ relURL "/css/syntax.css" }}" />
{{ $opts := dict "targetPath" "js/main.js" "minify" true "target" "es2015" }}
{{ $mainJs := resources.Get "js/main.js" | js.Build $opts }}
<script type="text/javascript" src="{{ $mainJs.RelPermalink }}" defer></script>
<link href="https://fonts.googleapis.com/css?family=Lora&display=swap" rel="stylesheet">

View File

@ -0,0 +1,28 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="HandheldFriendly" content="True" />
<meta name="MobileOptimized" content="320" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="apple-touch-icon" sizes="180x180" href="{{.Site.BaseURL}}/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{.Site.BaseURL}}/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{.Site.BaseURL}}/favicon-16x16.png">
<link rel="manifest" href="{{.Site.BaseURL}}/site.webmanifest">
<link rel="mask-icon" href="{{.Site.BaseURL}}/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
{{ with.Description }}<meta name="description" content="{{ . }}" />
{{ end }}
<meta name="keywords" content="{{ if .Keywords }}{{ range.Keywords }}{{ . }},
{{ end }}{{else if isset .Params "tags" }}{{ range.Params.tags }}{{ . }},
{{ end }}{{ end }}">
{{ template "_internal/opengraph.html" . }}
<meta property="og:site_name" content="{{ .Site.Title }}" />
{{ partial "twitter_cards_default_small.html" . }}
{{ template "_internal/schema.html" . }}

View File

@ -0,0 +1,9 @@
<ul class="list-unstyled right-links">
{{ range .Site.Params.TopMenu }}
<li>
<a href="{{ .url | relURL }}">
<span class="post-title">{{ .name }}</span>
</a>
</li>
{{ end }}
</ul>

View File

@ -0,0 +1,33 @@
<article
id="main"
class="content-container look-sheet article-pad-v {{ if (or .Params.disableMarginNotes .Site.Params.disableMarginNotes) }}no-floating-footnotes{{ end }}"
itemscope
itemtype="https://schema.org/Article" >
<meta itemprop="author" content="{{ .Site.Params.Author }}" />
<meta itemprop="publisher" content="{{ .Site.Params.Author }}" />
<meta itemprop="image" content="{{ partial "feature_image" . }}" />
<h1 itemprop="name" id="title">{{ .Title }}</h1>
<meta itemprop="headline" content="{{ .Title }}" />
{{ with .Params.tags }}
<div class="post-tags">
{{ range . }}
<a href="{{ (urlize (printf "tags/%s/" .)) | absLangURL }}">#{{ . }}</a>&nbsp;
{{ end }}
</div>
{{ end }}
{{ if .Date }}
{{ if eq .Lastmod .Date }}
<div class="post-date"><span itemprop="datePublished">{{ .Date.Format "January 2, 2006" }}</span></div>
<meta itemprop="dateModified" content="{{ .Lastmod.Format "January 2, 2006" }}"/>
{{ else }}
<div class="post-date">Created: <span itemprop="datePublished">{{ .Date.Format "January 2, 2006" }}</span></div>
<div class="post-date">Last Edited: <span itemprop="dateModified">{{ .Lastmod.Format "January 2, 2006" }}</span></div>
{{ end }}
{{ end }}
{{ if .Draft }}
<div class="post-date">{{ .WordCount }} words</div>
{{ end }}
<div itemprop="articleBody" id="content" class="article-body margin-top-2em">
{{ .Content }}
</div>
</article>

View File

@ -0,0 +1,30 @@
{{/* This is a modified copy/paste of https://github.com/gohugoio/hugo/blob/2b73e89d6d2822e86360a6c92c87f539677c119b/tpl/tplimpl/embedded/templates/twitter_cards.html */}}
{{- with $.Params.images -}}
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="{{ index . 0 | absURL }}"/>
{{ else -}}
{{- $images := $.Resources.ByType "image" -}}
{{- $featured := $images.GetMatch "*feature*" -}}
{{- $featured := cond (ne $featured nil) $featured ($images.GetMatch "{*cover*,*thumbnail*}") -}}
{{- with $featured -}}
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="{{ $featured.Permalink }}"/>
{{- else -}}
{{- with $.Site.Params.images -}}
<meta name="twitter:card" content="summary"/>
<meta name="twitter:image" content="{{ index . 0 | absURL }}"/>
{{ else -}}
<meta name="twitter:card" content="summary"/>
{{- end -}}
{{- end -}}
{{- end }}
<meta name="twitter:title" content="{{ .Title }}"/>
<meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}"/>
{{ with .Site.Social.twitter -}}
<meta name="twitter:site" content="@{{ . }}"/>
{{ end -}}
{{ range .Site.Authors }}
{{ with .twitter -}}
<meta name="twitter:creator" content="@{{ . }}"/>
{{ end -}}
{{ end -}}

View File

@ -0,0 +1,3 @@
<aside class="article-aside look-sheet-border">
{{- .Inner -}}
</aside>

View File

@ -0,0 +1,5 @@
<aside class="content-warning">
<p>
<strong>Content Note:</strong> {{ trim .Inner "\n" }}
</p>
</aside>

View File

@ -0,0 +1,18 @@
{{ $_hugo_config := `{ "version": 1 }` }}
<!--
Ok, so - see this: https://gohugo.io/content-management/shortcodes/#shortcodes-with-markdown
The outcome is that by default, accessing .Inner returns the Markdown. Because we're putting it
inside a div, it doesn't get rendered later. So we restore the old behaviour, which renders
everything to HTML as a first step (but apparently breaks the TOC functionality).
-->
<!-- Expander: hides content by default, shows it when clicked on. -->
<div class="expander look-sheet">
{{ $radioGroup := anchorize (.Get "title") }}
<h2>{{ .Get "title" }}</h2>
<input type=radio id="expand-{{$radioGroup}}-show" class="hidden show" name="{{$radioGroup}}">
<input type=radio id="expand-{{$radioGroup}}-hide" class="hidden hide" name="{{$radioGroup}}" checked="checked">
<label class="button button-show" for="expand-{{$radioGroup}}-show">+ Expand</label><label class="button button-hide" for="expand-{{$radioGroup}}-hide">- Collapse</label>
<div class="inner">
{{ .Inner }}
</div>
</div>

View File

@ -0,0 +1,53 @@
{{- $path := (.Get "src") -}}
{{- $original := .Page.Resources.GetMatch $path -}}
{{- if not $original -}}
{{- errorf "couldn't find resource for src: %s" $path -}}
{{- else if and (ne "jpeg" $original.MediaType.SubType) (ne "png" $original.MediaType.SubType) -}}
{{- errorf "src %s is a '%s', expected 'jpeg' or 'png'" $path $original.MediaType.SubType -}}
{{- else -}}
{{- /* Below is a copy paste of https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/shortcodes/figure.html from Hugo commit aba2647c152ffff927f42523b77ee6651630cd67, with the img tag modified to use srcsets */ -}}
{{- /* Note that it presently uses `markdownify` instead of $.Page.RenderString for consistency with Hugo's `figure` shortcode. */ -}}
<figure{{ with .Get "class" }} class="{{ . }}"{{ end }}>
{{- if .Get "link" -}}
<a href="{{ .Get "link" }}"{{ with .Get "target" }} target="{{ . }}"{{ end }}{{ with .Get "rel" }} rel="{{ . }}"{{ end }}>
{{- end }}
{{ if (or (ge $original.Width 1304) (ge $original.Height 1304)) -}}
{{/* Add the original to the droplist, so it will get removed later. */}}
{{- if not (.Page.Scratch.Get "droplist") -}}
{{- .Page.Scratch.Set "droplist" (slice) -}}
{{- end -}}
{{- .Page.Scratch.Add "droplist" $original.RelPermalink -}}
{{/* Note: 652px is the max width of an img in article content. I measured this using dev tools; but it's dependent on the styles for the page. */}}
{{- $resized_1x := $original.Fit "652x652 q95" -}}
{{- $resized_2x := $original.Fit "1304x1304 q95" -}}
<img srcset="{{ $resized_1x.RelPermalink }},
{{ $resized_2x.RelPermalink }} 2x"
{{- /* Use small one by default to be kind to old browsers */}}
src="{{ $resized_1x.RelPermalink }}"
{{ else }}
<img src="{{ $original.RelPermalink }}"
{{ end }}
{{- if or (.Get "alt") (.Get "caption") -}}
alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "caption" | markdownify| plainify }}{{ end }}"
{{- end -}}
{{- with .Get "width" }} width="{{ . }}"{{ end -}}
{{- with .Get "height" }} height="{{ . }}"{{ end -}}
/> <!-- Closing img tag -->
{{- if .Get "link" }}</a>{{ end -}}
{{- if or (or (.Get "title") (.Get "caption")) (.Get "attr") }}
<figcaption>
{{ with (.Get "title") -}}
<h4>{{ . }}</h4>
{{- end -}}
{{- if or (.Get "caption") (.Get "attr") -}}<p>
{{- .Get "caption" | markdownify -}}
{{- with .Get "attrlink" }}
<a href="{{ . }}">
{{- end -}}
{{- .Get "attr" | markdownify -}}
{{- if .Get "attrlink" }}</a>{{ end }}</p>
{{- end }}
</figcaption>
{{- end }}
</figure>
{{- end -}}

View File

@ -0,0 +1,33 @@
{{- /* This is a copy paste of
https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/shortcodes/figure.html
from Hugo commit aba2647c152ffff927f42523b77ee6651630cd67, with the img tag
replaced with an object tag, with type=svg. This allows for embedded SVGs to
be animated with CSS in Safari 13.1. See also: https://stackoverflow.com/q/60975613/996592
*/ -}}
{{- /* Note that it presently uses `markdownify` instead of $.Page.RenderString for consistency with Hugo's `figure` shortcode. */ -}}
<figure{{ with .Get "class" }} class="{{ . }}"{{ end }}>
{{- if .Get "link" -}}
<a href="{{ .Get "link" }}"{{ with .Get "target" }} target="{{ . }}"{{ end }}{{ with .Get "rel" }} rel="{{ . }}"{{ end }}>
{{- end }}
<object type="image/svg+xml" data="{{ .Get "src" }}"
{{- with .Get "width" }} width="{{ . }}"{{ end -}}
{{- with .Get "height" }} height="{{ . }}"{{ end -}}
></object>
{{- if .Get "link" }}</a>{{ end -}}
{{- if or (or (.Get "title") (.Get "caption")) (.Get "attr") -}}
<figcaption>
{{ with (.Get "title") -}}
<h4>{{ . }}</h4>
{{- end -}}
{{- if or (.Get "caption") (.Get "attr") -}}<p>
{{- .Get "caption" | markdownify -}}
{{- with .Get "attrlink" }}
<a href="{{ . }}">
{{- end -}}
{{- .Get "attr" | markdownify -}}
{{- if .Get "attrlink" }}</a>{{ end }}</p>
{{- end }}
</figcaption>
{{- end }}
</figure>

View File

@ -0,0 +1,18 @@
{{ define "content" }}
<section id="main" class="content-container article-pad-v">
<div>
<h1 id="title" style="margin-bottom: 0.75em">{{.Title}}</h1>
<ul id="list" class="list-unstyled list-of-titles">
{{ range .Data.Pages.ByDate.Reverse }}
<li class="{{ if .Draft }}draft{{ end }}">
<a href="{{ .RelPermalink}}">
<span class="post-title">{{ .Title }}</span>
<br>
<span class="post-meta">{{ .Date.Format "January 2, 2006" }}</span>
</a>
</li>
{{ end }}
</ul>
</div>
</section>
{{ end }}

View File

@ -0,0 +1,27 @@
{{ define "content" }}
<section id="main" class="content-container article-pad-v">
<div>
<h1 id="title" style="margin-bottom: 0.75em">{{.Title}}</h1>
<ul id="list" class="list-unstyled list-of-titles">
{{ range .Data.Terms.ByCount }}
{{ with .Page }}
{{ $numDrafts := len (where .Pages ".Draft" true) }}
<li class="{{ if gt $numDrafts 0 }}draft{{ end }}">
<a href="{{ .RelPermalink}}">
<span class="post-title">#{{ .Title }}</span>
<br>
<span class="post-meta">{{len .Pages}} {{if ne (len .Pages) 1}}entries{{else}}entry{{end}}</span>
<br>
{{ if gt $numDrafts 0 }}
<span class="post-meta draft">Including {{ $numDrafts }} {{if ne $numDrafts 1}}drafts{{else}}draft{{end}}</span>
<br>
{{ end }}
<span class="post-meta">Last updated {{ .Lastmod.Format "January 2, 2006" }}</span>
</a>
</li>
{{ end }}
{{ end }}
</ul>
</div>
</section>
{{ end }}

39
themes/paperesque/node_modules/.yarn-integrity generated vendored Normal file
View File

@ -0,0 +1,39 @@
{
"systemParams": "darwin-x64-72",
"modulesFolders": [
"node_modules"
],
"flags": [],
"linkedModules": [],
"topLevelPatterns": [
"@juggle/resize-observer@^3.0.2"
],
"lockfileEntries": {
"@juggle/resize-observer@^3.0.2": "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.2.0.tgz#5e0b448d27fe3091bae6216456512c5904d05661"
},
"files": [],
"artifacts": {
"fsevents@1.2.12": [
"build",
"build/.target.mk",
"build/Makefile",
"build/Release",
"build/Release/.deps",
"build/Release/.deps/Release",
"build/Release/.deps/Release/.node.d",
"build/Release/.deps/Release/fse.node.d",
"build/Release/.deps/Release/obj.target",
"build/Release/.deps/Release/obj.target/fse",
"build/Release/.deps/Release/obj.target/fse/fsevents.o.d",
"build/Release/.node",
"build/Release/fse.node",
"build/Release/obj.target",
"build/Release/obj.target/fse",
"build/Release/obj.target/fse/fsevents.o",
"build/binding.Makefile",
"build/config.gypi",
"build/fse.target.mk",
"build/gyp-mac-tool"
]
}
}

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2019 JUGGLE LTD
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,221 @@
<p align="center">
<img width="160" src="https://user-images.githubusercontent.com/1519516/68546625-51e0f680-03d0-11ea-9955-9f0e1964ba0c.png" />
</p>
<h1 align="center">Resize Observer</h1>
<p align="center">
<img src="https://img.shields.io/circleci/project/github/juggle/resize-observer/master.svg?logo=circleci&style=for-the-badge" />
<img src="https://img.shields.io/coveralls/github/juggle/resize-observer.svg?logoColor=white&style=for-the-badge" />
<img src="https://img.shields.io/bundlephobia/minzip/@juggle/resize-observer.svg?colorB=%233399ff&style=for-the-badge" />
<img src="https://img.shields.io/npm/l/@juggle/resize-observer.svg?colorB=%233399ff&style=for-the-badge" />
</p>
---
A minimal library which polyfills the **ResizeObserver** API and is entirely based on the latest [Draft Specification](https://drafts.csswg.org/resize-observer-1/).
It immediately detects when an element resizes and provides accurate sizing information back to the handler. Check out the [Example Playground](//juggle.studio/resize-observer) for more information on usage and performance.
> The latest [Resize Observer specification](https://drafts.csswg.org/resize-observer-1/) is not yet finalised and is subject to change.
> Any drastic changes to the specification will bump the major version of this library, as there will likely be breaking changes. Check the [release notes](https://github.com/juggle/resize-observer/releases) for more information.
## Installation
``` shell
npm i @juggle/resize-observer
```
## Basic usage
``` js
import { ResizeObserver } from '@juggle/resize-observer';
const ro = new ResizeObserver((entries, observer) => {
console.log('Body has resized!');
observer.disconnect(); // Stop observing
});
ro.observe(document.body); // Watch dimension changes on body
```
This will use the [ponyfilled](https://github.com/sindresorhus/ponyfill) version of **ResizeObserver**, even if the browser supports **ResizeObserver** natively.
## Watching multiple elements
``` js
import { ResizeObserver } from '@juggle/resize-observer';
const ro = new ResizeObserver((entries, observer) => {
console.log('Elements resized:', entries.length);
entries.forEach((entry, index) => {
const { inlineSize: width, blockSize: height } = entry.contentBoxSize[0];
console.log(`Element ${index + 1}:`, `${width}x${height}`);
});
});
const els = document.querySelectorAll('.resizes');
[...els].forEach(el => ro.observe(el)); // Watch multiple!
```
## Watching different box sizes
The latest standards allow for watching different box sizes. The box size option can be specified when observing an element. Options include `border-box`, `device-pixel-content-box` and `content-box` (default).
``` js
import { ResizeObserver } from '@juggle/resize-observer';
const ro = new ResizeObserver((entries, observer) => {
console.log('Elements resized:', entries.length);
entries.forEach((entry, index) => {
const [size] = entry.borderBoxSize;
console.log(`Element ${index + 1}:`, `${size.inlineSize}x${size.blockSize}`);
});
});
// Watch border-box
const observerOptions = {
box: 'border-box'
};
const els = document.querySelectorAll('.resizes');
[...els].forEach(el => ro.observe(el, observerOptions));
```
*From the spec:*
> The box size properties are exposed as sequences in order to support elements that have multiple fragments, which occur in [multi-column](https://www.w3.org/TR/css3-multicol/#) scenarios. However the current definitions of content rect and border box do not mention how those boxes are affected by multi-column layout. In this spec, there will only be a single ResizeObserverSize returned in the sequences, which will correspond to the dimensions of the first column. A future version of this spec will extend the returned sequences to contain the per-fragment size information.
## Using the legacy version (`contentRect`)
Early versions of the API return a `contentRect`. This is still made available for backwards compatibility.
``` js
import { ResizeObserver } from '@juggle/resize-observer';
const ro = new ResizeObserver((entries, observer) => {
console.log('Elements resized:', entries.length);
entries.forEach((entry, index) => {
const { width, height } = entry.contentRect;
console.log(`Element ${index + 1}:`, `${width}x${height}`);
});
});
const els = document.querySelectorAll('.resizes');
[...els].forEach(el => ro.observe(el));
```
## Switching between native and polyfilled versions
You can check to see if the native version is available and switch between this and the polyfill to improve performance on browsers with native support.
``` js
import { ResizeObserver as Polyfill } from '@juggle/resize-observer';
const ResizeObserver = window.ResizeObserver || Polyfill;
// Uses native or polyfill, depending on browser support.
const ro = new ResizeObserver((entries, observer) => {
console.log('Something has resized!');
});
```
To improve this even more, you could use dynamic imports to only load the file when the polyfill is required.
``` js
(async () => {
if ('ResizeObserver' in window === false) {
// Loads polyfill asynchronously, only if required.
const module = await import('@juggle/resize-observer');
window.ResizeObserver = module.ResizeObserver;
}
// Uses native or polyfill, depending on browser support.
const ro = new ResizeObserver((entries, observer) => {
console.log('Something has resized!');
});
})();
```
> Browsers with native support may be behind on the latest specification.
> Use `entry.contentRect` when switching between native and polyfilled versions.
## Resize loop detection
Resize Observers have inbuilt protection against infinite resize loops.
If an element's observed box size changes again within the same resize loop, the observation will be skipped and an error event will be dispatched on the window. Elements with undelivered notifications will be considered for delivery in the next loop.
```js
import { ResizeObserver } from '@juggle/resize-observer';
const ro = new ResizeObserver((entries, observer) => {
// Changing the body size inside of the observer
// will cause a resize loop and the next observation will be skipped
document.body.style.width = '50%';
});
// Listen for errors
window.addEventListener('error', e => console.log(e.message));
// Observe the body
ro.observe(document.body);
```
## Notification Schedule
Notifications are scheduled after all other changes have occurred and all other animation callbacks have been called. This allows the observer callback to get the most accurate size of an element, as no other changes should occur in the same frame.
![resize observer notification schedule](https://user-images.githubusercontent.com/1519516/52825568-20433500-30b5-11e9-9854-4cee13a09a7d.jpg)
## How are differences detected?
To prevent constant polling, every frame. The DOM is queried whenever an event occurs which could cause an element to change its size. This could be when an element is clicked, a DOM Node is added, or, when an animation is running.
To cover these scenarios, there are two types of observation. The first is to listen to specific DOM events, including `resize`, `mousedown` and `focus` to name a few. The second is to listen for any DOM mutations that occur. This detects when a DOM node is added or removed, an attribute is modified, or, even when some text has changed.
This allows for greater idle time, when the application itself is idle.
## Features
- Inbuilt resize loop protection.
- Supports pseudo classes `:hover`, `:active` and `:focus`.
- Supports transitions and animations, including infinite and long-running.
- Detects changes which occur during animation frame.
- Includes support for latest draft spec - observing different box sizes.
- Polls only when required, then shuts down automatically, reducing CPU usage.
- Zero delay system - Notifications are batched and delivered immediately, before the next paint.
## Limitations
- Transitions with initial delays cannot be detected.*
- Animations and transitions with long periods of no change, will not be detected.*
- Style changes from dev tools will only be noticed if they are inline styles.*
## Tested Browsers
[chrome]: https://github.com/alrra/browser-logos/raw/master/src/chrome/chrome_64x64.png
[safari]: https://github.com/alrra/browser-logos/raw/master/src/safari/safari_64x64.png
[safari-ios]: https://github.com/alrra/browser-logos/raw/master/src/safari-ios/safari-ios_64x64.png
[ff]: https://github.com/alrra/browser-logos/raw/master/src/firefox/firefox_64x64.png
[opera]: https://github.com/alrra/browser-logos/raw/master/src/opera/opera_64x64.png
[opera-mini]: https://github.com/alrra/browser-logos/raw/master/src/opera-mini/opera-mini_64x64.png
[edge_12-18]: https://github.com/alrra/browser-logos/raw/master/src/archive/edge_12-18/edge_12-18_64x64.png
[edge]: https://github.com/alrra/browser-logos/raw/master/src/edge/edge_64x64.png
[samsung]: https://github.com/alrra/browser-logos/raw/master/src/samsung-internet/samsung-internet_64x64.png
[ie]: https://github.com/alrra/browser-logos/raw/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_64x64.png
### Desktop
| ![chrome][chrome] | ![safari][safari] | ![ff][ff] | ![opera][opera] | ![edge][edge] | ![edge][edge_12-18] | ![IE][ie] |
|--------|--------|---------|-------|------|------------|---------------------------------------|
| Chrome | Safari | Firefox | Opera | Edge | Edge 12-18 | IE11<br/>IE 9-10 (with polyfills)\*\* |
### Mobile
| ![chrome][chrome] | ![safari][safari] | ![ff][ff] | ![opera][opera] | ![opera mini][opera-mini] | ![edge][edge_12-18] | ![samsung internet][samsung] |
|--------|--------|---------|-------|------------|------|------------------|
| Chrome | Safari | Firefox | Opera | Opera Mini | Edge | Samsung Internet |
---
\*If other interaction occurs, changes will be detected.
\*\*IE10 requires additional polyfills for `Map` and `MutationObserver`. IE9 requires IE10 polyfills plus `requestAnimationFrame`. For more information, [see issue here](https://github.com/juggle/resize-observer/issues/64).

View File

@ -0,0 +1,30 @@
interface Rectangle {
readonly x: number;
readonly y: number;
readonly width: number;
readonly height: number;
}
declare type DOMRectJSON = {
x: number;
y: number;
top: number;
right: number;
bottom: number;
left: number;
width: number;
height: number;
};
declare class DOMRectReadOnly {
readonly x: number;
readonly y: number;
readonly width: number;
readonly height: number;
readonly top: number;
readonly left: number;
readonly bottom: number;
readonly right: number;
constructor(x: number, y: number, width: number, height: number);
toJSON(): DOMRectJSON;
static fromRect(rectangle: Rectangle): Readonly<DOMRectReadOnly>;
}
export { DOMRectReadOnly };

View File

@ -0,0 +1,22 @@
var DOMRectReadOnly = (function () {
function DOMRectReadOnly(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.top = this.y;
this.left = this.x;
this.bottom = this.top + this.height;
this.right = this.left + this.width;
return Object.freeze(this);
}
DOMRectReadOnly.prototype.toJSON = function () {
var _a = this, x = _a.x, y = _a.y, top = _a.top, right = _a.right, bottom = _a.bottom, left = _a.left, width = _a.width, height = _a.height;
return { x: x, y: y, top: top, right: right, bottom: bottom, left: left, width: width, height: height };
};
DOMRectReadOnly.fromRect = function (rectangle) {
return new DOMRectReadOnly(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
};
return DOMRectReadOnly;
}());
export { DOMRectReadOnly };

View File

@ -0,0 +1,10 @@
import { ResizeObserverSize } from './ResizeObserverSize';
import { ResizeObserverBoxOptions } from './ResizeObserverBoxOptions';
declare class ResizeObservation {
target: Element;
observedBox: ResizeObserverBoxOptions;
lastReportedSize: ResizeObserverSize;
constructor(target: Element, observedBox?: ResizeObserverBoxOptions);
isActive(): boolean;
}
export { ResizeObservation };

View File

@ -0,0 +1,31 @@
import { ResizeObserverBoxOptions } from './ResizeObserverBoxOptions';
import { calculateBoxSize } from './algorithms/calculateBoxSize';
import { isSVG, isReplacedElement } from './utils/element';
var skipNotifyOnElement = function (target) {
return !isSVG(target)
&& !isReplacedElement(target)
&& getComputedStyle(target).display === 'inline';
};
var ResizeObservation = (function () {
function ResizeObservation(target, observedBox) {
this.target = target;
this.observedBox = observedBox || ResizeObserverBoxOptions.CONTENT_BOX;
this.lastReportedSize = {
inlineSize: 0,
blockSize: 0
};
}
ResizeObservation.prototype.isActive = function () {
var size = calculateBoxSize(this.target, this.observedBox, true);
if (skipNotifyOnElement(this.target)) {
this.lastReportedSize = size;
}
if (this.lastReportedSize.inlineSize !== size.inlineSize
|| this.lastReportedSize.blockSize !== size.blockSize) {
return true;
}
return false;
};
return ResizeObservation;
}());
export { ResizeObservation };

View File

@ -0,0 +1,10 @@
import { ResizeObserverCallback } from './ResizeObserverCallback';
import { ResizeObserverOptions } from './ResizeObserverOptions';
declare class ResizeObserver {
constructor(callback: ResizeObserverCallback);
observe(target: Element, options?: ResizeObserverOptions): void;
unobserve(target: Element): void;
disconnect(): void;
static toString(): string;
}
export { ResizeObserver };

View File

@ -0,0 +1,39 @@
import { ResizeObserverController } from './ResizeObserverController';
import { isElement } from './utils/element';
var ResizeObserver = (function () {
function ResizeObserver(callback) {
if (arguments.length === 0) {
throw new TypeError("Failed to construct 'ResizeObserver': 1 argument required, but only 0 present.");
}
if (typeof callback !== 'function') {
throw new TypeError("Failed to construct 'ResizeObserver': The callback provided as parameter 1 is not a function.");
}
ResizeObserverController.connect(this, callback);
}
ResizeObserver.prototype.observe = function (target, options) {
if (arguments.length === 0) {
throw new TypeError("Failed to execute 'observe' on 'ResizeObserver': 1 argument required, but only 0 present.");
}
if (!isElement(target)) {
throw new TypeError("Failed to execute 'observe' on 'ResizeObserver': parameter 1 is not of type 'Element");
}
ResizeObserverController.observe(this, target, options);
};
ResizeObserver.prototype.unobserve = function (target) {
if (arguments.length === 0) {
throw new TypeError("Failed to execute 'unobserve' on 'ResizeObserver': 1 argument required, but only 0 present.");
}
if (!isElement(target)) {
throw new TypeError("Failed to execute 'unobserve' on 'ResizeObserver': parameter 1 is not of type 'Element");
}
ResizeObserverController.unobserve(this, target);
};
ResizeObserver.prototype.disconnect = function () {
ResizeObserverController.disconnect(this);
};
ResizeObserver.toString = function () {
return 'function ResizeObserver () { [polyfill code] }';
};
return ResizeObserver;
}());
export { ResizeObserver };

View File

@ -0,0 +1,6 @@
declare enum ResizeObserverBoxOptions {
BORDER_BOX = "border-box",
CONTENT_BOX = "content-box",
DEVICE_PIXEL_CONTENT_BOX = "device-pixel-content-box"
}
export { ResizeObserverBoxOptions };

View File

@ -0,0 +1,7 @@
var ResizeObserverBoxOptions;
(function (ResizeObserverBoxOptions) {
ResizeObserverBoxOptions["BORDER_BOX"] = "border-box";
ResizeObserverBoxOptions["CONTENT_BOX"] = "content-box";
ResizeObserverBoxOptions["DEVICE_PIXEL_CONTENT_BOX"] = "device-pixel-content-box";
})(ResizeObserverBoxOptions || (ResizeObserverBoxOptions = {}));
export { ResizeObserverBoxOptions };

View File

@ -0,0 +1,4 @@
import { ResizeObserver } from './ResizeObserver';
import { ResizeObserverEntry } from './ResizeObserverEntry';
declare type ResizeObserverCallback = (entries: ResizeObserverEntry[], observer: ResizeObserver) => void;
export { ResizeObserverCallback };

View File

@ -0,0 +1,10 @@
import { ResizeObserver } from './ResizeObserver';
import { ResizeObserverCallback } from './ResizeObserverCallback';
import { ResizeObserverOptions } from './ResizeObserverOptions';
declare class ResizeObserverController {
static connect(resizeObserver: ResizeObserver, callback: ResizeObserverCallback): void;
static observe(resizeObserver: ResizeObserver, target: Element, options?: ResizeObserverOptions): void;
static unobserve(resizeObserver: ResizeObserver, target: Element): void;
static disconnect(resizeObserver: ResizeObserver): void;
}
export { ResizeObserverController };

View File

@ -0,0 +1,49 @@
import { scheduler, updateCount } from './utils/scheduler';
import { ResizeObservation } from './ResizeObservation';
import { ResizeObserverDetail } from './ResizeObserverDetail';
import { resizeObservers } from './utils/resizeObservers';
var observerMap = new WeakMap();
var getObservationIndex = function (observationTargets, target) {
for (var i = 0; i < observationTargets.length; i += 1) {
if (observationTargets[i].target === target) {
return i;
}
}
return -1;
};
var ResizeObserverController = (function () {
function ResizeObserverController() {
}
ResizeObserverController.connect = function (resizeObserver, callback) {
var detail = new ResizeObserverDetail(resizeObserver, callback);
observerMap.set(resizeObserver, detail);
};
ResizeObserverController.observe = function (resizeObserver, target, options) {
var detail = observerMap.get(resizeObserver);
var firstObservation = detail.observationTargets.length === 0;
if (getObservationIndex(detail.observationTargets, target) < 0) {
firstObservation && resizeObservers.push(detail);
detail.observationTargets.push(new ResizeObservation(target, options && options.box));
updateCount(1);
scheduler.schedule();
}
};
ResizeObserverController.unobserve = function (resizeObserver, target) {
var detail = observerMap.get(resizeObserver);
var index = getObservationIndex(detail.observationTargets, target);
var lastObservation = detail.observationTargets.length === 1;
if (index >= 0) {
lastObservation && resizeObservers.splice(resizeObservers.indexOf(detail), 1);
detail.observationTargets.splice(index, 1);
updateCount(-1);
}
};
ResizeObserverController.disconnect = function (resizeObserver) {
var _this = this;
var detail = observerMap.get(resizeObserver);
detail.observationTargets.slice().forEach(function (ot) { return _this.unobserve(resizeObserver, ot.target); });
detail.activeTargets.splice(0, detail.activeTargets.length);
};
return ResizeObserverController;
}());
export { ResizeObserverController };

View File

@ -0,0 +1,12 @@
import { ResizeObserver } from './ResizeObserver';
import { ResizeObservation } from './ResizeObservation';
import { ResizeObserverCallback } from './ResizeObserverCallback';
declare class ResizeObserverDetail {
callback: ResizeObserverCallback;
observer: ResizeObserver;
activeTargets: ResizeObservation[];
skippedTargets: ResizeObservation[];
observationTargets: ResizeObservation[];
constructor(resizeObserver: ResizeObserver, callback: ResizeObserverCallback);
}
export { ResizeObserverDetail };

View File

@ -0,0 +1,11 @@
var ResizeObserverDetail = (function () {
function ResizeObserverDetail(resizeObserver, callback) {
this.activeTargets = [];
this.skippedTargets = [];
this.observationTargets = [];
this.observer = resizeObserver;
this.callback = callback;
}
return ResizeObserverDetail;
}());
export { ResizeObserverDetail };

View File

@ -0,0 +1,11 @@
import { DOMRectReadOnly } from './DOMRectReadOnly';
import { ResizeObserverSize } from './ResizeObserverSize';
declare class ResizeObserverEntry {
target: Element;
contentRect: DOMRectReadOnly;
borderBoxSize: ResizeObserverSize[];
contentBoxSize: ResizeObserverSize[];
devicePixelContentBoxSize: ResizeObserverSize[];
constructor(target: Element);
}
export { ResizeObserverEntry };

View File

@ -0,0 +1,13 @@
import { calculateBoxSizes } from './algorithms/calculateBoxSize';
var ResizeObserverEntry = (function () {
function ResizeObserverEntry(target) {
var boxes = calculateBoxSizes(target);
this.target = target;
this.contentRect = boxes.contentRect;
this.borderBoxSize = [boxes.borderBoxSize];
this.contentBoxSize = [boxes.contentBoxSize];
this.devicePixelContentBoxSize = [boxes.devicePixelContentBoxSize];
}
return ResizeObserverEntry;
}());
export { ResizeObserverEntry };

View File

@ -0,0 +1,5 @@
import { ResizeObserverBoxOptions } from './ResizeObserverBoxOptions';
interface ResizeObserverOptions {
box?: 'content-box' | 'border-box' | 'device-pixel-content-box' | ResizeObserverBoxOptions;
}
export { ResizeObserverOptions };

View File

@ -0,0 +1,5 @@
interface ResizeObserverSize {
readonly inlineSize: number;
readonly blockSize: number;
}
export { ResizeObserverSize };

View File

@ -0,0 +1,2 @@
declare const broadcastActiveObservations: () => number;
export { broadcastActiveObservations };

View File

@ -0,0 +1,33 @@
import { resizeObservers } from '../utils/resizeObservers';
import { ResizeObserverEntry } from '../ResizeObserverEntry';
import { calculateDepthForNode } from './calculateDepthForNode';
import { calculateBoxSize } from './calculateBoxSize';
var broadcastActiveObservations = function () {
var shallowestDepth = Infinity;
var callbacks = [];
resizeObservers.forEach(function processObserver(ro) {
if (ro.activeTargets.length === 0) {
return;
}
var entries = [];
ro.activeTargets.forEach(function processTarget(ot) {
var entry = new ResizeObserverEntry(ot.target);
var targetDepth = calculateDepthForNode(ot.target);
entries.push(entry);
ot.lastReportedSize = calculateBoxSize(ot.target, ot.observedBox);
if (targetDepth < shallowestDepth) {
shallowestDepth = targetDepth;
}
});
callbacks.push(function resizeObserverCallback() {
ro.callback.call(ro.observer, entries, ro.observer);
});
ro.activeTargets.splice(0, ro.activeTargets.length);
});
for (var _i = 0, callbacks_1 = callbacks; _i < callbacks_1.length; _i++) {
var callback = callbacks_1[_i];
callback();
}
return shallowestDepth;
};
export { broadcastActiveObservations };

View File

@ -0,0 +1,12 @@
import { ResizeObserverBoxOptions } from '../ResizeObserverBoxOptions';
import { ResizeObserverSize } from '../ResizeObserverSize';
import { DOMRectReadOnly } from '../DOMRectReadOnly';
interface ResizeObserverSizeCollection {
devicePixelContentBoxSize: ResizeObserverSize;
borderBoxSize: ResizeObserverSize;
contentBoxSize: ResizeObserverSize;
contentRect: DOMRectReadOnly;
}
declare const calculateBoxSizes: (target: Element, forceRecalculation?: boolean) => ResizeObserverSizeCollection;
declare const calculateBoxSize: (target: Element, observedBox: ResizeObserverBoxOptions, forceRecalculation?: boolean | undefined) => ResizeObserverSize;
export { calculateBoxSize, calculateBoxSizes };

View File

@ -0,0 +1,80 @@
import { ResizeObserverBoxOptions } from '../ResizeObserverBoxOptions';
import { DOMRectReadOnly } from '../DOMRectReadOnly';
import { isSVG, isHidden } from '../utils/element';
import { global } from '../utils/global';
var cache = new WeakMap();
var scrollRegexp = /auto|scroll/;
var verticalRegexp = /^tb|vertical/;
var IE = (/msie|trident/i).test(global.navigator && global.navigator.userAgent);
var parseDimension = function (pixel) { return parseFloat(pixel || '0'); };
var size = function (inlineSize, blockSize, switchSizes) {
if (inlineSize === void 0) { inlineSize = 0; }
if (blockSize === void 0) { blockSize = 0; }
if (switchSizes === void 0) { switchSizes = false; }
return Object.freeze({
inlineSize: (switchSizes ? blockSize : inlineSize) || 0,
blockSize: (switchSizes ? inlineSize : blockSize) || 0
});
};
var zeroBoxes = Object.freeze({
devicePixelContentBoxSize: size(),
borderBoxSize: size(),
contentBoxSize: size(),
contentRect: new DOMRectReadOnly(0, 0, 0, 0)
});
var calculateBoxSizes = function (target, forceRecalculation) {
if (forceRecalculation === void 0) { forceRecalculation = false; }
if (cache.has(target) && !forceRecalculation) {
return cache.get(target);
}
if (isHidden(target)) {
cache.set(target, zeroBoxes);
return zeroBoxes;
}
var cs = getComputedStyle(target);
var svg = isSVG(target) && target.ownerSVGElement && target.getBBox();
var removePadding = !IE && cs.boxSizing === 'border-box';
var switchSizes = verticalRegexp.test(cs.writingMode || '');
var canScrollVertically = !svg && scrollRegexp.test(cs.overflowY || '');
var canScrollHorizontally = !svg && scrollRegexp.test(cs.overflowX || '');
var paddingTop = svg ? 0 : parseDimension(cs.paddingTop);
var paddingRight = svg ? 0 : parseDimension(cs.paddingRight);
var paddingBottom = svg ? 0 : parseDimension(cs.paddingBottom);
var paddingLeft = svg ? 0 : parseDimension(cs.paddingLeft);
var borderTop = svg ? 0 : parseDimension(cs.borderTopWidth);
var borderRight = svg ? 0 : parseDimension(cs.borderRightWidth);
var borderBottom = svg ? 0 : parseDimension(cs.borderBottomWidth);
var borderLeft = svg ? 0 : parseDimension(cs.borderLeftWidth);
var horizontalPadding = paddingLeft + paddingRight;
var verticalPadding = paddingTop + paddingBottom;
var horizontalBorderArea = borderLeft + borderRight;
var verticalBorderArea = borderTop + borderBottom;
var horizontalScrollbarThickness = !canScrollHorizontally ? 0 : target.offsetHeight - verticalBorderArea - target.clientHeight;
var verticalScrollbarThickness = !canScrollVertically ? 0 : target.offsetWidth - horizontalBorderArea - target.clientWidth;
var widthReduction = removePadding ? horizontalPadding + horizontalBorderArea : 0;
var heightReduction = removePadding ? verticalPadding + verticalBorderArea : 0;
var contentWidth = svg ? svg.width : parseDimension(cs.width) - widthReduction - verticalScrollbarThickness;
var contentHeight = svg ? svg.height : parseDimension(cs.height) - heightReduction - horizontalScrollbarThickness;
var borderBoxWidth = contentWidth + horizontalPadding + verticalScrollbarThickness + horizontalBorderArea;
var borderBoxHeight = contentHeight + verticalPadding + horizontalScrollbarThickness + verticalBorderArea;
var boxes = Object.freeze({
devicePixelContentBoxSize: size(Math.round(contentWidth * devicePixelRatio), Math.round(contentHeight * devicePixelRatio), switchSizes),
borderBoxSize: size(borderBoxWidth, borderBoxHeight, switchSizes),
contentBoxSize: size(contentWidth, contentHeight, switchSizes),
contentRect: new DOMRectReadOnly(paddingLeft, paddingTop, contentWidth, contentHeight)
});
cache.set(target, boxes);
return boxes;
};
var calculateBoxSize = function (target, observedBox, forceRecalculation) {
var _a = calculateBoxSizes(target, forceRecalculation), borderBoxSize = _a.borderBoxSize, contentBoxSize = _a.contentBoxSize, devicePixelContentBoxSize = _a.devicePixelContentBoxSize;
switch (observedBox) {
case ResizeObserverBoxOptions.DEVICE_PIXEL_CONTENT_BOX:
return devicePixelContentBoxSize;
case ResizeObserverBoxOptions.BORDER_BOX:
return borderBoxSize;
default:
return contentBoxSize;
}
};
export { calculateBoxSize, calculateBoxSizes };

View File

@ -0,0 +1,2 @@
declare const calculateDepthForNode: (node: Element) => number;
export { calculateDepthForNode };

View File

@ -0,0 +1,14 @@
import { isHidden } from '../utils/element';
var calculateDepthForNode = function (node) {
if (isHidden(node)) {
return Infinity;
}
var depth = 0;
var parent = node.parentNode;
while (parent) {
depth += 1;
parent = parent.parentNode;
}
return depth;
};
export { calculateDepthForNode };

View File

@ -0,0 +1,2 @@
declare const deliverResizeLoopError: () => void;
export { deliverResizeLoopError };

View File

@ -0,0 +1,16 @@
var msg = 'ResizeObserver loop completed with undelivered notifications.';
var deliverResizeLoopError = function () {
var event;
if (typeof ErrorEvent === 'function') {
event = new ErrorEvent('error', {
message: msg
});
}
else {
event = document.createEvent('Event');
event.initEvent('error', false, false);
event.message = msg;
}
window.dispatchEvent(event);
};
export { deliverResizeLoopError };

View File

@ -0,0 +1,2 @@
declare const gatherActiveObservationsAtDepth: (depth: number) => void;
export { gatherActiveObservationsAtDepth };

View File

@ -0,0 +1,19 @@
import { resizeObservers } from '../utils/resizeObservers';
import { calculateDepthForNode } from './calculateDepthForNode';
var gatherActiveObservationsAtDepth = function (depth) {
resizeObservers.forEach(function processObserver(ro) {
ro.activeTargets.splice(0, ro.activeTargets.length);
ro.skippedTargets.splice(0, ro.skippedTargets.length);
ro.observationTargets.forEach(function processTarget(ot) {
if (ot.isActive()) {
if (calculateDepthForNode(ot.target) > depth) {
ro.activeTargets.push(ot);
}
else {
ro.skippedTargets.push(ot);
}
}
});
});
};
export { gatherActiveObservationsAtDepth };

View File

@ -0,0 +1,2 @@
declare const hasActiveObservations: () => boolean;
export { hasActiveObservations };

View File

@ -0,0 +1,5 @@
import { resizeObservers } from '../utils/resizeObservers';
var hasActiveObservations = function () {
return resizeObservers.some(function (ro) { return ro.activeTargets.length > 0; });
};
export { hasActiveObservations };

View File

@ -0,0 +1,2 @@
declare const hasSkippedObservations: () => boolean;
export { hasSkippedObservations };

View File

@ -0,0 +1,5 @@
import { resizeObservers } from '../utils/resizeObservers';
var hasSkippedObservations = function () {
return resizeObservers.some(function (ro) { return ro.skippedTargets.length > 0; });
};
export { hasSkippedObservations };

View File

@ -0,0 +1,2 @@
export { ResizeObserver } from '../ResizeObserver';
export { ResizeObserverEntry } from '../ResizeObserverEntry';

View File

@ -0,0 +1,2 @@
export { ResizeObserver } from '../ResizeObserver';
export { ResizeObserverEntry } from '../ResizeObserverEntry';

View File

@ -0,0 +1,500 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.ResizeObserver = {}));
}(this, (function (exports) { 'use strict';
var resizeObservers = [];
var hasActiveObservations = function () {
return resizeObservers.some(function (ro) { return ro.activeTargets.length > 0; });
};
var hasSkippedObservations = function () {
return resizeObservers.some(function (ro) { return ro.skippedTargets.length > 0; });
};
var msg = 'ResizeObserver loop completed with undelivered notifications.';
var deliverResizeLoopError = function () {
var event;
if (typeof ErrorEvent === 'function') {
event = new ErrorEvent('error', {
message: msg
});
}
else {
event = document.createEvent('Event');
event.initEvent('error', false, false);
event.message = msg;
}
window.dispatchEvent(event);
};
var ResizeObserverBoxOptions;
(function (ResizeObserverBoxOptions) {
ResizeObserverBoxOptions["BORDER_BOX"] = "border-box";
ResizeObserverBoxOptions["CONTENT_BOX"] = "content-box";
ResizeObserverBoxOptions["DEVICE_PIXEL_CONTENT_BOX"] = "device-pixel-content-box";
})(ResizeObserverBoxOptions || (ResizeObserverBoxOptions = {}));
var DOMRectReadOnly = (function () {
function DOMRectReadOnly(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.top = this.y;
this.left = this.x;
this.bottom = this.top + this.height;
this.right = this.left + this.width;
return Object.freeze(this);
}
DOMRectReadOnly.prototype.toJSON = function () {
var _a = this, x = _a.x, y = _a.y, top = _a.top, right = _a.right, bottom = _a.bottom, left = _a.left, width = _a.width, height = _a.height;
return { x: x, y: y, top: top, right: right, bottom: bottom, left: left, width: width, height: height };
};
DOMRectReadOnly.fromRect = function (rectangle) {
return new DOMRectReadOnly(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
};
return DOMRectReadOnly;
}());
var isSVG = function (target) { return target instanceof SVGElement && 'getBBox' in target; };
var isHidden = function (target) {
if (isSVG(target)) {
var _a = target.getBBox(), width = _a.width, height = _a.height;
return !width && !height;
}
var _b = target, offsetWidth = _b.offsetWidth, offsetHeight = _b.offsetHeight;
return !(offsetWidth || offsetHeight || target.getClientRects().length);
};
var isElement = function (obj) {
var _a, _b;
var scope = (_b = (_a = obj) === null || _a === void 0 ? void 0 : _a.ownerDocument) === null || _b === void 0 ? void 0 : _b.defaultView;
return !!(scope && obj instanceof scope.Element);
};
var isReplacedElement = function (target) {
switch (target.tagName) {
case 'INPUT':
if (target.type !== 'image') {
break;
}
case 'VIDEO':
case 'AUDIO':
case 'EMBED':
case 'OBJECT':
case 'CANVAS':
case 'IFRAME':
case 'IMG':
return true;
}
return false;
};
var global = typeof window !== 'undefined' ? window : {};
var cache = new WeakMap();
var scrollRegexp = /auto|scroll/;
var verticalRegexp = /^tb|vertical/;
var IE = (/msie|trident/i).test(global.navigator && global.navigator.userAgent);
var parseDimension = function (pixel) { return parseFloat(pixel || '0'); };
var size = function (inlineSize, blockSize, switchSizes) {
if (inlineSize === void 0) { inlineSize = 0; }
if (blockSize === void 0) { blockSize = 0; }
if (switchSizes === void 0) { switchSizes = false; }
return Object.freeze({
inlineSize: (switchSizes ? blockSize : inlineSize) || 0,
blockSize: (switchSizes ? inlineSize : blockSize) || 0
});
};
var zeroBoxes = Object.freeze({
devicePixelContentBoxSize: size(),
borderBoxSize: size(),
contentBoxSize: size(),
contentRect: new DOMRectReadOnly(0, 0, 0, 0)
});
var calculateBoxSizes = function (target, forceRecalculation) {
if (forceRecalculation === void 0) { forceRecalculation = false; }
if (cache.has(target) && !forceRecalculation) {
return cache.get(target);
}
if (isHidden(target)) {
cache.set(target, zeroBoxes);
return zeroBoxes;
}
var cs = getComputedStyle(target);
var svg = isSVG(target) && target.ownerSVGElement && target.getBBox();
var removePadding = !IE && cs.boxSizing === 'border-box';
var switchSizes = verticalRegexp.test(cs.writingMode || '');
var canScrollVertically = !svg && scrollRegexp.test(cs.overflowY || '');
var canScrollHorizontally = !svg && scrollRegexp.test(cs.overflowX || '');
var paddingTop = svg ? 0 : parseDimension(cs.paddingTop);
var paddingRight = svg ? 0 : parseDimension(cs.paddingRight);
var paddingBottom = svg ? 0 : parseDimension(cs.paddingBottom);
var paddingLeft = svg ? 0 : parseDimension(cs.paddingLeft);
var borderTop = svg ? 0 : parseDimension(cs.borderTopWidth);
var borderRight = svg ? 0 : parseDimension(cs.borderRightWidth);
var borderBottom = svg ? 0 : parseDimension(cs.borderBottomWidth);
var borderLeft = svg ? 0 : parseDimension(cs.borderLeftWidth);
var horizontalPadding = paddingLeft + paddingRight;
var verticalPadding = paddingTop + paddingBottom;
var horizontalBorderArea = borderLeft + borderRight;
var verticalBorderArea = borderTop + borderBottom;
var horizontalScrollbarThickness = !canScrollHorizontally ? 0 : target.offsetHeight - verticalBorderArea - target.clientHeight;
var verticalScrollbarThickness = !canScrollVertically ? 0 : target.offsetWidth - horizontalBorderArea - target.clientWidth;
var widthReduction = removePadding ? horizontalPadding + horizontalBorderArea : 0;
var heightReduction = removePadding ? verticalPadding + verticalBorderArea : 0;
var contentWidth = svg ? svg.width : parseDimension(cs.width) - widthReduction - verticalScrollbarThickness;
var contentHeight = svg ? svg.height : parseDimension(cs.height) - heightReduction - horizontalScrollbarThickness;
var borderBoxWidth = contentWidth + horizontalPadding + verticalScrollbarThickness + horizontalBorderArea;
var borderBoxHeight = contentHeight + verticalPadding + horizontalScrollbarThickness + verticalBorderArea;
var boxes = Object.freeze({
devicePixelContentBoxSize: size(Math.round(contentWidth * devicePixelRatio), Math.round(contentHeight * devicePixelRatio), switchSizes),
borderBoxSize: size(borderBoxWidth, borderBoxHeight, switchSizes),
contentBoxSize: size(contentWidth, contentHeight, switchSizes),
contentRect: new DOMRectReadOnly(paddingLeft, paddingTop, contentWidth, contentHeight)
});
cache.set(target, boxes);
return boxes;
};
var calculateBoxSize = function (target, observedBox, forceRecalculation) {
var _a = calculateBoxSizes(target, forceRecalculation), borderBoxSize = _a.borderBoxSize, contentBoxSize = _a.contentBoxSize, devicePixelContentBoxSize = _a.devicePixelContentBoxSize;
switch (observedBox) {
case ResizeObserverBoxOptions.DEVICE_PIXEL_CONTENT_BOX:
return devicePixelContentBoxSize;
case ResizeObserverBoxOptions.BORDER_BOX:
return borderBoxSize;
default:
return contentBoxSize;
}
};
var ResizeObserverEntry = (function () {
function ResizeObserverEntry(target) {
var boxes = calculateBoxSizes(target);
this.target = target;
this.contentRect = boxes.contentRect;
this.borderBoxSize = [boxes.borderBoxSize];
this.contentBoxSize = [boxes.contentBoxSize];
this.devicePixelContentBoxSize = [boxes.devicePixelContentBoxSize];
}
return ResizeObserverEntry;
}());
var calculateDepthForNode = function (node) {
if (isHidden(node)) {
return Infinity;
}
var depth = 0;
var parent = node.parentNode;
while (parent) {
depth += 1;
parent = parent.parentNode;
}
return depth;
};
var broadcastActiveObservations = function () {
var shallowestDepth = Infinity;
var callbacks = [];
resizeObservers.forEach(function processObserver(ro) {
if (ro.activeTargets.length === 0) {
return;
}
var entries = [];
ro.activeTargets.forEach(function processTarget(ot) {
var entry = new ResizeObserverEntry(ot.target);
var targetDepth = calculateDepthForNode(ot.target);
entries.push(entry);
ot.lastReportedSize = calculateBoxSize(ot.target, ot.observedBox);
if (targetDepth < shallowestDepth) {
shallowestDepth = targetDepth;
}
});
callbacks.push(function resizeObserverCallback() {
ro.callback.call(ro.observer, entries, ro.observer);
});
ro.activeTargets.splice(0, ro.activeTargets.length);
});
for (var _i = 0, callbacks_1 = callbacks; _i < callbacks_1.length; _i++) {
var callback = callbacks_1[_i];
callback();
}
return shallowestDepth;
};
var gatherActiveObservationsAtDepth = function (depth) {
resizeObservers.forEach(function processObserver(ro) {
ro.activeTargets.splice(0, ro.activeTargets.length);
ro.skippedTargets.splice(0, ro.skippedTargets.length);
ro.observationTargets.forEach(function processTarget(ot) {
if (ot.isActive()) {
if (calculateDepthForNode(ot.target) > depth) {
ro.activeTargets.push(ot);
}
else {
ro.skippedTargets.push(ot);
}
}
});
});
};
var process = function () {
var depth = 0;
gatherActiveObservationsAtDepth(depth);
while (hasActiveObservations()) {
depth = broadcastActiveObservations();
gatherActiveObservationsAtDepth(depth);
}
if (hasSkippedObservations()) {
deliverResizeLoopError();
}
return depth > 0;
};
var trigger;
var callbacks = [];
var notify = function () { return callbacks.splice(0).forEach(function (cb) { return cb(); }); };
var queueMicroTask = function (callback) {
if (!trigger) {
var toggle_1 = 0;
var el_1 = document.createTextNode('');
var config = { characterData: true };
new MutationObserver(function () { return notify(); }).observe(el_1, config);
trigger = function () { el_1.textContent = "" + (toggle_1 ? toggle_1-- : toggle_1++); };
}
callbacks.push(callback);
trigger();
};
var queueResizeObserver = function (cb) {
queueMicroTask(function ResizeObserver() {
requestAnimationFrame(cb);
});
};
var watching = 0;
var isWatching = function () { return !!watching; };
var CATCH_PERIOD = 250;
var observerConfig = { attributes: true, characterData: true, childList: true, subtree: true };
var events = [
'resize',
'load',
'transitionend',
'animationend',
'animationstart',
'animationiteration',
'keyup',
'keydown',
'mouseup',
'mousedown',
'mouseover',
'mouseout',
'blur',
'focus'
];
var time = function (timeout) {
if (timeout === void 0) { timeout = 0; }
return Date.now() + timeout;
};
var scheduled = false;
var Scheduler = (function () {
function Scheduler() {
var _this = this;
this.stopped = true;
this.listener = function () { return _this.schedule(); };
}
Scheduler.prototype.run = function (timeout) {
var _this = this;
if (timeout === void 0) { timeout = CATCH_PERIOD; }
if (scheduled) {
return;
}
scheduled = true;
var until = time(timeout);
queueResizeObserver(function () {
var elementsHaveResized = false;
try {
elementsHaveResized = process();
}
finally {
scheduled = false;
timeout = until - time();
if (!isWatching()) {
return;
}
if (elementsHaveResized) {
_this.run(1000);
}
else if (timeout > 0) {
_this.run(timeout);
}
else {
_this.start();
}
}
});
};
Scheduler.prototype.schedule = function () {
this.stop();
this.run();
};
Scheduler.prototype.observe = function () {
var _this = this;
var cb = function () { return _this.observer && _this.observer.observe(document.body, observerConfig); };
document.body ? cb() : global.addEventListener('DOMContentLoaded', cb);
};
Scheduler.prototype.start = function () {
var _this = this;
if (this.stopped) {
this.stopped = false;
this.observer = new MutationObserver(this.listener);
this.observe();
events.forEach(function (name) { return global.addEventListener(name, _this.listener, true); });
}
};
Scheduler.prototype.stop = function () {
var _this = this;
if (!this.stopped) {
this.observer && this.observer.disconnect();
events.forEach(function (name) { return global.removeEventListener(name, _this.listener, true); });
this.stopped = true;
}
};
return Scheduler;
}());
var scheduler = new Scheduler();
var updateCount = function (n) {
!watching && n > 0 && scheduler.start();
watching += n;
!watching && scheduler.stop();
};
var skipNotifyOnElement = function (target) {
return !isSVG(target)
&& !isReplacedElement(target)
&& getComputedStyle(target).display === 'inline';
};
var ResizeObservation = (function () {
function ResizeObservation(target, observedBox) {
this.target = target;
this.observedBox = observedBox || ResizeObserverBoxOptions.CONTENT_BOX;
this.lastReportedSize = {
inlineSize: 0,
blockSize: 0
};
}
ResizeObservation.prototype.isActive = function () {
var size = calculateBoxSize(this.target, this.observedBox, true);
if (skipNotifyOnElement(this.target)) {
this.lastReportedSize = size;
}
if (this.lastReportedSize.inlineSize !== size.inlineSize
|| this.lastReportedSize.blockSize !== size.blockSize) {
return true;
}
return false;
};
return ResizeObservation;
}());
var ResizeObserverDetail = (function () {
function ResizeObserverDetail(resizeObserver, callback) {
this.activeTargets = [];
this.skippedTargets = [];
this.observationTargets = [];
this.observer = resizeObserver;
this.callback = callback;
}
return ResizeObserverDetail;
}());
var observerMap = new WeakMap();
var getObservationIndex = function (observationTargets, target) {
for (var i = 0; i < observationTargets.length; i += 1) {
if (observationTargets[i].target === target) {
return i;
}
}
return -1;
};
var ResizeObserverController = (function () {
function ResizeObserverController() {
}
ResizeObserverController.connect = function (resizeObserver, callback) {
var detail = new ResizeObserverDetail(resizeObserver, callback);
observerMap.set(resizeObserver, detail);
};
ResizeObserverController.observe = function (resizeObserver, target, options) {
var detail = observerMap.get(resizeObserver);
var firstObservation = detail.observationTargets.length === 0;
if (getObservationIndex(detail.observationTargets, target) < 0) {
firstObservation && resizeObservers.push(detail);
detail.observationTargets.push(new ResizeObservation(target, options && options.box));
updateCount(1);
scheduler.schedule();
}
};
ResizeObserverController.unobserve = function (resizeObserver, target) {
var detail = observerMap.get(resizeObserver);
var index = getObservationIndex(detail.observationTargets, target);
var lastObservation = detail.observationTargets.length === 1;
if (index >= 0) {
lastObservation && resizeObservers.splice(resizeObservers.indexOf(detail), 1);
detail.observationTargets.splice(index, 1);
updateCount(-1);
}
};
ResizeObserverController.disconnect = function (resizeObserver) {
var _this = this;
var detail = observerMap.get(resizeObserver);
detail.observationTargets.slice().forEach(function (ot) { return _this.unobserve(resizeObserver, ot.target); });
detail.activeTargets.splice(0, detail.activeTargets.length);
};
return ResizeObserverController;
}());
var ResizeObserver = (function () {
function ResizeObserver(callback) {
if (arguments.length === 0) {
throw new TypeError("Failed to construct 'ResizeObserver': 1 argument required, but only 0 present.");
}
if (typeof callback !== 'function') {
throw new TypeError("Failed to construct 'ResizeObserver': The callback provided as parameter 1 is not a function.");
}
ResizeObserverController.connect(this, callback);
}
ResizeObserver.prototype.observe = function (target, options) {
if (arguments.length === 0) {
throw new TypeError("Failed to execute 'observe' on 'ResizeObserver': 1 argument required, but only 0 present.");
}
if (!isElement(target)) {
throw new TypeError("Failed to execute 'observe' on 'ResizeObserver': parameter 1 is not of type 'Element");
}
ResizeObserverController.observe(this, target, options);
};
ResizeObserver.prototype.unobserve = function (target) {
if (arguments.length === 0) {
throw new TypeError("Failed to execute 'unobserve' on 'ResizeObserver': 1 argument required, but only 0 present.");
}
if (!isElement(target)) {
throw new TypeError("Failed to execute 'unobserve' on 'ResizeObserver': parameter 1 is not of type 'Element");
}
ResizeObserverController.unobserve(this, target);
};
ResizeObserver.prototype.disconnect = function () {
ResizeObserverController.disconnect(this);
};
ResizeObserver.toString = function () {
return 'function ResizeObserver () { [polyfill code] }';
};
return ResizeObserver;
}());
exports.ResizeObserver = ResizeObserver;
exports.ResizeObserverEntry = ResizeObserverEntry;
Object.defineProperty(exports, '__esModule', { value: true });
})));

View File

@ -0,0 +1,5 @@
declare const isSVG: (target: Element) => boolean;
declare const isHidden: (target: Element) => boolean;
declare const isElement: (obj: unknown) => boolean;
declare const isReplacedElement: (target: Element) => boolean;
export { isSVG, isHidden, isElement, isReplacedElement };

View File

@ -0,0 +1,32 @@
var isSVG = function (target) { return target instanceof SVGElement && 'getBBox' in target; };
var isHidden = function (target) {
if (isSVG(target)) {
var _a = target.getBBox(), width = _a.width, height = _a.height;
return !width && !height;
}
var _b = target, offsetWidth = _b.offsetWidth, offsetHeight = _b.offsetHeight;
return !(offsetWidth || offsetHeight || target.getClientRects().length);
};
var isElement = function (obj) {
var _a, _b;
var scope = (_b = (_a = obj) === null || _a === void 0 ? void 0 : _a.ownerDocument) === null || _b === void 0 ? void 0 : _b.defaultView;
return !!(scope && obj instanceof scope.Element);
};
var isReplacedElement = function (target) {
switch (target.tagName) {
case 'INPUT':
if (target.type !== 'image') {
break;
}
case 'VIDEO':
case 'AUDIO':
case 'EMBED':
case 'OBJECT':
case 'CANVAS':
case 'IFRAME':
case 'IMG':
return true;
}
return false;
};
export { isSVG, isHidden, isElement, isReplacedElement };

View File

@ -0,0 +1,8 @@
import { ResizeObserver } from '../ResizeObserver';
import { ResizeObserverEntry } from '../ResizeObserverEntry';
declare type IsomorphicWindow = Window & {
ResizeObserver?: typeof ResizeObserver;
ResizeObserverEntry?: typeof ResizeObserverEntry;
};
export declare const global: IsomorphicWindow;
export {};

View File

@ -0,0 +1 @@
export var global = typeof window !== 'undefined' ? window : {};

View File

@ -0,0 +1,2 @@
declare const process: () => boolean;
export { process };

View File

@ -0,0 +1,18 @@
import { hasActiveObservations } from '../algorithms/hasActiveObservations';
import { hasSkippedObservations } from '../algorithms/hasSkippedObservations';
import { deliverResizeLoopError } from '../algorithms/deliverResizeLoopError';
import { broadcastActiveObservations } from '../algorithms/broadcastActiveObservations';
import { gatherActiveObservationsAtDepth } from '../algorithms/gatherActiveObservationsAtDepth';
var process = function () {
var depth = 0;
gatherActiveObservationsAtDepth(depth);
while (hasActiveObservations()) {
depth = broadcastActiveObservations();
gatherActiveObservationsAtDepth(depth);
}
if (hasSkippedObservations()) {
deliverResizeLoopError();
}
return depth > 0;
};
export { process };

View File

@ -0,0 +1,2 @@
declare const queueMicroTask: (callback: () => void) => void;
export { queueMicroTask };

View File

@ -0,0 +1,15 @@
var trigger;
var callbacks = [];
var notify = function () { return callbacks.splice(0).forEach(function (cb) { return cb(); }); };
var queueMicroTask = function (callback) {
if (!trigger) {
var toggle_1 = 0;
var el_1 = document.createTextNode('');
var config = { characterData: true };
new MutationObserver(function () { return notify(); }).observe(el_1, config);
trigger = function () { el_1.textContent = "" + (toggle_1 ? toggle_1-- : toggle_1++); };
}
callbacks.push(callback);
trigger();
};
export { queueMicroTask };

View File

@ -0,0 +1,2 @@
declare const queueResizeObserver: (cb: () => void) => void;
export { queueResizeObserver };

View File

@ -0,0 +1,7 @@
import { queueMicroTask } from './queueMicroTask';
var queueResizeObserver = function (cb) {
queueMicroTask(function ResizeObserver() {
requestAnimationFrame(cb);
});
};
export { queueResizeObserver };

View File

@ -0,0 +1,3 @@
import { ResizeObserverDetail } from '../ResizeObserverDetail';
declare const resizeObservers: ResizeObserverDetail[];
export { resizeObservers };

Some files were not shown because too many files have changed in this diff Show More