About Standard Ebooks
+ if(DONATION_DRIVE_ON){ ?> + = Template::DonationProgress() ?> + } ?>Standard Ebooks is a volunteer-driven effort to produce a collection of high quality, carefully formatted, accessible, open source, and free public domain ebooks that meet or exceed the quality of commercially produced ebooks. The text and cover art in our ebooks is already believed to be in the U.S. public domain, and Standard Ebooks dedicates its own work to the public domain, thus releasing the entirety of each ebook file into the public domain. All the ebooks we produce are distributed free of cost and free of U.S. copyright restrictions.
Standard Ebooks is organized as a “low-profit L.L.C.,” or “L3C,” a kind of legal entity that blends the charitable focus of a traditional not-for-profit with the ease of organization and maintenance of a regular L.L.C. Our only source of income is donations from readers like you.
Corporate Sponsors
-
+ if(false){ ?>
+
-
+
+
+
Threadable
+ +
+ } ?>
-
@@ -111,11 +122,26 @@ require_once('Core.php');
Patrons Circle
-Join the Patrons Circle to support free literature and to have a direct voice in shaping the future of the Standard Ebooks catalog.
+Join the Patrons Circle to support beautiful, free, and unrestricted digital literature, and to have a direct voice in shaping the future of the Standard Ebooks catalog.
-
+
-
+
Jon Allen
+
+ -
+
David Ballenger
+ -
Michael Barrineau
+ -
+
Leland Blanton
+
+ -
+
Keith Bradner
+
+ -
+
Chris Brooks
+ -
Derrick Burns
@@ -131,6 +157,21 @@ require_once('Core.php');
-
Maxwell Collins-Shenfield
+ -
+
Liza Daly
+
+ -
+
In memory of Georgia Dawson
+
+ -
+
Mahdi Dibaiee
+
+ -
+
Jeffrey Dlouhy
+
+ -
+
Christian Eager
+ -
Alec Feather
@@ -140,12 +181,21 @@ require_once('Core.php');
-
Mac Foster
+ -
+
Michael Ham
+ -
Carey Handfield
+ -
+
Richard Healey
+ -
Curtis Hendzell
+ -
+
Stephen Heywood
+ -
David Howell
@@ -155,20 +205,62 @@ require_once('Core.php');
-
Jared Joslin
+ -
+
Leonard Kirke
+
+ -
+
Eric Korhonen
+
+ -
+
Brendan LeFebvre
+
+ -
+
Monica & Matthew Levine
+
+ -
+
Lance Linimon
+
+ -
+
James Luke
+ -
Patrick Greeley Mahoney
+ -
+
Judith Moore
+
+ -
+
Ozan Ocal
+ -
Andrew Roach
+ -
+
Rollin Salsbery
+ -
Brandon Sowers
+ -
+
Mark Swenson
+
+ -
+
Joshua Tompkins
+
+ -
+
Ted van der Togt
+
+ -
+
Nathan West
+
+ -
+
Blake Westerdahl
+ -
Patrick Weyer
-
-
Anonymous × 5
+Anonymous × 19
Get Involved
+ if(DONATION_DRIVE_ON){ ?> + = Template::DonationProgress() ?> + } ?>Help create ebooks that are a pleasure to read
Standard Ebooks is a volunteer-driven project, and there’s room for people of all skill levels to contribute.
diff --git a/www/css/core.css b/www/css/core.css index 6393e269..131499d7 100644 --- a/www/css/core.css +++ b/www/css/core.css @@ -592,7 +592,6 @@ h1 + section > h2:first-child{ } - a.button, .ebooks nav > a, form button{ @@ -612,7 +611,6 @@ form button{ cursor: pointer; white-space: nowrap; font-size: 1rem; - height: calc(1.4rem + 2rem + 2px); hyphens: none; line-height: 1.2; } @@ -1036,7 +1034,7 @@ section#description h2{ display: none; } -section#description p:first-of-type{ +section#description > p:first-of-type{ margin-top: 0; } @@ -1809,12 +1807,15 @@ main.ebooks nav ol li.highlighted:nth-last-child(2)::after{ line-height: 1; } -.masthead ol.donors{ - grid-template-columns: repeat(auto-fill, minmax(10rem, max-content)); -} - .masthead ol.donors.patrons{ font-style: italic; + display: block; + columns: 3; + margin-left: -.25rem; +} + +.masthead ol.donors.patrons li{ + padding: .25rem; } .masthead ol.donors.patrons li:last-child{ @@ -1943,6 +1944,7 @@ article.ebook section aside.donation{ background-color: rgba(255, 255, 255, .5); font-style: italic; hyphens: none; + box-sizing: border-box; } aside.donation::before{ @@ -1953,6 +1955,7 @@ aside.donation::before{ width: calc(1rem + 2px); top: 0; left: 0; + -webkit-clip-path: polygon(0% 0%, 100% 100%, 0% 100%); clip-path: polygon(0% 0%, 100% 100%, 0% 100%); transform: rotate(90deg); border-radius: 0 0 0 .25rem; @@ -1967,6 +1970,7 @@ aside.donation::after{ width: 1rem; top: 0; left: 0; + -webkit-clip-path: polygon(0% 0%, 100% 100%, 0% 100%); clip-path: polygon(0% 0%, 100% 100%, 0% 100%); transform: rotate(90deg); border-radius: 0 0 0 .25rem; @@ -2051,6 +2055,184 @@ abbr.acronym{ font-variant: all-small-caps; } +aside header{ + font-family: "League Spartan", Arial, sans-serif; + font-style: normal; + text-transform: uppercase; + margin-bottom: 2rem; + font-size: 1.5rem; +} + +.progress{ + position: relative; + font-size: 0; +} + +.progress > div{ + position: absolute; + width: 100%; + height: 100%; + display: flex; + justify-content: center; +} + +.donation a.button{ + display: inline-block; + white-space: normal; + text-align: center; +} + +.donation > p{ + font-style: normal; +} + +.progress + p{ + margin-top: 2rem; + hyphens: auto; +} + +.progress p{ + font-size: 1rem; + font-family: "League Spartan", Arial, sans-serif; + left: 0; + background: rgba(0, 0, 0, .1); + font-style: normal; + border-radius: .25rem; + padding: .25rem .5rem; + color: #fff; + display: block; + align-self: center; + justify-self: center; + justify-content: center; + text-shadow: 1px 1px 0px rgba(0, 0, 0, .5); + border: 1px solid var(--border); + margin-top: 0; +} + +.progress p.start{ + margin-right: auto; + margin-left: 1rem; + border: none; + background: transparent; + padding: 0; + font-size: .75rem; +} + +.progress p.target{ + margin-left: auto; + margin-right: 1rem; + border: none; + background: transparent; + padding: 0; + font-size: .75rem; +} + +.progress > div{ + /* Animate the div instead of the bar itself, because animating the bar triggers an + FF bug that causes infinite requsts to stripes.svg */ + background: url('/images/stripes.svg') transparent; + background-position: 0 0; + animation: progress 2s linear infinite; + z-index: 3; +} + +progress{ + -webkit-appearance: none; + appearance: none; + height: 3rem; + border: 1px solid var(--input-border); + border-radius: .25rem; + width: 100%; + background: var(--body-bg); + overflow: hidden; +} + +progress::-webkit-progress-bar{ + background: var(--body-bg); +} + +progress::-webkit-progress-value{ + background: var(--button); + box-shadow: 1px 0 1px rgba(0, 0, 0, .25); +} + +progress::-moz-progress-bar{ + background: var(--button); + box-shadow: 1px 0 1px rgba(0, 0, 0, .25); +} + +p.stretch-base{ + position: absolute; + border: none; + background: none; + padding: 0; + left: 70.5%; + font-size: .75rem; +} + +progress.stretch{ + --starting-pos: 71.5%; + position: absolute; + left: var(--starting-pos); + top: 0; + width: calc(100% - var(--starting-pos) + 1px); + border-top-left-radius: 0; + border-bottom-left-radius: 0; + overflow: auto; /* enable drop shadow glow */ + border-left: none; +} + +progress.stretch::-webkit-progress-value{ + background: #f2d745; + box-shadow: none; + filter: drop-shadow(0 0 10px #f2d745); + border-right: 1px solid #E9C91F; + border-left: 5px solid #E9C91F; +} + +progress.stretch::-moz-progress-bar{ + background: #f2d745; + box-shadow: none; + filter: drop-shadow(0 0 10px #f2d745); + border-right: 1px solid #E9C91F; + border-left: 5px solid #E9C91F; +} + +aside button.close{ + font-size: 0; + border: none; + box-shadow: none; + padding: 0; + position: absolute; + top: 1rem; + right: 1rem; + background: url('/images/close.svg') transparent; + background-size: cover; + height: 1rem; + width: 1rem; + opacity: .75; +} + +aside button.close:hover{ + opacity: 1; +} + +aside button.close:active{ + left: auto; + top: calc(1rem + 1px); + right: calc(1rem - 1px); +} + +@keyframes progress{ + 0%{ + background-position: -60px 0px; + } + + 100%{ + background-position: 0 0; + } +} + .downloads-container{ display: flex; } @@ -2497,6 +2679,12 @@ abbr.acronym{ text-align: justify; } + aside.donation p:last-child, + article.ebook section aside.donation p:last-child, + aside.donation header p{ + text-align: center; + } + body > header{ padding: 1rem 0; } @@ -2581,6 +2769,10 @@ abbr.acronym{ form[action*="list-manage.com"] fieldset{ grid-column: 1 / span 2; } + + .masthead ol.donors.patrons{ + columns: 2; + } } @media(max-width: 500px){ @@ -2663,13 +2855,13 @@ abbr.acronym{ text-align: left; } - form[action="/settings"] button{ + form[action="/settings"] button:not(.close){ display: block; margin-top: 1rem; margin-left: 0; } - form[action="/settings"] button, + form[action="/settings"] button:not(.close), form[action="/settings"] span, form[action="/settings"] select{ width: 100%; @@ -2703,6 +2895,17 @@ abbr.acronym{ footer ul li::after{ display: none; } + + .progress p.start, + .progress p.target, + .progress p.stretch-base{ + display: none; + } + + .progress p{ + border: none; + background: transparent; + } } @media(max-width: 380px){ @@ -2776,6 +2979,10 @@ abbr.acronym{ max-width: 50%; margin-bottom: 1rem; } + + .masthead ol.donors.patrons{ + columns: 1; + } } @supports not(hyphens: auto){ diff --git a/www/css/dark.css b/www/css/dark.css index 76a245a8..d93b729a 100644 --- a/www/css/dark.css +++ b/www/css/dark.css @@ -80,3 +80,7 @@ label.search::before{ h1,h2,h3,h4,h5,h6{ text-shadow: 2px 2px 0 rgba(0, 0, 0, .75); } + +aside button.close{ + filter: invert(1); +} diff --git a/www/donate/index.php b/www/donate/index.php index 9460cc15..eb4a46b0 100644 --- a/www/donate/index.php +++ b/www/donate/index.php @@ -7,6 +7,9 @@ require_once('Core.php');Donate to Standard Ebooks
and help bring the beauty of literature to the digital age
+ if(DONATION_DRIVE_ON){ ?> + = Template::DonationProgress(['autoHide' => false]) ?> + } ?>Join the Patrons Circle
-Start a monthly donation of $10/month or more to become a member of the Patrons Circle. For less than the cost of a meal out, Patrons Circle members have a direct voice in shaping the future of the Standard Ebooks catalog.
+Members of the Patrons Circle are steadfast supporters of free, unrestricted, and beautifully presented digital literature. Besides helping support the creation of gorgeous ebooks for everyone to enjoy, they also have a direct voice in shaping the future of the Standard Ebooks catalog.
+Join the Patrons Circle with a donation of $10/month or more, or join for one year with a one-time donation of $100 or more.
-
-
Patrons Circle members have their name listed on our masthead for the duration of their gift.
+Patrons Circle members have their name listed on our masthead for the duration of their gift.
-
-
Once per quarter, Patrons Circle members may submit a book for inclusion on our Wanted Ebooks list. (Submissions must conform to our collections policy and may be subject to approval.)
+Once per quarter, Patrons Circle members may submit a book for inclusion on our Wanted Ebooks list. (Submissions must conform to our collections policy and are subject to approval.)
-
Periodically, members of the Patrons Circle vote on a selection from our Wanted Ebooks list to choose an ebook for immediate production. The resulting ebook will be a permanent addition to our online catalog of free digital literature.
Sponsor an ebook of your choice
Is there a book you want to see made into a beautiful digital edition? You can give the gift of literature to readers everywhere by sponsoring an addition to the Standard Ebooks catalog.
Donate at one of the levels below and a book you select will be carefully hand-produced into an ebook edition that meets our high standard of quality, before becoming a permanent addition to our online catalog for the world to read, share, and reuse free of cost or U.S. copyright restrictions. Your name will appear in the ebook’s colophon and metadata, sealing your legacy as a sponsor of the literate arts.
-Before you make your donation, contact the S.E. Editor-in-Chief to confirm that your ebook selection will be accepted at your chosen donation level. These are the general rules:
+Before you make your donation, contact the S.E. Editor-in-Chief to confirm that your ebook selection will be accepted at your chosen donation level. These are the general rules:
-
Your selection must conform to our collections policy. The S.E. Editor-in-Chief reserves the right to modify or reject a selection for any reason.
@@ -52,7 +57,7 @@ require_once('Core.php');Your selection must have a usable transcription available online, for example at Project Gutenberg or Wikisource. In general, Standard Ebooks doesn’t create new transcriptions or work with raw O.C.R. output.
-
-
Your selection’s word count will be calculated from the source transcription using the S.E. toolset, and includes front and back matter like forewords and endnotes. Contact the S.E. Editor-in-Chief before donating so that we can calculate your selection’s word count for you; a word processor can give you an estimate, but we must make the final determination. Note that the final ebook’s word count will differ.
+Your selection’s word count will be calculated from the source transcription using the S.E. toolset, and includes front and back matter like forewords and endnotes. Contact the S.E. Editor-in-Chief before donating so that we can calculate your selection’s word count for you; a word processor can give you an estimate, but we must make the final determination. Note that the final ebook’s word count will differ.
-
Standard Ebooks is the sole decisionmaker regarding all aspects of ebook editing and production. Trust us to create an artifact of beauty.
@@ -117,7 +122,7 @@ require_once('Core.php');Sponsorships at the corporate level are a great way to show your organization’s commitment to the support of the literate arts.
-
-
Your organization’s logo and a link will be listed on our masthead, which is prominently linked to on the Standard Ebooks website’s header and footer.
+Your organization’s logo and a link will be listed on our masthead, which is prominently linked to on the Standard Ebooks website’s header and footer.
-
Get customized ONIX, OPDS, or RSS feeds of our ebook catalog for use by your organization for the duration of your sponsorship.
diff --git a/www/ebooks/author.php b/www/ebooks/author.php index c22c5a2b..38951b4e 100644 --- a/www/ebooks/author.php +++ b/www/ebooks/author.php @@ -2,7 +2,7 @@ require_once('Core.php'); try{ - $urlPath = trim(str_replace('.', '', HttpInput::GetString('url-path', true, '')), '/'); // Contains the portion of the URL (without query string) that comes after https://standardebooks.org/ebooks/ + $urlPath = trim(str_replace('.', '', HttpInput::Str(GET, 'url-path', true, '')), '/'); // Contains the portion of the URL (without query string) that comes after https://standardebooks.org/ebooks/ $wwwFilesystemPath = EBOOKS_DIST_PATH . $urlPath; // Path to the deployed WWW files for this ebook if($urlPath == '' || mb_stripos($wwwFilesystemPath, EBOOKS_DIST_PATH) !== 0 || !is_dir($wwwFilesystemPath)){ diff --git a/www/ebooks/ebook.php b/www/ebooks/ebook.php index ef35d2d0..48f120b5 100644 --- a/www/ebooks/ebook.php +++ b/www/ebooks/ebook.php @@ -9,7 +9,7 @@ use function Safe\apcu_fetch; use function Safe\shuffle; try{ - $urlPath = trim(str_replace('.', '', HttpInput::GetString('url-path', true, '')), '/'); // Contains the portion of the URL (without query string) that comes after https://standardebooks.org/ebooks/ + $urlPath = trim(str_replace('.', '', HttpInput::Str(GET, 'url-path', true, '')), '/'); // Contains the portion of the URL (without query string) that comes after https://standardebooks.org/ebooks/ $wwwFilesystemPath = EBOOKS_DIST_PATH . $urlPath; // Path to the deployed WWW files for this ebook if($urlPath == '' || mb_stripos($wwwFilesystemPath, EBOOKS_DIST_PATH) !== 0){ @@ -127,8 +127,8 @@ catch(InvalidEbookException $ex){Description
- if(DONATION_HOLIDAY_ALERT_ON){ ?> - = Template::DonationAlert(['holidays' => true]) ?> + if(DONATION_DRIVE_ON){ ?> + = Template::DonationProgress() ?> }elseif(DONATION_ALERT_ON){ ?> = Template::DonationAlert() ?> } ?> diff --git a/www/ebooks/index.php b/www/ebooks/index.php index ea6b933e..21076398 100644 --- a/www/ebooks/index.php +++ b/www/ebooks/index.php @@ -4,13 +4,13 @@ require_once('Core.php'); use function Safe\preg_replace; try{ - $page = HttpInput::GetInt('page', 1); - $perPage = HttpInput::GetInt('per-page', EBOOKS_PER_PAGE); - $query = HttpInput::GetString('query', false); + $page = HttpInput::Int(GET, 'page', 1); + $perPage = HttpInput::Int(GET, 'per-page', EBOOKS_PER_PAGE); + $query = HttpInput::Str(GET, 'query', false); $tags = HttpInput::GetArray('tags', []); - $collection = HttpInput::GetString('collection', false); - $view = HttpInput::GetString('view', false); - $sort = HttpInput::GetString('sort', false); + $collection = HttpInput::Str(GET, 'collection', false); + $view = HttpInput::Str(GET, 'view', false); + $sort = HttpInput::Str(GET, 'sort', false); $pages = 0; $totalEbooks = 0; $collectionObject = null; @@ -135,8 +135,10 @@ catch(InvalidCollectionException $ex){ ?>= Template::Header(['title' => $pageTitle, 'highlight' => 'ebooks', 'description' => $pageDescription]) ?>= $pageHeader ?>
- if(DONATION_HOLIDAY_ALERT_ON){ ?> - = Template::DonationAlert(['holidays' => true]) ?> + if(DONATION_DRIVE_ON){ ?> + = Template::DonationProgress() ?> + }elseif(DONATION_HOLIDAY_ALERT_ON){ ?> + = Template::DonationAlert() ?> } ?> if($collection === null){ ?> = Template::SearchForm(['query' => $query, 'tags' => $tags, 'sort' => $sort, 'view' => $view, 'perPage' => $perPage]) ?> diff --git a/www/images/close.svg b/www/images/close.svg new file mode 100644 index 00000000..c795b285 --- /dev/null +++ b/www/images/close.svg @@ -0,0 +1,16 @@ + + diff --git a/www/images/masthead/sponsors/threadable.svg b/www/images/masthead/sponsors/threadable.svg new file mode 100644 index 00000000..1426211d --- /dev/null +++ b/www/images/masthead/sponsors/threadable.svg @@ -0,0 +1,13 @@ + diff --git a/www/manual/index.php b/www/manual/index.php index 9e2fb942..853c96f2 100644 --- a/www/manual/index.php +++ b/www/manual/index.php @@ -8,7 +8,7 @@ use function Safe\sort; $currentManual = Manual::GetLatestVersion(); -$url = HttpInput::GetString('url', true, ''); +$url = HttpInput::Str(GET, 'url', true, ''); $url = preg_replace('|^/|ius', '', $url); $url = preg_replace('|\.php$|ius', '', $url); $url = preg_replace('|/$|ius', '', $url); diff --git a/www/opds/search.php b/www/opds/search.php index 2642835d..dd6cb260 100644 --- a/www/opds/search.php +++ b/www/opds/search.php @@ -6,7 +6,7 @@ $now = new DateTime('now', new DateTimeZone('UTC')); $ebooks = []; try{ - $query = HttpInput::GetString('query', false); + $query = HttpInput::Str(GET, 'query', false); if($query !== null){ $ebooks = Library::Search($query); diff --git a/www/settings/post.php b/www/settings/post.php index c18f1da2..1b520ce5 100644 --- a/www/settings/post.php +++ b/www/settings/post.php @@ -3,21 +3,30 @@ require_once('Core.php'); use function Safe\strtotime; -$colorScheme = $_POST['color-scheme'] ?? 'auto'; +$hideDonationAlert = HttpInput::Bool(POST, 'hide-donation-alert'); +$colorScheme = HttpInput::Str(POST, 'color-scheme'); -if($colorScheme !== 'dark' && $colorScheme !== 'light' && $colorScheme !== 'auto'){ - $colorScheme = 'auto'; +if($hideDonationAlert !== null){ + setcookie('hide-donation-alert', $hideDonationAlert ? 'true' : 'false', strtotime('+1 month'), '/', '', true, true); } -if($colorScheme == 'auto'){ - // Delete the cookie; auto is the default - setcookie('color-scheme', '', 0, '/', '', true, true); -} -else{ - setcookie('color-scheme', $colorScheme, strtotime('+10 years'), '/', '', true, true); +if($colorScheme !== null){ + if($colorScheme !== 'dark' && $colorScheme !== 'light' && $colorScheme !== 'auto'){ + $colorScheme = 'auto'; + } + + if($colorScheme == 'auto'){ + // Delete the cookie; auto is the default + setcookie('color-scheme', '', 0, '/', '', true, true); + } + else{ + setcookie('color-scheme', $colorScheme, strtotime('+1 year'), '/', '', true, true); + } } // HTTP 303, See other http_response_code(303); -header('Location: /settings'); + +$redirect = $_SERVER['HTTP_REFERER'] ?? '/'; +header('Location: ' . $redirect); ?>
-
-