├── .gitignore ├── ads.txt ├── img ├── share.jpg ├── screenshot1.png ├── screenshot2.png ├── screenshot3.png ├── favicon-16x16.png ├── favicon-32x32.png ├── icon-thumbs-up.png ├── maskable_icon.png ├── mstile-144x144.png ├── mstile-150x150.png ├── mstile-310x150.png ├── mstile-310x310.png ├── mstile-70x70.png ├── apple-touch-icon.png ├── icon-thumbs-down.png ├── monochrome_icon.png ├── android-chrome-36x36.png ├── android-chrome-48x48.png ├── android-chrome-72x72.png ├── android-chrome-96x96.png ├── android-chrome-144x144.png ├── android-chrome-192x192.png ├── android-chrome-256x256.png ├── android-chrome-384x384.png ├── android-chrome-512x512.png ├── apple-touch-icon-114x114.png ├── apple-touch-icon-120x120.png ├── apple-touch-icon-144x144.png ├── apple-touch-icon-152x152.png ├── apple-touch-icon-180x180.png ├── apple-touch-icon-57x57.png ├── apple-touch-icon-60x60.png ├── apple-touch-icon-72x72.png ├── apple-touch-icon-76x76.png ├── browserconfig.xml ├── safari-pinned-tab.svg ├── favicon.svg ├── logo_header.svg └── logo_nav.svg ├── scss ├── _images.scss ├── _icons.scss ├── _animations.scss ├── _colors.scss ├── _layout.scss ├── _fonts.scss ├── _notifications.scss ├── _typography.scss ├── _links-buttons-forms.scss ├── style.scss └── index.scss ├── fonts ├── roboto │ ├── roboto-700.eot │ ├── roboto-700.ttf │ ├── roboto-700.woff │ ├── roboto-700.woff2 │ ├── roboto-regular.eot │ ├── roboto-regular.ttf │ ├── roboto-regular.woff │ └── roboto-regular.woff2 ├── roboto-slab │ ├── roboto-slab-700.eot │ ├── roboto-slab-700.ttf │ ├── roboto-slab-700.woff │ ├── roboto-slab-700.woff2 │ ├── roboto-slab-regular.eot │ ├── roboto-slab-regular.ttf │ ├── roboto-slab-regular.woff │ └── roboto-slab-regular.woff2 └── fontawesome │ ├── webfonts │ ├── fa-brands-400.eot │ ├── fa-brands-400.ttf │ ├── fa-solid-900.eot │ ├── fa-solid-900.ttf │ ├── fa-solid-900.woff │ ├── fa-brands-400.woff │ ├── fa-brands-400.woff2 │ └── fa-solid-900.woff2 │ └── css │ ├── solid.min.css │ └── brands.min.css ├── robots.txt ├── sellers.json ├── tsconfig.json ├── .github └── FUNDING.yml ├── package.json ├── .well-known └── assetlinks.json ├── webpack.config.js ├── js ├── index.ts ├── cookies.ts ├── normalizeText.ts ├── urls.ts ├── notifications.ts └── main.ts ├── input.js ├── README.md ├── manifest.json ├── index.php ├── _redirects ├── sw.js ├── index.html ├── gin.html ├── ham.html ├── rum.html ├── tea.html ├── acai.html └── beef.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.code-workspace 2 | node_modules 3 | .DS_Store -------------------------------------------------------------------------------- /ads.txt: -------------------------------------------------------------------------------- 1 | google.com, pub-9469003608080437, DIRECT, f08c47fec0942fa0 -------------------------------------------------------------------------------- /img/share.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/share.jpg -------------------------------------------------------------------------------- /scss/_images.scss: -------------------------------------------------------------------------------- 1 | /* 2 | ** Images 3 | */ 4 | :root { 5 | --header-image: 'none'; 6 | } -------------------------------------------------------------------------------- /img/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/screenshot1.png -------------------------------------------------------------------------------- /img/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/screenshot2.png -------------------------------------------------------------------------------- /img/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/screenshot3.png -------------------------------------------------------------------------------- /img/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/favicon-16x16.png -------------------------------------------------------------------------------- /img/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/favicon-32x32.png -------------------------------------------------------------------------------- /img/icon-thumbs-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/icon-thumbs-up.png -------------------------------------------------------------------------------- /img/maskable_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/maskable_icon.png -------------------------------------------------------------------------------- /img/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/mstile-144x144.png -------------------------------------------------------------------------------- /img/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/mstile-150x150.png -------------------------------------------------------------------------------- /img/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/mstile-310x150.png -------------------------------------------------------------------------------- /img/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/mstile-310x310.png -------------------------------------------------------------------------------- /img/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/mstile-70x70.png -------------------------------------------------------------------------------- /img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/apple-touch-icon.png -------------------------------------------------------------------------------- /img/icon-thumbs-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/icon-thumbs-down.png -------------------------------------------------------------------------------- /img/monochrome_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/monochrome_icon.png -------------------------------------------------------------------------------- /fonts/roboto/roboto-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto/roboto-700.eot -------------------------------------------------------------------------------- /fonts/roboto/roboto-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto/roboto-700.ttf -------------------------------------------------------------------------------- /fonts/roboto/roboto-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto/roboto-700.woff -------------------------------------------------------------------------------- /fonts/roboto/roboto-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto/roboto-700.woff2 -------------------------------------------------------------------------------- /img/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/android-chrome-36x36.png -------------------------------------------------------------------------------- /img/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/android-chrome-48x48.png -------------------------------------------------------------------------------- /img/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/android-chrome-72x72.png -------------------------------------------------------------------------------- /img/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/android-chrome-96x96.png -------------------------------------------------------------------------------- /fonts/roboto/roboto-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto/roboto-regular.eot -------------------------------------------------------------------------------- /fonts/roboto/roboto-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto/roboto-regular.ttf -------------------------------------------------------------------------------- /fonts/roboto/roboto-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto/roboto-regular.woff -------------------------------------------------------------------------------- /img/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/android-chrome-144x144.png -------------------------------------------------------------------------------- /img/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/android-chrome-192x192.png -------------------------------------------------------------------------------- /img/android-chrome-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/android-chrome-256x256.png -------------------------------------------------------------------------------- /img/android-chrome-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/android-chrome-384x384.png -------------------------------------------------------------------------------- /img/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/android-chrome-512x512.png -------------------------------------------------------------------------------- /img/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /img/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /img/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /img/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /img/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /img/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /img/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /img/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /img/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/img/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /fonts/roboto/roboto-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto/roboto-regular.woff2 -------------------------------------------------------------------------------- /fonts/roboto-slab/roboto-slab-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto-slab/roboto-slab-700.eot -------------------------------------------------------------------------------- /fonts/roboto-slab/roboto-slab-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto-slab/roboto-slab-700.ttf -------------------------------------------------------------------------------- /fonts/roboto-slab/roboto-slab-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto-slab/roboto-slab-700.woff -------------------------------------------------------------------------------- /fonts/roboto-slab/roboto-slab-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto-slab/roboto-slab-700.woff2 -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /.git 3 | Disallow: /.gitignore 4 | Allow: / 5 | 6 | Sitemap: https://dogsafefoods.com/sitemap.xml -------------------------------------------------------------------------------- /fonts/roboto-slab/roboto-slab-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto-slab/roboto-slab-regular.eot -------------------------------------------------------------------------------- /fonts/roboto-slab/roboto-slab-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto-slab/roboto-slab-regular.ttf -------------------------------------------------------------------------------- /fonts/roboto-slab/roboto-slab-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto-slab/roboto-slab-regular.woff -------------------------------------------------------------------------------- /fonts/fontawesome/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/fontawesome/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /fonts/fontawesome/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/fontawesome/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /fonts/fontawesome/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/fontawesome/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /fonts/fontawesome/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/fontawesome/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /fonts/fontawesome/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/fontawesome/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /fonts/roboto-slab/roboto-slab-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/roboto-slab/roboto-slab-regular.woff2 -------------------------------------------------------------------------------- /fonts/fontawesome/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/fontawesome/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /fonts/fontawesome/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/fontawesome/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /fonts/fontawesome/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikkifurls/dogsafefoods/HEAD/fonts/fontawesome/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /sellers.json: -------------------------------------------------------------------------------- 1 | { 2 | "sellerId": "pub-9469003608080437", 3 | "sellerType": "PUBLISHER", 4 | "name": "Nicole Furlan", 5 | "domain": "nicolefurlan.com" 6 | } -------------------------------------------------------------------------------- /scss/_icons.scss: -------------------------------------------------------------------------------- 1 | @use 'colors'; 2 | 3 | /* 4 | ** Icons. 5 | */ 6 | .icon, 7 | span[class*="icon"] { 8 | vertical-align: bottom; 9 | } 10 | 11 | .icon-heart { 12 | color: var(--color-accent); 13 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "noImplicitAny": true, 5 | "module": "esnext", 6 | "target": "es6", 7 | "allowJs": true, 8 | "moduleResolution": "node", 9 | } 10 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [nikkifurls] 4 | patreon: nicolefurlan 5 | custom: ["https://nicolefurlan.com", "https://www.paypal.com/donate?hosted_button_id=M7MMF3EWQTLKG"] 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dogsafefoods.com", 3 | "version": "73", 4 | "description": "Sharing food with your dog? Make sure it's safe first", 5 | "scripts": { 6 | "build": "webpack --mode production" 7 | }, 8 | "dependencies": { 9 | "css-loader": "^6.8.1", 10 | "sass": "^1.65.1", 11 | "sass-loader": "^13.3.2", 12 | "style-loader": "^3.3.3", 13 | "ts-loader": "^9.4.4", 14 | "webpack-cli": "^5.1.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /scss/_animations.scss: -------------------------------------------------------------------------------- 1 | @use 'colors'; 2 | 3 | /* 4 | ** Animations 5 | */ 6 | @keyframes pulse { 7 | 0% { 8 | color: var(--color-gray-med); 9 | } 10 | 11 | 17% { 12 | color: var(--color-main); 13 | } 14 | 15 | 35% { 16 | color: var(--color-main-dark); 17 | } 18 | 19 | 52% { 20 | color: var(--color-accent-dark); 21 | } 22 | 23 | 68% { 24 | color: var(--color-accent); 25 | } 26 | 27 | 86% { 28 | color: var(--color-gray-med); 29 | } 30 | } -------------------------------------------------------------------------------- /img/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #000000 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /scss/_colors.scss: -------------------------------------------------------------------------------- 1 | /* 2 | ** Colors 3 | */ 4 | :root { 5 | --color-main: #0FF; /* cyan */ 6 | --color-main-dark: #008B8B; /* darkcyan */ 7 | --color-accent: #FFA07A; /* lightsalmon */ 8 | --color-accent-dark: #FF6347; /* tomato */ 9 | --color-black: #000; 10 | --color-gray-light: #A0A0A0; 11 | --color-gray-med: #464646; 12 | --color-gray-dark: #151515; 13 | --color-white: #FFF; 14 | --color-facebook: #3B5998; 15 | --color-twitter: #1DA1F2; 16 | --color-patreon: #F96854; 17 | --color-paypal: #0E55AA; 18 | --color-android: #839E2E; 19 | --color-apple: #8A98A0; 20 | } -------------------------------------------------------------------------------- /img/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fonts/fontawesome/css/solid.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900} -------------------------------------------------------------------------------- /fonts/fontawesome/css/brands.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands";font-weight:400} -------------------------------------------------------------------------------- /.well-known/assetlinks.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "relation": ["delegate_permission/common.handle_all_urls"], 3 | "target": { 4 | "namespace": "android_app", 5 | "package_name": "com.dog_safe_foods.twa", 6 | "sha256_cert_fingerprints": [ 7 | "57:73:04:66:7A:C2:F2:B0:8D:25:7B:BF:C8:56:89:E4:2A:D7:6C:F5:F9:CC:85:40:B8:11:EE:83:34:20:63:63" 8 | ] 9 | } 10 | }, { 11 | "relation": ["delegate_permission/common.handle_all_urls"], 12 | "target": { 13 | "namespace": "android_app", 14 | "package_name": "com.dogsafefoods.twa", 15 | "sha256_cert_fingerprints": [ 16 | "F2:26:49:E0:DC:CC:80:65:21:CC:F8:F5:49:EB:57:5E:FE:F7:0E:28:C0:52:E3:14:73:36:BC:43:9B:91:FC:E2" 17 | ] 18 | } 19 | }] -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: [ './js/index.ts', './js/main.ts' ], 5 | devtool: 'inline-source-map', 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.tsx?$/, 10 | use: 'ts-loader', 11 | exclude: /node_modules/, 12 | }, 13 | { 14 | test: /\.s[ac]ss$/i, 15 | use: [ 16 | // Creates `style` nodes from JS strings 17 | "style-loader", 18 | // Translates CSS into CommonJS 19 | "css-loader", 20 | // Compiles Sass to CSS 21 | "sass-loader", 22 | ], 23 | }, 24 | ], 25 | }, 26 | resolve: { 27 | extensions: ['.ts', '.js'], 28 | }, 29 | output: { 30 | filename: 'bundle.[contenthash].js', 31 | path: path.resolve(__dirname, 'js'), 32 | }, 33 | }; -------------------------------------------------------------------------------- /js/index.ts: -------------------------------------------------------------------------------- 1 | import { showNotification } from "./notifications"; 2 | import { setLinkEventListeners } from "./urls"; 3 | 4 | import '../scss/index.scss'; 5 | import '../scss/style.scss'; 6 | 7 | window.addEventListener('load', () => { 8 | 9 | // Display cookie notification. 10 | showNotification({ text: 'cookie', className: 'notification-cookie' }); 11 | 12 | // If URL parameters are passed in, check for notification. 13 | if (window.location.search) { 14 | const query = window.location.search.replace('?', '').split('='); 15 | if (query.length > 1) { 16 | const [ param, value ] = query; 17 | 18 | if ('notification' === param && value) { 19 | showNotification({ text: value }); 20 | } 21 | } 22 | } 23 | 24 | // Set event listeners for all event links. 25 | setLinkEventListeners(); 26 | }); 27 | -------------------------------------------------------------------------------- /scss/_layout.scss: -------------------------------------------------------------------------------- 1 | @use 'colors'; 2 | 3 | /* 4 | ** Layout 5 | */ 6 | 7 | $media-tablet: 30em; 8 | $media-desktop: 48em; 9 | $media-desktop-large: 75em; 10 | 11 | :root { 12 | --space-xxsmall: .1em; 13 | --space-xsmall: .25em; 14 | --space-small: .5em; 15 | --space-medium: .75em; 16 | --space-large: 1em; 17 | --space-xlarge: 1.25em; 18 | --space-xxlarge: 1.5em; 19 | --space-super: 2em; 20 | --space-xsuper: 3em; 21 | --space-xxsuper: 4em; 22 | --space-border: 4px; 23 | } 24 | 25 | @mixin padding-default($padding-top-bottom: 0) { 26 | padding: $padding-top-bottom var(--space-large); 27 | 28 | @media (min-width: $media-desktop) { 29 | padding: $padding-top-bottom var(--space-xlarge); 30 | } 31 | 32 | @media (min-width: $media-desktop-large) { 33 | padding: $padding-top-bottom var(--space-xxlarge); 34 | } 35 | } -------------------------------------------------------------------------------- /scss/_fonts.scss: -------------------------------------------------------------------------------- 1 | @use 'layout'; 2 | 3 | /* 4 | ** Fonts 5 | */ 6 | :root { 7 | --font-heading: "Roboto Slab"; 8 | --font-heading-stack: var(--font-heading), Georgia, serif; 9 | --font-body: "Roboto"; 10 | --font-body-stack: var(--font-body), helvetica, arial, sans-serif; 11 | --font-small: .8rem; 12 | --font-medium: 1rem; 13 | --font-large: 1.25rem; 14 | --font-xlarge: 1.5rem; 15 | --font-xxlarge: 2rem; 16 | --font-super: 2.5rem; 17 | --font-xsuper: 3rem; 18 | --font-xxsuper: 3.5rem; 19 | --font-normal: 400; 20 | --font-bold: 700; 21 | }; 22 | 23 | @mixin font-size($font-size: 1em) { 24 | font-size: $font-size; 25 | 26 | @media (min-width: layout.$media-desktop) { 27 | font-size: calc(#{$font-size} * 1.25); 28 | } 29 | 30 | @media (min-width: layout.$media-desktop-large) { 31 | font-size: calc(#{$font-size} * 1.5); 32 | } 33 | } -------------------------------------------------------------------------------- /scss/_notifications.scss: -------------------------------------------------------------------------------- 1 | @use 'colors'; 2 | @use 'layout'; 3 | 4 | /* 5 | ** Notifications 6 | */ 7 | .notification { 8 | background-color: var(--color-gray-med); 9 | border-top: var(--space-border) solid var(--color-gray-light); 10 | bottom: 0; 11 | color: var(--color-white); 12 | display: none; 13 | left: 0; 14 | @include layout.padding-default(var(--space-small)); 15 | position: fixed; 16 | right: 0; 17 | z-index: 999; 18 | 19 | &.notification-share { 20 | p { 21 | @extend .container-scrollable; 22 | padding-bottom: var(--space-xsmall); 23 | 24 | a { 25 | display: flex; 26 | align-items: center; 27 | } 28 | } 29 | } 30 | 31 | p { 32 | margin: 0; 33 | margin-right: var(--space-large); 34 | } 35 | 36 | .container { 37 | display: grid; 38 | grid-template-columns: 1fr minmax(10px, 5%); 39 | } 40 | 41 | .url { 42 | user-select: all; 43 | } 44 | } -------------------------------------------------------------------------------- /input.js: -------------------------------------------------------------------------------- 1 | const decodeText = (text, type = "text") => { 2 | 3 | if (typeof text == "string") { 4 | // Remove leading and trailing slashes 5 | text = text.replace(/^\/|\/$/g, ""); 6 | 7 | // Transform accented characters to their non-accented version 8 | text = text.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); 9 | 10 | // Remove .html extension 11 | text = text.replace(/.html/g, " "); 12 | 13 | // Remove + 14 | text = text.replace(/\+/g, " "); 15 | 16 | // Remove %20 17 | text = text.replace(/%20/g, " "); 18 | 19 | // Change dashes to spaces, unless type=url 20 | if (type == "url") { 21 | text = text.replace(/ /g, "-"); 22 | } else { 23 | text = text.replace(/-/g, " "); 24 | } 25 | 26 | // Transform to lowercase 27 | text = text.toLowerCase(); 28 | 29 | // Trim whitespace 30 | text = text.trim(); 31 | 32 | } else { 33 | 34 | console.error("decodeText()", "text should be string, " + typeof text + " provided") 35 | } 36 | 37 | return text; 38 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dog Safe Foods ([dogsafefoods.com](https://dogsafefoods.com)) 2 | Sharing food with your dog? Make sure it's safe first 3 | 4 | ### What is this? 5 | Dog Safe Foods is an app that allows you to instantly determine whether a food is safe for dogs to consume. 6 | 7 | It is a static Progressive Web App (PWA), using an "offline-first" [(cache falling back to the network)](https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker#cache_falling_back_to_the_network) caching strategy, which means any files specified in [`sw.js`](https://github.com/nikkifurls/dogsafefoods/blob/master/sw.js) will be cached, and therefore, accessible offline. Non-cached requests (including non-GET requests, as they cannot be cached), will not be accessible offline, and instead, will ping the network or fail if there is no network available. 8 | 9 | ### How do I use it? 10 | [Open it up](https://dogsafefoods.com) and search! 11 | 12 | ### Can I contribute? 13 | YES! Contributions are welcome! 14 | 15 | ### How do I get in touch? 16 | Twitter: [@dogsafefoods](https://twitter.com/dogsafefoods) 17 | 18 | Email: [info@dogsafefoods.com](mailto:info@dogsafefoods.com) 19 | -------------------------------------------------------------------------------- /js/cookies.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Set cookie. 3 | * @param {Object} cookie setCookie function requires an object with the following properties: 4 | * @param {string} cookie.name Cookie name. 5 | * @param {string|boolean=} cookie.value Cookie value. Defaults to boolean true. 6 | */ 7 | export const setCookie = ({ name, value = true } : { name: string, value?: string|boolean }): void => { 8 | const date = new Date(); 9 | date.setTime(date.getTime() + (365 * 24 * 60 * 60 * 1000)); 10 | document.cookie = name + '=' + value + '; expires=' + date.toUTCString() + '; path=/'; 11 | } 12 | 13 | /** 14 | * Get cookie by name. 15 | * @param {Object} cookie getCookie function requires an object with the following properties: 16 | * @param {string} cookie.name Cookie name. 17 | * @returns {string} Cookie value. 18 | */ 19 | export const getCookie = ({ name }: { name: string }): string => { 20 | const cookieValue = document.cookie.split('; ') 21 | .map(cookie => { 22 | // Extract the cookie name and value from the cookie string. 23 | const [ cookieName, cookieValue ] = cookie.split('='); 24 | 25 | // If the cookie name matches the name provided, return the cookie. 26 | return cookieName === name ? cookieValue : null; 27 | }) 28 | .filter(cookie => cookie); // Remove null values. 29 | 30 | return cookieValue[0] ?? ''; 31 | } 32 | -------------------------------------------------------------------------------- /js/normalizeText.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Normalize a text string. 3 | * 4 | * @param {Object} textString The `normalizeText` function takes an object as its parameter with the following properties: 5 | * @param {string} textString.text Text string to normalize. 6 | * @param {string=} textString.type Type of normalization to perform. Defaults to 'text'. Other possible value is 'url'. 7 | * @returns {string} Normalized text string. 8 | */ 9 | const normalizeText = ({ text, type = 'text' } : { text: string, type?: string }): string => { 10 | 11 | // Remove leading and trailing slashes. 12 | let decodedText = text.replace(/^\/|\/$/g, ''); 13 | 14 | // Transform accented characters to their non-accented version. 15 | decodedText = decodedText.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); 16 | 17 | // Remove .html extension. 18 | decodedText = decodedText.replace(/.html/g, ' '); 19 | 20 | // Remove +. 21 | decodedText = decodedText.replace(/\+/g, ' '); 22 | 23 | // Remove %20. 24 | decodedText = decodedText.replace(/%20/g, ' '); 25 | 26 | // Change dashes to spaces, unless type=url. 27 | if (type === 'url') { 28 | decodedText = decodedText.replace(/ /g, '-'); 29 | } else { 30 | decodedText = decodedText.replace(/-/g, ' '); 31 | } 32 | 33 | // Transform to lowercase. 34 | decodedText = decodedText.toLowerCase(); 35 | 36 | // Trim whitespace. 37 | decodedText = decodedText.trim(); 38 | 39 | return decodedText; 40 | } 41 | 42 | export default normalizeText; 43 | -------------------------------------------------------------------------------- /scss/_typography.scss: -------------------------------------------------------------------------------- 1 | @use 'fonts'; 2 | 3 | /* 4 | ** Typography 5 | */ 6 | html { 7 | font-size: 100%; 8 | } 9 | 10 | body, 11 | .description { 12 | font-family: var(--font-body-stack); 13 | } 14 | 15 | body { 16 | @include fonts.font-size(var(--font-medium)); 17 | background-color: var(--color-gray-dark); 18 | line-height: 1.4; 19 | } 20 | 21 | header, 22 | h1, 23 | h2, 24 | h3, 25 | h4, 26 | .title { 27 | font-family: var(--font-heading-stack); 28 | 29 | } 30 | 31 | h1, 32 | h2, 33 | .title { 34 | line-height: 1.1; 35 | } 36 | 37 | h3, 38 | h4, 39 | .description { 40 | line-height: 1.2; 41 | } 42 | 43 | h1, 44 | h2, 45 | h3, 46 | h4, 47 | .title { 48 | color: var(--color-accent); 49 | } 50 | 51 | header, 52 | h1, 53 | h2, 54 | h3, 55 | h4, 56 | .title, 57 | .description, 58 | a, 59 | button, 60 | .button, 61 | input[type="button"], 62 | input[type="submit"], 63 | summary, 64 | dt { 65 | font-weight: var(--font-bold); 66 | } 67 | 68 | blockquote, 69 | body, 70 | dd, 71 | dl, 72 | figure, 73 | h1, 74 | h2, 75 | h3, 76 | h4, 77 | p { 78 | margin: 0; 79 | } 80 | 81 | h1, 82 | h2, 83 | h3, 84 | h4, 85 | p, 86 | dd { 87 | margin: var(--space-small) auto; 88 | } 89 | 90 | dl, 91 | ol, 92 | ul { 93 | dt, 94 | dd, 95 | li { 96 | margin: var(--space-small) auto; 97 | } 98 | } 99 | 100 | h1, 101 | .title { 102 | @include fonts.font-size(var(--font-xsuper)); 103 | } 104 | 105 | h2 { 106 | @include fonts.font-size(var(--font-super)); 107 | } 108 | 109 | h3, 110 | .description { 111 | @include fonts.font-size(var(--font-xlarge)); 112 | } 113 | 114 | h4 { 115 | @include fonts.font-size(var(--font-large)); 116 | } 117 | 118 | .social, 119 | button, 120 | .button, 121 | input[type="button"], 122 | input[type="submit"], 123 | textarea, 124 | select { 125 | font-size: var(--font-large); 126 | } 127 | 128 | code, 129 | kbd, 130 | samp, 131 | pre, 132 | .code { 133 | font-family: monospace, monospace; 134 | } 135 | 136 | .bold { 137 | font-weight: var(--font-bold); 138 | } 139 | 140 | .italic { 141 | font-style: italic; 142 | } 143 | 144 | .underline { 145 | text-decoration: underline; 146 | } 147 | 148 | mark::before, 149 | mark::after { 150 | clip-path: inset(100%); 151 | clip: rect(1px, 1px, 1px, 1px); 152 | height: 1px; 153 | overflow: hidden; 154 | position: absolute; 155 | white-space: nowrap; 156 | width: 1px; 157 | } 158 | 159 | mark::before { 160 | content: " [highlight start] "; 161 | } 162 | 163 | mark::after { 164 | content: " [highlight end] "; 165 | } -------------------------------------------------------------------------------- /scss/_links-buttons-forms.scss: -------------------------------------------------------------------------------- 1 | @use 'colors'; 2 | @use 'layout'; 3 | 4 | /* 5 | ** Links, Buttons, and Forms. 6 | */ 7 | .buttons, 8 | label { 9 | margin: var(--space-small) auto; 10 | } 11 | 12 | a { 13 | color: var(--color-main); 14 | position: relative; 15 | text-decoration: none; 16 | 17 | &:hover, 18 | &:active, 19 | &:focus { 20 | color: var(--color-accent); 21 | } 22 | } 23 | 24 | button, 25 | .button, 26 | input, 27 | select, 28 | textarea { 29 | border: none; 30 | border-radius: var(--space-border); 31 | vertical-align: middle; 32 | } 33 | 34 | .facebook, 35 | .button.facebook, 36 | button.facebook { 37 | background-color: var(--color-facebook); 38 | } 39 | 40 | .twitter, 41 | .button.twitter, 42 | button.twitter { 43 | background-color: var(--color-twitter); 44 | } 45 | 46 | .patreon, 47 | .button.patreon, 48 | button.patreon { 49 | background-color: var(--color-patreon); 50 | } 51 | 52 | .paypal, 53 | .button.paypal 54 | button.paypal { 55 | background-color: var(--color-paypal); 56 | } 57 | 58 | .android:not(.install), 59 | .button.android 60 | button.android { 61 | background-color: var(--color-android); 62 | } 63 | 64 | .apple:not(.install), 65 | .button.apple 66 | button.apple { 67 | background-color: var(--color-apple); 68 | } 69 | 70 | button, 71 | .button, 72 | input[type="button"], 73 | input[type="submit"] { 74 | background-color: var(--color-main-dark); 75 | color: var(--color-white); 76 | line-height: 1; 77 | padding: var(--space-small) var(--space-large); 78 | 79 | &[type=color] { 80 | padding: 0; 81 | } 82 | 83 | &::-webkit-search-cancel-button { 84 | cursor: pointer; 85 | } 86 | 87 | &:hover, 88 | &:active, 89 | &:focus { 90 | background-color: var(--color-accent-dark); 91 | color: var(--color-white); 92 | } 93 | 94 | &.disabled { 95 | &, 96 | &:active, 97 | &:hover, 98 | &:focus { 99 | background-color: var(--color-gray-dark); 100 | color: var(--color-white); 101 | cursor: not-allowed; 102 | pointer-events: none; 103 | } 104 | } 105 | } 106 | 107 | button, 108 | .button, 109 | details summary, 110 | .close, 111 | .copy, 112 | .install, 113 | .share { 114 | cursor: pointer; 115 | } 116 | 117 | input, 118 | textarea, 119 | select { 120 | background-color: var(--color-white); 121 | color: var(--color-gray-dark); 122 | font-size: inherit; 123 | padding: var(--space-small); 124 | } 125 | 126 | label { 127 | display: grid; 128 | align-content: center; 129 | align-items: center; 130 | justify-content: space-between; 131 | grid-template-columns: minmax(10em, 25%) 1fr; 132 | } -------------------------------------------------------------------------------- /img/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scss/style.scss: -------------------------------------------------------------------------------- 1 | @use 'colors'; 2 | @use 'fonts'; 3 | @use 'layout'; 4 | 5 | :root { 6 | --font-heading: "Roboto Slab"; 7 | --font-body: "Roboto"; 8 | --color-main: #8BCFDF; 9 | --color-main-dark: #264052; 10 | --color-accent: #F8B2BF; 11 | --color-accent-dark: #662632; 12 | --color-accent-secondary: #D6CB62; 13 | --color-accent-secondary-dark: #B17D1F; 14 | } 15 | 16 | .index, 17 | .search, 18 | .special { 19 | header h1 { 20 | display: none; 21 | } 22 | } 23 | 24 | main { 25 | padding-top: 0; 26 | 27 | .search { 28 | text-align: center; 29 | 30 | h2 { 31 | display: none; 32 | } 33 | 34 | label { 35 | grid-template-columns: none; 36 | justify-content: center; 37 | } 38 | } 39 | 40 | .tips { 41 | li { 42 | b { 43 | background-color: var(--color-accent-dark); 44 | } 45 | } 46 | } 47 | 48 | .num-results { 49 | background-color: var(--color-gray-dark); 50 | font-weight: var(--font-bold); 51 | text-align: center; 52 | text-transform: uppercase; 53 | } 54 | 55 | .results { 56 | text-align: left; 57 | 58 | a { 59 | color: var(--color-white); 60 | } 61 | } 62 | 63 | .result { 64 | background-color: var(--color-gray-dark); 65 | border: var(--space-border) solid var(--color-gray-med); 66 | border-radius: var(--space-border); 67 | margin: var(--space-small) 0; 68 | padding: var(--space-xxlarge) var(--space-super); 69 | position: relative; 70 | 71 | @media (min-width: layout.$media-tablet) { 72 | padding: var(--space-xsuper) var(--space-xxsuper); 73 | } 74 | 75 | .share { 76 | color: var(--color-white); 77 | font-size: inherit; 78 | font-size: larger; 79 | position: absolute; 80 | right: var(--space-small); 81 | top: var(--space-xsmall); 82 | } 83 | 84 | h3, 85 | h4 { 86 | color: var(--color-white); 87 | } 88 | 89 | &.safe { 90 | background-color: var(--color-main-dark); 91 | border: var(--space-border) solid var(--color-main); 92 | 93 | a { 94 | color: var(--color-main); 95 | 96 | &:hover, 97 | a:active { 98 | color: var(--color-white); 99 | } 100 | } 101 | } 102 | 103 | &.unsafe { 104 | background-color: var(--color-accent-dark); 105 | border: var(--space-border) solid var(--color-accent); 106 | 107 | a { 108 | color: var(--color-accent); 109 | 110 | &:hover, 111 | a:active { 112 | color: var(--color-white); 113 | } 114 | } 115 | } 116 | 117 | &.caution { 118 | background-color: var(--color-accent-secondary-dark); 119 | border: var(--space-border) solid var(--color-accent-secondary); 120 | 121 | a { 122 | color: var(--color-accent-secondary); 123 | 124 | &:hover, 125 | a:active { 126 | color: var(--color-white); 127 | } 128 | } 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dog Safe Foods", 3 | "short_name": "Dog Safe Foods", 4 | "description": "Sharing food with your dog? Make sure it's safe first", 5 | "display": "standalone", 6 | "start_url": "./", 7 | "scope": ".", 8 | "theme_color": "#000", 9 | "background_color": "#000", 10 | "orientation": "portrait-primary", 11 | "icons": [ 12 | { 13 | "src": "img/android-chrome-36x36.png", 14 | "sizes": "36x36", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "img/android-chrome-48x48.png", 19 | "sizes": "48x48", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "img/android-chrome-72x72.png", 24 | "sizes": "72x72", 25 | "type": "image/png" 26 | }, 27 | { 28 | "src": "img/android-chrome-96x96.png", 29 | "sizes": "96x96", 30 | "type": "image/png" 31 | }, 32 | { 33 | "src": "img/android-chrome-144x144.png", 34 | "sizes": "144x144", 35 | "type": "image/png" 36 | }, 37 | { 38 | "src": "img/android-chrome-192x192.png", 39 | "sizes": "192x192", 40 | "type": "image/png" 41 | }, 42 | { 43 | "src": "img/android-chrome-256x256.png", 44 | "sizes": "256x256", 45 | "type": "image/png" 46 | }, 47 | { 48 | "src": "img/android-chrome-384x384.png", 49 | "sizes": "384x384", 50 | "type": "image/png" 51 | }, 52 | { 53 | "src": "img/android-chrome-512x512.png", 54 | "sizes": "512x512", 55 | "type": "image/png" 56 | }, 57 | { 58 | "src": "img/maskable_icon.png", 59 | "sizes": "650x650", 60 | "type": "image/png", 61 | "purpose": "maskable" 62 | }, 63 | { 64 | "src": "img/monochrome_icon.png", 65 | "sizes": "512x512", 66 | "type": "image/png", 67 | "purpose": "monochrome" 68 | } 69 | ], 70 | "categories": [ 71 | "pets", 72 | "food" 73 | ], 74 | "screenshots": [ 75 | { 76 | "src": "img/screenshot1.png", 77 | "sizes": "1500x800", 78 | "type": "image/png" 79 | }, 80 | { 81 | "src": "img/screenshot2.png", 82 | "sizes": "1500x800", 83 | "type": "image/png" 84 | }, 85 | { 86 | "src": "img/screenshot3.png", 87 | "sizes": "1500x800", 88 | "type": "image/png" 89 | } 90 | ], 91 | "shortcuts": [ 92 | { 93 | "name": "View Safe Foods", 94 | "description": "View all foods that are safe for dogs to eat", 95 | "url": "./safe", 96 | "icons": [ 97 | { 98 | "src": "img/icon-thumbs-up.png", 99 | "sizes": "512x512" 100 | } 101 | ] 102 | }, 103 | { 104 | "name": "View Unsafe Foods", 105 | "description": "View all foods that are not safe for dogs to eat", 106 | "url": "./unsafe", 107 | "icons": [ 108 | { 109 | "src": "img/icon-thumbs-down.png", 110 | "sizes": "512x512" 111 | } 112 | ] 113 | } 114 | ], 115 | "related_applications": [ 116 | { 117 | "platform": "webapp", 118 | "url": "https://dogsafefoods.com/manifest.json" 119 | }, 120 | { 121 | "platform": "play", 122 | "url": "https://play.google.com/store/apps/details?id=com.dogsafefoods.twa", 123 | "id": "com.dogsafefoods.twa" 124 | } 125 | ], 126 | "prefer_related_applications": true 127 | } -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 |

Tips

14 | When sharing food with dogs, it is important to keep a few things in mind: 15 | 26 |
-------------------------------------------------------------------------------- /js/urls.ts: -------------------------------------------------------------------------------- 1 | import { showNotification } from "./notifications"; 2 | 3 | declare global { 4 | interface Window { 5 | baseUrl: string; 6 | baseTitle: string; 7 | baseDescription: string; 8 | gtag: any; 9 | } 10 | } 11 | 12 | /** 13 | * Copies provided URL to clipboard if navigator.clipboard is available, otherwise displays a notification with the URL. 14 | * @param {Object} url The `copyUrl` function requires an object with the following properties: 15 | * @param {string=} url.url Url to copy. 16 | */ 17 | export const copyUrl = ({ url = window.location.href } : { url?: string }): void => { 18 | if (!url) { 19 | return; 20 | } 21 | 22 | if (navigator.clipboard) { 23 | navigator.clipboard.writeText(url) 24 | .then(() => { 25 | showNotification({ text: `Success! 🥳 URL copied to clipboard: ${url}` }); 26 | }) 27 | .catch(() => { 28 | showNotification({ text: `Copy URL: ${url}` }); 29 | }); 30 | 31 | } else { 32 | showNotification({ text: `Copy URL: ${url}` }); 33 | } 34 | 35 | // Send copy event to GA. 36 | window.gtag('event', 'select_content', { 37 | item_id: url, 38 | }); 39 | } 40 | 41 | /** 42 | * Displays the native sharing mechanism for the device if navigator.share is available, otherwise displays a notification with the share data. 43 | * @param {Object} url The `shareUrl` function requires an object with the following properties: 44 | * @param {string=} url.url URL to share. Defaults to window.location.href. 45 | * @param {string=} url.title Title of website. Defaults to window.baseTitle. 46 | * @param {string=} url.text Text to share. Defaults to window.baseDescription. 47 | */ 48 | export const shareUrl = ({ url = window.location.href, title = window.baseTitle, text = window.baseDescription } : { url?: string, title?: string, text?: string }): void => { 49 | if (navigator.share) { 50 | navigator.share({ title, url }).catch(error => console.warn(error)); 51 | } else { 52 | const textEncoded = encodeURIComponent(text + ' ' + url); 53 | showNotification({ 54 | text: `  Facebook` + 55 | `  Twitter` + 56 | `  Email` + 57 | `  Copy`, 58 | cookieName: '', 59 | className: 'notification-share' 60 | }); 61 | } 62 | 63 | // Send share event to GA. 64 | window.gtag('event', 'share', { 65 | content_id: url, 66 | }); 67 | } 68 | 69 | /** 70 | * Adds event listeners to share and copy links. 71 | */ 72 | export const setLinkEventListeners = (): void => { 73 | 74 | const shareLinks = document.querySelectorAll('.share'); 75 | 76 | if (shareLinks) { 77 | shareLinks.forEach(link => { 78 | link.addEventListener('click', event => { 79 | event.preventDefault(); 80 | 81 | const targetElement = event.target; 82 | 83 | if (!targetElement) { 84 | return; 85 | } 86 | 87 | let element = targetElement.closest('.share'); 88 | 89 | if (!element) { 90 | return; 91 | } 92 | 93 | shareUrl({ 94 | url: element.dataset.url ?? window.baseUrl, 95 | title: element.dataset.title ?? window.baseTitle, 96 | text: element.dataset.text ?? window.baseDescription 97 | }); 98 | }); 99 | }); 100 | } 101 | 102 | const copyLinks = document.querySelectorAll('.copy'); 103 | 104 | if (copyLinks) { 105 | copyLinks.forEach(link => { 106 | link.addEventListener('click', event => { 107 | event.preventDefault(); 108 | 109 | const targetElement = event.target; 110 | 111 | if (!targetElement) { 112 | return; 113 | } 114 | 115 | let element = targetElement.closest('.copy'); 116 | 117 | if (!element) { 118 | return; 119 | } 120 | 121 | copyUrl({ url: element.dataset.url ?? window.baseUrl }); 122 | }); 123 | }); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /img/logo_header.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /js/notifications.ts: -------------------------------------------------------------------------------- 1 | import { setCookie, getCookie } from "./cookies"; 2 | import { setLinkEventListeners } from "./urls"; 3 | 4 | /** 5 | * Show notification. 6 | * 7 | * @param {Object} notification The `showNotification` function requires an object with the following properties: 8 | * @param {string} notification.text Text to display in notification. Also accepts 'paypal-confirmation' to display PayPal confirmation text, or 'cookie' to display cookie consent text. 9 | * @param {string=} notification.cookieName Cookie name to set once notification is closed so that the notification does not display again for the user. Defaults to ''. 10 | * @param {string=} notification.className Class name to apply to notification element. Defaults to ''. 11 | */ 12 | export const showNotification = ({ text, cookieName = '', className = '' } : { text: string, cookieName?: string, className?: string }): void => { 13 | 14 | // Don't display notification if cookie is set. 15 | if (cookieName && getCookie({name: cookieName})) { 16 | return; 17 | } 18 | 19 | new Promise((resolve, reject) => { 20 | 21 | const notificationContainer = document.querySelector('.notification'); 22 | const notificationTextContainer = document.querySelector('.notification p'); 23 | const notificationCloseContainer = document.querySelector('.notification .close'); 24 | 25 | if (!notificationContainer) { 26 | reject('.notification element not found'); 27 | } 28 | 29 | // Close any open notifications. 30 | closeNotification(); 31 | 32 | // Set notification text. 33 | if (notificationTextContainer) { 34 | if (text === 'paypal-confirmation') { 35 | notificationTextContainer.innerHTML = `Transaction approved! Thank you so much! `; 36 | } else if (text === 'cookie') { 37 | notificationTextContainer.innerHTML = `Cookies and other tracking technologies are used on this website to improve your browsing experience, analyze website traffic, and show personalized content and targeted ads. By browsing this website, you consent to the use of cookies and other tracking technologies.`; 38 | } else { 39 | notificationTextContainer.innerHTML = text; 40 | } 41 | } 42 | 43 | // Set notification class. 44 | if (className) { 45 | notificationContainer.setAttribute('data-class', className); 46 | notificationContainer.classList.add(className); 47 | } 48 | 49 | // Set cookie data attribute, which will cause the cookie to get set when the notification is closed, so that it doesn't display again for the user. 50 | if (cookieName) { 51 | notificationContainer.setAttribute('data-cookie', cookieName); 52 | } 53 | 54 | // Set close functionality. 55 | if (notificationCloseContainer) { 56 | notificationCloseContainer.addEventListener('click', () => { 57 | closeNotification(); 58 | }); 59 | } 60 | 61 | // Display notification. 62 | notificationContainer.show(); 63 | notificationContainer.setAttribute('aria-hidden', 'false'); 64 | 65 | // Resolve promise. 66 | resolve(true); 67 | }) 68 | .then((result) => { 69 | if (result) { 70 | // Set event listeners for any event links in notification. 71 | setLinkEventListeners(); 72 | } 73 | }) 74 | .catch((error) => { 75 | console.error(error); 76 | }); 77 | } 78 | 79 | /** 80 | * Close notification. 81 | */ 82 | export const closeNotification = (): void => { 83 | 84 | const notificationContainer = document.querySelector('.notification'); 85 | 86 | if (!notificationContainer) { 87 | return; 88 | } 89 | 90 | notificationContainer.close(); 91 | notificationContainer.setAttribute('aria-hidden', 'true'); 92 | 93 | // If notification has class data attribute set, remove class after closing. 94 | if (notificationContainer.dataset.class) { 95 | notificationContainer.classList.remove(notificationContainer.dataset.class); 96 | notificationContainer.removeAttribute('data-class'); 97 | } 98 | 99 | // If notification has cookie data attribute set, set cookie after closing. 100 | if (notificationContainer.dataset.cookie) { 101 | setCookie({ name: notificationContainer.dataset.cookie, value: true }); 102 | notificationContainer.removeAttribute('data-cookie'); 103 | } 104 | } 105 | 106 | /** 107 | * Show promo notification. 108 | * @param {Object} notification The `showPromo` function requires an object with the following properties: 109 | * @param {string} notification.text Text to display in notification. 110 | * @param {string=} notification.cookieName Cookie name to provide to showNotification(), so that the promo does not display again for the user. Defaults to the website URL with '-promo' appended. 111 | * @param {string=} notification.customIconSelector Selector for the promo icon, if one exists. If provided, the icon will be animated and a cookie will be set on click so that it only animates once. 112 | */ 113 | export const showPromo = ({ text, cookieName = `${window.baseUrl}-promo`, customIconSelector = 'nav .custom' }: { text: string, cookieName?: string, customIconSelector?: string }): void => { 114 | 115 | if (!text) { 116 | return; 117 | } 118 | 119 | // Animate custom icon and set click event listener so that it only animates once. 120 | if (customIconSelector) { 121 | setTimeout(() => { 122 | const element = document.querySelector(customIconSelector); 123 | 124 | if (!element) { 125 | return; 126 | } 127 | 128 | element.classList.add('animate'); 129 | element.addEventListener('click', () => { 130 | setCookie({ name: `${cookieName}-icon`, value: true }); 131 | }); 132 | }, 20000); 133 | } 134 | 135 | // Show the notification for the promo. 136 | setTimeout(() => { 137 | showNotification({ text, cookieName }); 138 | }, 60000); 139 | } 140 | -------------------------------------------------------------------------------- /img/logo_nav.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /js/main.ts: -------------------------------------------------------------------------------- 1 | import normalizeText from './normalizeText'; 2 | import { setLinkEventListeners } from './urls'; 3 | import { showPromo } from './notifications'; 4 | 5 | declare global { 6 | interface Window { 7 | baseUrl: string; 8 | baseTitle: string; 9 | baseDescription: string; 10 | gtag: any; 11 | } 12 | } 13 | 14 | type SearchResult = { 15 | title: Array; 16 | edible: string; 17 | processed: boolean; 18 | dcm: boolean; 19 | highfat: boolean; 20 | canned: boolean; 21 | content: string; 22 | links: Array; 23 | } 24 | 25 | window.addEventListener('load', async () => { 26 | 27 | const searchInput = document.querySelector(`.search input[name='search']`); 28 | 29 | if (!searchInput) { 30 | return; 31 | } 32 | 33 | // Add event listener for search input. 34 | searchInput.addEventListener('input', async (event) => { 35 | event.preventDefault(); 36 | 37 | const targetElement = event.target; 38 | 39 | const query = targetElement?.value?.toLowerCase() ?? ''; 40 | 41 | // Replace spaces with dashes in query text and change URL path. 42 | window.history.pushState(null, null, query ? normalizeText({ text: query, type: 'url' }) : '/'); 43 | 44 | // Get search results. 45 | const results = await getSearchResults({ query }); 46 | 47 | // Display search results. 48 | displayResults({ query, results }); 49 | }); 50 | 51 | // If URL path is provided, override search field contents. 52 | if (window.location.pathname) { 53 | // Remove file extension and slashes from query text and replace dashes with a space. 54 | const query = normalizeText({ text: window.location.pathname.replace('.html', '').replace('/', '').toLowerCase() }); 55 | 56 | // Change search input value to query text. 57 | searchInput.value = query; 58 | 59 | // Get search results. 60 | const results = await getSearchResults({ query }); 61 | 62 | // Display search results. 63 | displayResults({ query, results }); 64 | } 65 | 66 | // Show catsafefoods.com promo. 67 | showPromo({ 68 | text: `Sharing food with your cat, too? Check out catsafefoods.com!`, 69 | cookieName: 'catsafefoods.com-promo', 70 | }); 71 | }); 72 | 73 | /** 74 | * Get search results. 75 | * 76 | * @param {Object} getSearchResultsData Data for the search results to get. 77 | * @param {string} getSearchResultsData.query Query string. 78 | * @returns {Promise>} Search results. 79 | */ 80 | const getSearchResults = async ({ 81 | query, 82 | }: { 83 | query: string, 84 | }): Promise> => { 85 | 86 | if (!query) { 87 | return []; 88 | } 89 | 90 | // Send search event to GA 91 | window.gtag('event', 'search', { 92 | search_term: query 93 | }); 94 | 95 | const response = await fetch('data.json'); 96 | 97 | if (!response.ok) { 98 | console.warn(response.status, response.statusText); 99 | return []; 100 | } 101 | 102 | const results: Array = await response.json(); 103 | 104 | const matches = results.filter(result => { 105 | // @todo: adjust this logic. 106 | if (query == 'safe' || query == 'caution' || query == 'unsafe') { 107 | return result.edible.toLowerCase() == query; 108 | } else if (query.length < 4) { 109 | return result.title.some(title => normalizeText({ text: title }).startsWith(query)); 110 | } else { 111 | return result.title.some(title => normalizeText({ text: title }).includes(query)); 112 | } 113 | }); 114 | 115 | return matches; 116 | } 117 | 118 | /** 119 | * Display search results. 120 | * 121 | * @param {Object} displayResultsData Data for the search results to display. 122 | * @param {string} displayResultsData.query Query string. 123 | * @param {Array} displayResultsData.results Search results. Defaults to empty array. 124 | */ 125 | const displayResults = ({ 126 | query, 127 | results = [], 128 | }: { 129 | query: string, 130 | results?: Array, 131 | }): void => { 132 | 133 | const searchNumResultsContainer = document.querySelector(`.search .num-results`); 134 | const searchResultsContainer = document.querySelector(`.search .results`); 135 | 136 | if (!searchNumResultsContainer || !searchResultsContainer) { 137 | return; 138 | } 139 | 140 | const iconSafe = `👍`; 141 | const iconUnsafe = `👎`; 142 | const iconCaution = ``; 143 | 144 | if (!results.length) { 145 | searchNumResultsContainer.innerHTML = query 146 | ? `No Results 🤷‍♀️` 147 | : ''; 148 | searchResultsContainer.innerHTML = query 149 | ? `
150 |

This food is not yet in our database. We add foods regularly, so check back soon!

151 |

Check with your veterinarian before sharing any food with dogs—especially those not found in our database.

152 |
` 153 | : ''; 154 | } else { 155 | searchNumResultsContainer.innerHTML = results.length + results.length ? ' Results' : ' Result'; 156 | searchResultsContainer.innerHTML = ''; 157 | 158 | // Populate search results. Each result has a share button, so wait for all search results to be populated, then set event listeners for the share icons in the search results. 159 | Promise.all(results.map(result => { 160 | 161 | return new Promise((resolve) => { 162 | 163 | /** 164 | * If a result has a title array 3 items long or less, display a comma separated list of the array items as the title, and no subtitle. 165 | * If a result has a title array 3 items long or more, display the first array item as the title, and the rest as a subtitle. 166 | */ 167 | const title = result.title.length <= 3 ? result.title.join(', ') : result.title[0]; 168 | const subtitle = result.title.length <= 3 ? '' : result.title.slice(1).join(', ') + ', etc.'; 169 | 170 | // Set up the URL and text for sharing. 171 | const shareUrl = window.baseUrl + '/' + normalizeText({ text: result.title[0], type: 'url' }); 172 | const shareText = result.content.replace(/(|<\/strong>)/g, ''); 173 | 174 | searchResultsContainer.innerHTML += 175 | `
176 | 177 |
178 |

179 | ${result.edible == 'safe' ? iconSafe 180 | : result.edible == 'unsafe' ? iconUnsafe 181 | : iconCaution} 182 | ${title} 183 |

184 | ${subtitle ? '

' + subtitle + '

' : ''} 185 |

${result.content}

186 | ${result.processed ? "

Caution: Processed foods are unhealthy for dogs and can even conceal toxic ingredients. Read more.

" : ''} 187 | ${result.dcm ? "

Caution: Recent research has suggested that several foods, such as peas, legumes, and potatoes, while not toxic, may not be appropriate to be fed to dogs regularly. Read more.

" : ''} 188 | ${result.highfat ? "

Caution: Foods that are high in fat can cause dogs to get gastrointestinal upset and even pancreatitis. Read more.

" : ''} 189 | ${result.canned ? "

Caution: Dogs should avoid canned versions of foods due to their high sodium and/or sugar content, and opt for the fresh or frozen version instead. Read more.

" : ''} 190 |
191 |
`; 192 | 193 | resolve(true); 194 | }); 195 | })).then(() => { 196 | // Set event listeners for share icons in results. 197 | setLinkEventListeners(); 198 | }); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /_redirects: -------------------------------------------------------------------------------- 1 | /safe/* /safe.html 200 2 | /unsafe/* /unsafe.html 200 3 | /disclaimer/* /disclaimer.html 200 4 | /privacy-policy/* /privacy-policy.html 200 5 | /terms-and-conditions/* /terms-and-conditions.html 200 6 | /acai/* /acai.html 200 7 | /alcohol/* /alcohol.html 200 8 | /beer/* /beer.html 200 9 | /champagne/* /champagne.html 200 10 | /gin/* /gin.html 200 11 | /liquor/* /liquor.html 200 12 | /rum/* /rum.html 200 13 | /vodka/* /vodka.html 200 14 | /whiskey/* /whiskey.html 200 15 | /wine/* /wine.html 200 16 | /apples/* /apples.html 200 17 | /apricots/* /apricots.html 200 18 | /artichokes/* /artichokes.html 200 19 | /asparagus/* /asparagus.html 200 20 | /aspartame/* /aspartame.html 200 21 | /avocados/* /avocados.html 200 22 | /bananas/* /bananas.html 200 23 | /beans/* /beans.html 200 24 | /black-beans/* /black-beans.html 200 25 | /chickpeas/* /chickpeas.html 200 26 | /edamame/* /edamame.html 200 27 | /kidney-beans/* /kidney-beans.html 200 28 | /lentils/* /lentils.html 200 29 | /pinto-beans/* /pinto-beans.html 200 30 | /soybeans/* /soybeans.html 200 31 | /beets/* /beets.html 200 32 | /bell-peppers/* /bell-peppers.html 200 33 | /blackberries/* /blackberries.html 200 34 | /blueberries/* /blueberries.html 200 35 | /bread/* /bread.html 200 36 | /bagels/* /bagels.html 200 37 | /croissants/* /croissants.html 200 38 | /english-muffins/* /english-muffins.html 200 39 | /pita-bread/* /pita-bread.html 200 40 | /pretzels/* /pretzels.html 200 41 | /rolls/* /rolls.html 200 42 | /broccoli/* /broccoli.html 200 43 | /brussels-sprouts/* /brussels-sprouts.html 200 44 | /butter/* /butter.html 200 45 | /margarine/* /margarine.html 200 46 | /cabbage/* /cabbage.html 200 47 | /cantaloupe/* /cantaloupe.html 200 48 | /melon/* /melon.html 200 49 | /honeydew-melon/* /honeydew-melon.html 200 50 | /carrots/* /carrots.html 200 51 | /cat-food/* /cat-food.html 200 52 | /celery/* /celery.html 200 53 | /cheese/* /cheese.html 200 54 | /asiago-cheese/* /asiago-cheese.html 200 55 | /blue-cheese/* /blue-cheese.html 200 56 | /brie-cheese/* /brie-cheese.html 200 57 | /cheddar-cheese/* /cheddar-cheese.html 200 58 | /colby-jack-cheese/* /colby-jack-cheese.html 200 59 | /cottage-cheese/* /cottage-cheese.html 200 60 | /cream-cheese/* /cream-cheese.html 200 61 | /feta-cheese/* /feta-cheese.html 200 62 | /gorgonzola-cheese/* /gorgonzola-cheese.html 200 63 | /mascarpone-cheese/* /mascarpone-cheese.html 200 64 | /mozzarella-cheese/* /mozzarella-cheese.html 200 65 | /parmesan-cheese/* /parmesan-cheese.html 200 66 | /pepper-jack-cheese/* /pepper-jack-cheese.html 200 67 | /ricotta-cheese/* /ricotta-cheese.html 200 68 | /swiss-cheese/* /swiss-cheese.html 200 69 | /cherries/* /cherries.html 200 70 | /chicken-meat/* /chicken-meat.html 200 71 | /poultry/* /poultry.html 200 72 | /chocolate/* /chocolate.html 200 73 | /cocoa/* /cocoa.html 200 74 | /coconut/* /coconut.html 200 75 | /coconut-oil/* /coconut-oil.html 200 76 | /coffee/* /coffee.html 200 77 | /tea/* /tea.html 200 78 | /corn/* /corn.html 200 79 | /cranberries/* /cranberries.html 200 80 | /cucumbers/* /cucumbers.html 200 81 | /dairy/* /dairy.html 200 82 | /dragon-fruit/* /dragon-fruit.html 200 83 | /pitaya/* /pitaya.html 200 84 | /eggplant/* /eggplant.html 200 85 | /eggs/* /eggs.html 200 86 | /figs/* /figs.html 200 87 | /fish/* /fish.html 200 88 | /salmon/* /salmon.html 200 89 | /whitefish/* /whitefish.html 200 90 | /herring/* /herring.html 200 91 | /walleye/* /walleye.html 200 92 | /flounder/* /flounder.html 200 93 | /arctic-char/* /arctic-char.html 200 94 | /sardines/* /sardines.html 200 95 | /fried-foods/* /fried-foods.html 200 96 | /chicken-fingers/* /chicken-fingers.html 200 97 | /chicken-nuggets/* /chicken-nuggets.html 200 98 | /chips/* /chips.html 200 99 | /clam-cakes/* /clam-cakes.html 200 100 | /corn-dogs/* /corn-dogs.html 200 101 | /crab-rangoons/* /crab-rangoons.html 200 102 | /donuts/* /donuts.html 200 103 | /egg-rolls/* /egg-rolls.html 200 104 | /falafel/* /falafel.html 200 105 | /fish-and-chips/* /fish-and-chips.html 200 106 | /french-fries/* /french-fries.html 200 107 | /hash-browns/* /hash-browns.html 200 108 | /home-fries/* /home-fries.html 200 109 | /mozzarella-sticks/* /mozzarella-sticks.html 200 110 | /onion-rings/* /onion-rings.html 200 111 | /potato-chips/* /potato-chips.html 200 112 | /spring-rolls/* /spring-rolls.html 200 113 | /tater-tots/* /tater-tots.html 200 114 | /tortilla-chips/* /tortilla-chips.html 200 115 | /garlic/* /garlic.html 200 116 | /gatorade/* /gatorade.html 200 117 | /grapes/* /grapes.html 200 118 | /raisins/* /raisins.html 200 119 | /green-beans/* /green-beans.html 200 120 | /string-beans/* /string-beans.html 200 121 | /guava/* /guava.html 200 122 | /honey/* /honey.html 200 123 | /hot-dogs/* /hot-dogs.html 200 124 | /ice-cream/* /ice-cream.html 200 125 | /frozen-yogurt/* /frozen-yogurt.html 200 126 | /ice-cubes/* /ice-cubes.html 200 127 | /hot-peppers/* /hot-peppers.html 200 128 | /chili-peppers/* /chili-peppers.html 200 129 | /jalapeno-peppers/* /jalapeno-peppers.html 200 130 | /serrano-peppers/* /serrano-peppers.html 200 131 | /kale/* /kale.html 200 132 | /kiwis/* /kiwis.html 200 133 | /citrus-fruits/* /citrus-fruits.html 200 134 | /lemons/* /lemons.html 200 135 | /limes/* /limes.html 200 136 | /oranges/* /oranges.html 200 137 | /clementines/* /clementines.html 200 138 | /grapefruit/* /grapefruit.html 200 139 | /kumquats/* /kumquats.html 200 140 | /persimmons/* /persimmons.html 200 141 | /tangerines/* /tangerines.html 200 142 | /greens/* /greens.html 200 143 | /arugula-greens/* /arugula-greens.html 200 144 | /green-leaf-lettuce/* /green-leaf-lettuce.html 200 145 | /iceberg-lettuce/* /iceberg-lettuce.html 200 146 | /lettuce-greens/* /lettuce-greens.html 200 147 | /red-leaf-lettuce/* /red-leaf-lettuce.html 200 148 | /romaine-lettuce/* /romaine-lettuce.html 200 149 | /mangoes/* /mangoes.html 200 150 | /marshmallows/* /marshmallows.html 200 151 | /red-meat/* /red-meat.html 200 152 | /beef/* /beef.html 200 153 | /steak/* /steak.html 200 154 | /bones/* /bones.html 200 155 | /beef-bones/* /beef-bones.html 200 156 | /chicken-bones/* /chicken-bones.html 200 157 | /ham-bones/* /ham-bones.html 200 158 | /lamb-bones/* /lamb-bones.html 200 159 | /meat-bones/* /meat-bones.html 200 160 | /pork-bones/* /pork-bones.html 200 161 | /pork-chop-bones/* /pork-chop-bones.html 200 162 | /steak-bones/* /steak-bones.html 200 163 | /turkey-bones/* /turkey-bones.html 200 164 | /milk/* /milk.html 200 165 | /mushrooms/* /mushrooms.html 200 166 | /champignon-mushrooms/* /champignon-mushrooms.html 200 167 | /chanterelle-mushrooms/* /chanterelle-mushrooms.html 200 168 | /cremini-mushrooms/* /cremini-mushrooms.html 200 169 | /maitake-mushrooms/* /maitake-mushrooms.html 200 170 | /morel-mushrooms/* /morel-mushrooms.html 200 171 | /oyster-mushrooms/* /oyster-mushrooms.html 200 172 | /porcini-mushrooms/* /porcini-mushrooms.html 200 173 | /portabella-mushrooms/* /portabella-mushrooms.html 200 174 | /shiitake-mushrooms/* /shiitake-mushrooms.html 200 175 | /nectarines/* /nectarines.html 200 176 | /nuts/* /nuts.html 200 177 | /almonds/* /almonds.html 200 178 | /brazil-nuts/* /brazil-nuts.html 200 179 | /cashews/* /cashews.html 200 180 | /chestnuts/* /chestnuts.html 200 181 | /hazelnuts/* /hazelnuts.html 200 182 | /macadamia-nuts/* /macadamia-nuts.html 200 183 | /pecans/* /pecans.html 200 184 | /pine-nuts/* /pine-nuts.html 200 185 | /pistachios/* /pistachios.html 200 186 | /walnuts/* /walnuts.html 200 187 | /oatmeal/* /oatmeal.html 200 188 | /olives/* /olives.html 200 189 | /onions/* /onions.html 200 190 | /chives/* /chives.html 200 191 | /cipollini-onions/* /cipollini-onions.html 200 192 | /green-onions/* /green-onions.html 200 193 | /leeks/* /leeks.html 200 194 | /pearl-onions/* /pearl-onions.html 200 195 | /red-onions/* /red-onions.html 200 196 | /scallions/* /scallions.html 200 197 | /shallots/* /shallots.html 200 198 | /sweet-onions/* /sweet-onions.html 200 199 | /vidalia-onions/* /vidalia-onions.html 200 200 | /white-onions/* /white-onions.html 200 201 | /yellow-onions/* /yellow-onions.html 200 202 | /pomegranates/* /pomegranates.html 200 203 | /peaches/* /peaches.html 200 204 | /peanut-butter/* /peanut-butter.html 200 205 | /peanuts/* /peanuts.html 200 206 | /pears/* /pears.html 200 207 | /peas/* /peas.html 200 208 | /green-peas/* /green-peas.html 200 209 | /snow-peas/* /snow-peas.html 200 210 | /sugar-snap-peas/* /sugar-snap-peas.html 200 211 | /pickles/* /pickles.html 200 212 | /pineapples/* /pineapples.html 200 213 | /plums/* /plums.html 200 214 | /prunes/* /prunes.html 200 215 | /popcorn/* /popcorn.html 200 216 | /pork/* /pork.html 200 217 | /ham/* /ham.html 200 218 | /sausage/* /sausage.html 200 219 | /bacon/* /bacon.html 200 220 | /salami/* /salami.html 200 221 | /ribs/* /ribs.html 200 222 | /potatoes/* /potatoes.html 200 223 | /pumpkin/* /pumpkin.html 200 224 | /quinoa/* /quinoa.html 200 225 | /radishes/* /radishes.html 200 226 | /raspberries/* /raspberries.html 200 227 | /black-raspberries/* /black-raspberries.html 200 228 | /rice/* /rice.html 200 229 | /shellfish/* /shellfish.html 200 230 | /shrimp/* /shrimp.html 200 231 | /crab/* /crab.html 200 232 | /lobster/* /lobster.html 200 233 | /clams/* /clams.html 200 234 | /mussels/* /mussels.html 200 235 | /oysters/* /oysters.html 200 236 | /scallops/* /scallops.html 200 237 | /spinach/* /spinach.html 200 238 | /squash/* /squash.html 200 239 | /acorn-squash/* /acorn-squash.html 200 240 | /butternut-squash/* /butternut-squash.html 200 241 | /spaghetti-squash/* /spaghetti-squash.html 200 242 | /summer-squash/* /summer-squash.html 200 243 | /winter-squash/* /winter-squash.html 200 244 | /yellow-squash/* /yellow-squash.html 200 245 | /strawberries/* /strawberries.html 200 246 | /sweet-potatoes/* /sweet-potatoes.html 200 247 | /tomatoes/* /tomatoes.html 200 248 | /tuna/* /tuna.html 200 249 | /swordfish/* /swordfish.html 200 250 | /turkey-meat/* /turkey-meat.html 200 251 | /watermelon/* /watermelon.html 200 252 | /xylitol/* /xylitol.html 200 253 | /yogurt/* /yogurt.html 200 254 | /zucchini/* /zucchini.html 200 255 | /healthy /safe 301! 256 | /unhealthy /unsafe 301! 257 | /healthy.html /safe 301! 258 | /unhealthy.html /unsafe 301! 259 | /* food=:food /:food 301! 260 | /* /index.html 200 -------------------------------------------------------------------------------- /scss/index.scss: -------------------------------------------------------------------------------- 1 | @use 'animations'; 2 | @use 'colors'; 3 | @use 'fonts'; 4 | @use 'images'; 5 | @use 'layout'; 6 | 7 | $media-tablet: 30em; 8 | $media-desktop: 48em; 9 | $media-desktop-large: 75em; 10 | 11 | /* 12 | ** Typography 13 | */ 14 | html, 15 | *, 16 | *::before, 17 | *::after { 18 | box-sizing: border-box; 19 | } 20 | 21 | html { 22 | font-size: 100%; 23 | } 24 | 25 | html, 26 | body { 27 | margin: 0; 28 | padding: 0; 29 | } 30 | 31 | body, 32 | .description { 33 | font-family: var(--font-body-stack); 34 | } 35 | 36 | body { 37 | @include fonts.font-size(var(--font-medium)); 38 | background-color: var(--color-gray-dark); 39 | color: var(--color-white); 40 | font-weight: var(--font-normal); 41 | line-height: 1.4; 42 | } 43 | 44 | header, 45 | h1, 46 | h2, 47 | h3, 48 | h4, 49 | .title { 50 | font-family: var(--font-heading-stack); 51 | 52 | } 53 | 54 | h1, 55 | h2, 56 | .title { 57 | line-height: 1.1; 58 | } 59 | 60 | h3, 61 | h4, 62 | .description { 63 | line-height: 1.2; 64 | } 65 | 66 | h1, 67 | h2, 68 | h3, 69 | h4, 70 | .title { 71 | color: var(--color-accent); 72 | } 73 | 74 | header, 75 | h1, 76 | h2, 77 | h3, 78 | h4, 79 | .title, 80 | .description, 81 | a, 82 | button, 83 | .button, 84 | input[type="button"], 85 | input[type="submit"], 86 | summary, 87 | dt { 88 | font-weight: var(--font-bold); 89 | } 90 | 91 | blockquote, 92 | body, 93 | dd, 94 | dl, 95 | figure, 96 | h1, 97 | h2, 98 | h3, 99 | h4, 100 | p { 101 | margin: 0; 102 | } 103 | 104 | h1, 105 | h2, 106 | h3, 107 | h4, 108 | p, 109 | dd { 110 | margin: var(--space-small) auto; 111 | } 112 | 113 | dl, 114 | ol, 115 | ul { 116 | dt, 117 | dd, 118 | li { 119 | margin: var(--space-small) auto; 120 | } 121 | } 122 | 123 | h1, 124 | .title { 125 | @include fonts.font-size(var(--font-xsuper)); 126 | } 127 | 128 | h2 { 129 | @include fonts.font-size(var(--font-super)); 130 | } 131 | 132 | h3, 133 | .description { 134 | @include fonts.font-size(var(--font-xlarge)); 135 | } 136 | 137 | h4 { 138 | @include fonts.font-size(var(--font-large)); 139 | } 140 | 141 | .social, 142 | button, 143 | .button, 144 | input[type="button"], 145 | input[type="submit"], 146 | textarea, 147 | select { 148 | font-size: var(--font-large); 149 | } 150 | 151 | code, 152 | kbd, 153 | samp, 154 | pre, 155 | .code { 156 | font-family: monospace, monospace; 157 | } 158 | 159 | .bold { 160 | font-weight: var(--font-bold); 161 | } 162 | 163 | .italic { 164 | font-style: italic; 165 | } 166 | 167 | .underline { 168 | text-decoration: underline; 169 | } 170 | 171 | mark::before, 172 | mark::after { 173 | clip-path: inset(100%); 174 | clip: rect(1px, 1px, 1px, 1px); 175 | height: 1px; 176 | overflow: hidden; 177 | position: absolute; 178 | white-space: nowrap; 179 | width: 1px; 180 | } 181 | 182 | mark::before { 183 | content: " [highlight start] "; 184 | } 185 | 186 | mark::after { 187 | content: " [highlight end] "; 188 | } 189 | 190 | .icon, 191 | span[class*="icon"] { 192 | vertical-align: bottom; 193 | } 194 | 195 | .icon-heart { 196 | color: var(--color-accent); 197 | } 198 | 199 | /* 200 | ** Layout 201 | */ 202 | article, 203 | aside, 204 | footer, 205 | header, 206 | nav, 207 | section { 208 | display: block; 209 | } 210 | 211 | .container-scrollable { 212 | align-self: normal; 213 | display: flex; 214 | flex-wrap: nowrap; 215 | gap: 0 var(--space-xsmall); 216 | justify-content: space-between; 217 | list-style: none; 218 | overflow-x: auto; 219 | padding: inherit; 220 | padding-bottom: 0; 221 | 222 | @media (min-width: layout.$media-desktop) { 223 | padding: 0; 224 | } 225 | 226 | a { 227 | margin: 0 var(--space-xsmall); 228 | 229 | &:first-of-type { 230 | margin-left: 0; 231 | } 232 | 233 | &:last-of-type { 234 | margin-right: 0; 235 | } 236 | } 237 | 238 | &::-webkit-scrollbar { 239 | height: 1rem; 240 | } 241 | 242 | &::-webkit-scrollbar-thumb { 243 | background: var(--color-accent-dark); 244 | } 245 | 246 | &::-webkit-scrollbar-track { 247 | background: var(--color-accent); 248 | } 249 | } 250 | 251 | .container { 252 | margin: 0 auto; 253 | width: min(700px, 100%); 254 | 255 | @media (min-width: layout.$media-desktop-large) { 256 | width: min(1000px, 100%); 257 | } 258 | } 259 | 260 | .container-text { 261 | margin: 0 auto; 262 | padding: var(--space-super); 263 | width: min(55ch, 100%); 264 | } 265 | 266 | .container-nav { 267 | background-color: rgba(0, 0, 0, .85); 268 | @include layout.padding-default(0); 269 | 270 | &:first-of-type { 271 | top: 0; 272 | position: sticky; 273 | z-index: 999; 274 | } 275 | 276 | &:last-of-type { 277 | .nav { 278 | @media (max-width: layout.$media-tablet) { 279 | display: none; 280 | } 281 | } 282 | } 283 | 284 | nav { 285 | display: flex; 286 | flex-direction: column; 287 | justify-content: center; 288 | align-items: center; 289 | 290 | @media (min-width: layout.$media-desktop) { 291 | display: grid; 292 | grid-template-columns: minmax(250px, 25%) 1fr; 293 | align-items: center; 294 | } 295 | 296 | &:has(.nav) { 297 | grid-template-columns: minmax(250px, 25%) 1fr 1fr; 298 | } 299 | 300 | a { 301 | padding: var(--space-xsmall); 302 | } 303 | 304 | .logo { 305 | margin: var(--space-xsmall) auto; 306 | padding: var(--space-small) var(--space-xsmall) 0; 307 | 308 | img { 309 | width: 250px; 310 | } 311 | 312 | @media (min-width: layout.$media-desktop) { 313 | margin: 0 auto; 314 | padding: 0; 315 | 316 | img { 317 | width: 100%; 318 | } 319 | } 320 | } 321 | 322 | .nav { 323 | align-items: center; 324 | display: flex; 325 | gap: 10px; 326 | list-style: none; 327 | margin: 0; 328 | padding: 0; 329 | 330 | li { 331 | margin: 0; 332 | } 333 | } 334 | 335 | .social { 336 | @extend .container-scrollable; 337 | justify-content: center; 338 | 339 | @media (min-width: layout.$media-desktop) { 340 | align-items: center; 341 | justify-content: flex-end; 342 | margin: 0; 343 | } 344 | 345 | a { 346 | &:first-of-type { 347 | @media (max-width: layout.$media-desktop) { 348 | margin-left: 0; 349 | } 350 | } 351 | 352 | &.custom.animate { 353 | animation: pulse 1s 0s forwards 3; 354 | } 355 | } 356 | } 357 | } 358 | } 359 | 360 | header { 361 | background: var(--color-black) no-repeat center/cover; 362 | display: flex; 363 | flex-direction: column; 364 | min-height: 25vh; 365 | place-content: center; 366 | 367 | .description { 368 | margin: 0 auto; 369 | } 370 | 371 | &.image-logo { 372 | align-items: center; 373 | justify-content: flex-end; 374 | @include layout.padding-default(var(--space-large)); 375 | 376 | .logo + .text { 377 | margin-top: var(--space-large); 378 | } 379 | 380 | .logo { 381 | img { 382 | max-height: 32vh; 383 | width: 100%; 384 | 385 | @media (min-width: layout.$media-desktop-large) { 386 | max-height: 30vh; 387 | } 388 | } 389 | } 390 | 391 | .text { 392 | text-align: center; 393 | 394 | address, 395 | time { 396 | display: none; 397 | } 398 | } 399 | } 400 | 401 | &.image-background { 402 | align-items: flex-start; 403 | justify-content: center; 404 | @include layout.padding-default(var(--space-xxsuper)); 405 | 406 | .text { 407 | @media (min-width: layout.$media-tablet) { 408 | max-width: 65%; 409 | } 410 | 411 | @media (min-width: layout.$media-desktop) { 412 | max-width: 52.5%; 413 | } 414 | 415 | hgroup { 416 | h1 { 417 | margin: 0 auto var(--space-small); 418 | } 419 | 420 | .description { 421 | color: var(--color-white); 422 | } 423 | } 424 | 425 | address { 426 | margin-top: var(--space-super); 427 | } 428 | } 429 | } 430 | } 431 | 432 | .template { 433 | header { 434 | .description { 435 | display: none; 436 | } 437 | } 438 | } 439 | 440 | main { 441 | margin: var(--space-large) auto; 442 | @include layout.padding-default(var(--space-super)); 443 | 444 | section { 445 | padding-bottom: var(--space-super); 446 | 447 | &:last-of-type { 448 | padding-bottom: 0; 449 | } 450 | 451 | &:only-of-type { 452 | padding-bottom: var(--space-super); 453 | } 454 | } 455 | } 456 | 457 | .template, 458 | .post { 459 | main { 460 | .container { 461 | background-color: var(--color-black); 462 | color: var(--color-white); 463 | } 464 | } 465 | } 466 | 467 | footer { 468 | @include layout.padding-default(var(--space-small)); 469 | 470 | .container-text, 471 | .ad-disclaimer, 472 | .image-credit, 473 | .tech-credit, 474 | .links:not(.list) { 475 | font-size: var(--font-small); 476 | text-align: center; 477 | } 478 | 479 | .image-credit, 480 | .tech-credit { 481 | margin: var(--space-large) auto; 482 | } 483 | 484 | .container-text { 485 | padding: var(--space-large); 486 | 487 | @media (min-width: layout.$media-desktop) { 488 | background-color: var(--color-gray-dark); 489 | border: var(--space-border) solid var(--color-black); 490 | border-radius: var(--space-border); 491 | padding: var(--space-super); 492 | } 493 | } 494 | 495 | .links:not(.list) { 496 | a { 497 | display: block; 498 | margin: 0 auto; 499 | padding: var(--space-medium) 0; 500 | 501 | @media (min-width: layout.$media-tablet) { 502 | display: inline-block; 503 | margin: var(--space-xsmall); 504 | padding: var(--space-large); 505 | 506 | &:first-of-type { 507 | margin-left: 0; 508 | padding-left: 0; 509 | } 510 | 511 | &:last-of-type { 512 | margin-right: 0; 513 | padding-right: 0; 514 | } 515 | } 516 | } 517 | } 518 | } 519 | 520 | details { 521 | display: block; 522 | 523 | summary { 524 | margin: var(--space-small) auto; 525 | } 526 | } 527 | 528 | /* 529 | ** Links / Buttons / Forms 530 | */ 531 | .buttons, 532 | label { 533 | margin: var(--space-small) auto; 534 | } 535 | 536 | a { 537 | color: var(--color-main); 538 | position: relative; 539 | text-decoration: none; 540 | 541 | &:hover, 542 | &:active, 543 | &:focus { 544 | color: var(--color-accent); 545 | } 546 | } 547 | 548 | button, 549 | .button, 550 | input, 551 | select, 552 | textarea { 553 | border: none; 554 | border-radius: var(--space-border); 555 | vertical-align: middle; 556 | } 557 | 558 | .facebook, 559 | .button.facebook, 560 | button.facebook { 561 | background-color: var(--color-facebook); 562 | } 563 | 564 | .twitter, 565 | .button.twitter, 566 | button.twitter { 567 | background-color: var(--color-twitter); 568 | } 569 | 570 | .patreon, 571 | .button.patreon, 572 | button.patreon { 573 | background-color: var(--color-patreon); 574 | } 575 | 576 | .paypal, 577 | .button.paypal 578 | button.paypal { 579 | background-color: var(--color-paypal); 580 | } 581 | 582 | .android:not(.install), 583 | .button.android 584 | button.android { 585 | background-color: var(--color-android); 586 | } 587 | 588 | .apple:not(.install), 589 | .button.apple 590 | button.apple { 591 | background-color: var(--color-apple); 592 | } 593 | 594 | button, 595 | .button, 596 | input[type="button"], 597 | input[type="submit"] { 598 | background-color: var(--color-main-dark); 599 | color: var(--color-white); 600 | line-height: 1; 601 | padding: var(--space-small) var(--space-large); 602 | 603 | &[type=color] { 604 | padding: 0; 605 | } 606 | 607 | &::-webkit-search-cancel-button { 608 | cursor: pointer; 609 | } 610 | 611 | &:hover, 612 | &:active, 613 | &:focus { 614 | background-color: var(--color-accent-dark); 615 | color: var(--color-white); 616 | } 617 | 618 | &.disabled { 619 | &, 620 | &:active, 621 | &:hover, 622 | &:focus { 623 | background-color: var(--color-gray-dark); 624 | color: var(--color-white); 625 | cursor: not-allowed; 626 | pointer-events: none; 627 | } 628 | } 629 | } 630 | 631 | button, 632 | .button, 633 | details summary, 634 | .close, 635 | .copy, 636 | .install, 637 | .share { 638 | cursor: pointer; 639 | } 640 | 641 | input, 642 | textarea, 643 | select { 644 | background-color: var(--color-white); 645 | color: var(--color-gray-dark); 646 | font-size: inherit; 647 | padding: var(--space-small); 648 | } 649 | 650 | label { 651 | display: grid; 652 | align-content: center; 653 | align-items: center; 654 | justify-content: space-between; 655 | grid-template-columns: minmax(10em, 25%) 1fr; 656 | } 657 | 658 | /* 659 | ** Media 660 | */ 661 | img { 662 | background: transparent; 663 | border-style: none; 664 | height: auto; 665 | max-width: 100%; 666 | } 667 | 668 | /* 669 | ** Notifications 670 | */ 671 | .notification { 672 | background-color: var(--color-gray-med); 673 | bottom: 0; 674 | color: var(--color-white); 675 | display: none; 676 | left: 0; 677 | @include layout.padding-default(var(--space-small)); 678 | position: fixed; 679 | right: 0; 680 | width: 100%; 681 | z-index: 999; 682 | 683 | &[open] { 684 | display: block; 685 | } 686 | 687 | &.notification-share { 688 | p { 689 | @extend .container-scrollable; 690 | justify-content: normal; 691 | padding-bottom: var(--space-xsmall); 692 | 693 | a { 694 | display: flex; 695 | align-items: center; 696 | } 697 | } 698 | } 699 | 700 | p { 701 | margin: 0; 702 | margin-right: var(--space-large); 703 | } 704 | 705 | .container { 706 | display: grid; 707 | grid-template-columns: 1fr minmax(10px, 5%); 708 | } 709 | 710 | .url { 711 | user-select: all; 712 | } 713 | } 714 | 715 | /* 716 | ** Misc 717 | */ 718 | @keyframes pulse { 719 | 0% { 720 | color: var(--color-gray-med); 721 | } 722 | 723 | 17% { 724 | color: var(--color-main); 725 | } 726 | 727 | 35% { 728 | color: var(--color-main-dark); 729 | } 730 | 731 | 52% { 732 | color: var(--color-accent-dark); 733 | } 734 | 735 | 68% { 736 | color: var(--color-accent); 737 | } 738 | 739 | 86% { 740 | color: var(--color-gray-med); 741 | } 742 | } -------------------------------------------------------------------------------- /sw.js: -------------------------------------------------------------------------------- 1 | const cacheName = "cache73"; 2 | const cacheFiles = [ 3 | "/", 4 | "manifest.json", 5 | "data.json", 6 | "img/logo_header.svg", 7 | "img/logo_nav.svg", 8 | "img/icon-thumbs-up.png", 9 | "img/icon-thumbs-down.png", 10 | "ads.txt", 11 | "sellers.json", 12 | "safe.html", 13 | "unsafe.html", 14 | "index.html", 15 | "disclaimer.html", 16 | "privacy-policy.html", 17 | "terms-and-conditions.html", 18 | "acai.html", 19 | "alcohol.html", 20 | "beer.html", 21 | "champagne.html", 22 | "gin.html", 23 | "liquor.html", 24 | "rum.html", 25 | "vodka.html", 26 | "whiskey.html", 27 | "wine.html", 28 | "apples.html", 29 | "apricots.html", 30 | "artichokes.html", 31 | "asparagus.html", 32 | "aspartame.html", 33 | "avocados.html", 34 | "bananas.html", 35 | "beans.html", 36 | "black-beans.html", 37 | "chickpeas.html", 38 | "edamame.html", 39 | "kidney-beans.html", 40 | "lentils.html", 41 | "pinto-beans.html", 42 | "soybeans.html", 43 | "beets.html", 44 | "bell-peppers.html", 45 | "blackberries.html", 46 | "blueberries.html", 47 | "bread.html", 48 | "bagels.html", 49 | "croissants.html", 50 | "english-muffins.html", 51 | "pita-bread.html", 52 | "pretzels.html", 53 | "rolls.html", 54 | "broccoli.html", 55 | "brussels-sprouts.html", 56 | "butter.html", 57 | "margarine.html", 58 | "cabbage.html", 59 | "cantaloupe.html", 60 | "melon.html", 61 | "honeydew-melon.html", 62 | "carrots.html", 63 | "cat-food.html", 64 | "celery.html", 65 | "cheese.html", 66 | "asiago-cheese.html", 67 | "blue-cheese.html", 68 | "brie-cheese.html", 69 | "cheddar-cheese.html", 70 | "colby-jack-cheese.html", 71 | "cottage-cheese.html", 72 | "cream-cheese.html", 73 | "feta-cheese.html", 74 | "gorgonzola-cheese.html", 75 | "mascarpone-cheese.html", 76 | "mozzarella-cheese.html", 77 | "parmesan-cheese.html", 78 | "pepper-jack-cheese.html", 79 | "ricotta-cheese.html", 80 | "swiss-cheese.html", 81 | "cherries.html", 82 | "chicken-meat.html", 83 | "poultry.html", 84 | "chocolate.html", 85 | "cocoa.html", 86 | "coconut.html", 87 | "coconut-oil.html", 88 | "coffee.html", 89 | "tea.html", 90 | "corn.html", 91 | "cranberries.html", 92 | "cucumbers.html", 93 | "dairy.html", 94 | "dragon-fruit.html", 95 | "pitaya.html", 96 | "eggplant.html", 97 | "eggs.html", 98 | "figs.html", 99 | "fish.html", 100 | "salmon.html", 101 | "whitefish.html", 102 | "herring.html", 103 | "walleye.html", 104 | "flounder.html", 105 | "arctic-char.html", 106 | "sardines.html", 107 | "fried-foods.html", 108 | "chicken-fingers.html", 109 | "chicken-nuggets.html", 110 | "chips.html", 111 | "clam-cakes.html", 112 | "corn-dogs.html", 113 | "crab-rangoons.html", 114 | "donuts.html", 115 | "egg-rolls.html", 116 | "falafel.html", 117 | "fish-and-chips.html", 118 | "french-fries.html", 119 | "hash-browns.html", 120 | "home-fries.html", 121 | "mozzarella-sticks.html", 122 | "onion-rings.html", 123 | "potato-chips.html", 124 | "spring-rolls.html", 125 | "tater-tots.html", 126 | "tortilla-chips.html", 127 | "garlic.html", 128 | "gatorade.html", 129 | "grapes.html", 130 | "raisins.html", 131 | "green-beans.html", 132 | "string-beans.html", 133 | "guava.html", 134 | "honey.html", 135 | "hot-dogs.html", 136 | "ice-cream.html", 137 | "frozen-yogurt.html", 138 | "ice-cubes.html", 139 | "hot-peppers.html", 140 | "chili-peppers.html", 141 | "jalapeno-peppers.html", 142 | "serrano-peppers.html", 143 | "kale.html", 144 | "kiwis.html", 145 | "citrus-fruits.html", 146 | "lemons.html", 147 | "limes.html", 148 | "oranges.html", 149 | "clementines.html", 150 | "grapefruit.html", 151 | "kumquats.html", 152 | "persimmons.html", 153 | "tangerines.html", 154 | "greens.html", 155 | "arugula-greens.html", 156 | "green-leaf-lettuce.html", 157 | "iceberg-lettuce.html", 158 | "lettuce-greens.html", 159 | "red-leaf-lettuce.html", 160 | "romaine-lettuce.html", 161 | "mangoes.html", 162 | "marshmallows.html", 163 | "red-meat.html", 164 | "beef.html", 165 | "steak.html", 166 | "bones.html", 167 | "beef-bones.html", 168 | "chicken-bones.html", 169 | "ham-bones.html", 170 | "lamb-bones.html", 171 | "meat-bones.html", 172 | "pork-bones.html", 173 | "pork-chop-bones.html", 174 | "steak-bones.html", 175 | "turkey-bones.html", 176 | "milk.html", 177 | "mushrooms.html", 178 | "champignon-mushrooms.html", 179 | "chanterelle-mushrooms.html", 180 | "cremini-mushrooms.html", 181 | "maitake-mushrooms.html", 182 | "morel-mushrooms.html", 183 | "oyster-mushrooms.html", 184 | "porcini-mushrooms.html", 185 | "portabella-mushrooms.html", 186 | "shiitake-mushrooms.html", 187 | "nectarines.html", 188 | "nuts.html", 189 | "almonds.html", 190 | "brazil-nuts.html", 191 | "cashews.html", 192 | "chestnuts.html", 193 | "hazelnuts.html", 194 | "macadamia-nuts.html", 195 | "pecans.html", 196 | "pine-nuts.html", 197 | "pistachios.html", 198 | "walnuts.html", 199 | "oatmeal.html", 200 | "olives.html", 201 | "onions.html", 202 | "chives.html", 203 | "cipollini-onions.html", 204 | "green-onions.html", 205 | "leeks.html", 206 | "pearl-onions.html", 207 | "red-onions.html", 208 | "scallions.html", 209 | "shallots.html", 210 | "sweet-onions.html", 211 | "vidalia-onions.html", 212 | "white-onions.html", 213 | "yellow-onions.html", 214 | "pomegranates.html", 215 | "peaches.html", 216 | "peanut-butter.html", 217 | "peanuts.html", 218 | "pears.html", 219 | "peas.html", 220 | "green-peas.html", 221 | "snow-peas.html", 222 | "sugar-snap-peas.html", 223 | "pickles.html", 224 | "pineapples.html", 225 | "plums.html", 226 | "prunes.html", 227 | "popcorn.html", 228 | "pork.html", 229 | "ham.html", 230 | "sausage.html", 231 | "bacon.html", 232 | "salami.html", 233 | "ribs.html", 234 | "potatoes.html", 235 | "pumpkin.html", 236 | "quinoa.html", 237 | "radishes.html", 238 | "raspberries.html", 239 | "black-raspberries.html", 240 | "rice.html", 241 | "shellfish.html", 242 | "shrimp.html", 243 | "crab.html", 244 | "lobster.html", 245 | "clams.html", 246 | "mussels.html", 247 | "oysters.html", 248 | "scallops.html", 249 | "spinach.html", 250 | "squash.html", 251 | "acorn-squash.html", 252 | "butternut-squash.html", 253 | "spaghetti-squash.html", 254 | "summer-squash.html", 255 | "winter-squash.html", 256 | "yellow-squash.html", 257 | "strawberries.html", 258 | "sweet-potatoes.html", 259 | "tomatoes.html", 260 | "tuna.html", 261 | "swordfish.html", 262 | "turkey-meat.html", 263 | "watermelon.html", 264 | "xylitol.html", 265 | "yogurt.html", 266 | "zucchini.html", 267 | "fonts/roboto-slab/roboto-slab-700.eot", 268 | "fonts/roboto-slab/roboto-slab-700.svg", 269 | "fonts/roboto-slab/roboto-slab-700.ttf", 270 | "fonts/roboto-slab/roboto-slab-700.woff", 271 | "fonts/roboto-slab/roboto-slab-700.woff2", 272 | "fonts/roboto-slab/roboto-slab-regular.eot", 273 | "fonts/roboto-slab/roboto-slab-regular.svg", 274 | "fonts/roboto-slab/roboto-slab-regular.ttf", 275 | "fonts/roboto-slab/roboto-slab-regular.woff", 276 | "fonts/roboto-slab/roboto-slab-regular.woff2", 277 | "fonts/roboto/roboto-700.eot", 278 | "fonts/roboto/roboto-700.svg", 279 | "fonts/roboto/roboto-700.ttf", 280 | "fonts/roboto/roboto-700.woff", 281 | "fonts/roboto/roboto-700.woff2", 282 | "fonts/roboto/roboto-regular.eot", 283 | "fonts/roboto/roboto-regular.svg", 284 | "fonts/roboto/roboto-regular.ttf", 285 | "fonts/roboto/roboto-regular.woff", 286 | "fonts/roboto/roboto-regular.woff2", 287 | "fonts/fontawesome/css/brands.min.css", 288 | "fonts/fontawesome/css/fontawesome.min.css", 289 | "fonts/fontawesome/css/solid.min.css", 290 | "fonts/fontawesome/webfonts/fa-brands-400.eot", 291 | "fonts/fontawesome/webfonts/fa-brands-400.svg", 292 | "fonts/fontawesome/webfonts/fa-brands-400.ttf", 293 | "fonts/fontawesome/webfonts/fa-brands-400.woff", 294 | "fonts/fontawesome/webfonts/fa-brands-400.woff2", 295 | "fonts/fontawesome/webfonts/fa-solid-900.eot", 296 | "fonts/fontawesome/webfonts/fa-solid-900.svg", 297 | "fonts/fontawesome/webfonts/fa-solid-900.ttf", 298 | "fonts/fontawesome/webfonts/fa-solid-900.woff", 299 | "fonts/fontawesome/webfonts/fa-solid-900.woff2", 300 | "js/bundle.b697b0f46a5b7946eb33.js", 301 | "img/android-chrome-36x36.png", // Favicon, Android Chrome M39+ with 0.75 screen density 302 | "img/android-chrome-48x48.png", // Favicon, Android Chrome M39+ with 1.0 screen density 303 | "img/android-chrome-72x72.png", // Favicon, Android Chrome M39+ with 1.5 screen density 304 | "img/android-chrome-96x96.png", // Favicon, Android Chrome M39+ with 2.0 screen density 305 | "img/android-chrome-144x144.png", // Favicon, Android Chrome M39+ with 3.0 screen density 306 | "img/android-chrome-192x192.png", // Favicon, Android Chrome M39+ with 4.0 screen density 307 | "img/android-chrome-256x256.png", // Favicon, Android Chrome M47+ Splash screen with 1.5 screen density 308 | "img/android-chrome-384x384.png", // Favicon, Android Chrome M47+ Splash screen with 3.0 screen density 309 | "img/android-chrome-512x512.png", // Favicon, Android Chrome M47+ Splash screen with 4.0 screen density 310 | "img/apple-touch-icon.png", // Favicon, Apple default 311 | "img/apple-touch-icon-57x57.png", // Apple iPhone, Non-retina with iOS6 or prior 312 | "img/apple-touch-icon-60x60.png", // Apple iPhone, Non-retina with iOS7 313 | "img/apple-touch-icon-72x72.png", // Apple iPad, Non-retina with iOS6 or prior 314 | "img/apple-touch-icon-76x76.png", // Apple iPad, Non-retina with iOS7 315 | "img/apple-touch-icon-114x114.png", // Apple iPhone, Retina with iOS6 or prior 316 | "img/apple-touch-icon-120x120.png", // Apple iPhone, Retina with iOS7 317 | "img/apple-touch-icon-144x144.png", // Apple iPad, Retina with iOS6 or prior 318 | "img/apple-touch-icon-152x152.png", // Apple iPad, Retina with iOS7 319 | "img/apple-touch-icon-180x180.png", // Apple iPhone 6 Plus with iOS8 320 | "img/browserconfig.xml", // IE11 icon configuration file 321 | "img/favicon.ico", // Favicon, IE and fallback for other browsers 322 | "img/favicon-16x16.png", // Favicon, default 323 | "img/favicon-32x32.png", // Favicon, Safari on Mac OS 324 | "img/maskable_icon.png", // Favicon, maskable https://web.dev/maskable-icon 325 | "img/monochrome_icon.png", // Favicon, monochrome https://web.dev/monochrome-icon 326 | "img/mstile-70x70.png", // Favicon, Windows 8 / IE11 327 | "img/mstile-144x144.png", // Favicon, Windows 8 / IE10 328 | "img/mstile-150x150.png", // Favicon, Windows 8 / IE11 329 | "img/mstile-310x150.png", // Favicon, Windows 8 / IE11 330 | "img/mstile-310x310.png", // Favicon, Windows 8 / IE11 331 | "img/safari-pinned-tab.svg", // Favicon, Safari pinned tab 332 | "img/share.jpg" // Social media sharing 333 | ]; 334 | 335 | // 1) INSTALL - triggers when service worker-controlled pages are accessed subsequently 336 | // Add all cacheFiles to cache 337 | // If any file fails to be fetched 338 | // cache.addAll rejects 339 | // install fails 340 | // the service worker will be abandoned (if an older version is running, it'll be left intact) 341 | self.addEventListener("install", event => { 342 | 343 | // Kick out the old service worker 344 | self.skipWaiting(); 345 | 346 | event.waitUntil( 347 | caches.open(cacheName).then(cache => { 348 | return cache.addAll(cacheFiles); 349 | }) 350 | ); 351 | }); 352 | 353 | // 2) ACTIVATE - triggers when service worker is installed successfully 354 | // Delete non-current caches used in previous versions 355 | // (Can block page loads, only use for things you couldn't do while previous version was active) 356 | self.addEventListener("activate", event => { 357 | event.waitUntil( 358 | caches.keys().then(cacheObjects => { 359 | return Promise.all( 360 | cacheObjects.map(cacheObjectName => { 361 | if (cacheObjectName != cacheName) { 362 | return caches.delete(cacheObjectName); 363 | } 364 | }) 365 | ) 366 | }) 367 | ); 368 | }); 369 | 370 | // 3) FETCH - triggers when any resource controlled by a service worker is fetched 371 | // Offline-first - cache falling back to network strategy 372 | self.addEventListener("fetch", event => { 373 | const cacheBlacklist = [ 374 | "adservice", 375 | "amazon-adsystem", 376 | "amazon.com", 377 | "doubleclick", 378 | "facebook", 379 | "google-analytics", 380 | "google.com", 381 | "googleads", 382 | "googlesyndication", 383 | "googletagmanager", 384 | "googletagservices", 385 | "pagead", 386 | "repixel" 387 | ]; 388 | 389 | const url = new URL(event.request.url); 390 | const online = navigator.onLine ? true : false; 391 | const blacklisted = [url.hostname].filter(hostname => cacheBlacklist.some(item => hostname.includes(item))).length ? true : false; 392 | const cacheMatch = cacheFiles.find(cacheFile => cacheFile.includes(url.pathname.replace("/", ""))); 393 | const cacheResponse = (online && (event.request.method === "GET") && !blacklisted) ? true : false; 394 | 395 | if (!online) { 396 | if (cacheMatch) { 397 | event.respondWith(fetchCacheResponse(cacheMatch)); 398 | } else { 399 | if (blacklisted) { 400 | console.warn(`Ignoring offline blacklisted request: ${event.request.url}`); 401 | } else { 402 | event.respondWith(fetchCacheResponse("index.html")); 403 | } 404 | } 405 | } else { 406 | if (cacheMatch) { 407 | event.respondWith(fetchCacheResponse(cacheMatch) || fetchNetworkResponse(event.request, cacheResponse)); 408 | } else { 409 | event.respondWith(fetchNetworkResponse(event.request, cacheResponse)); 410 | } 411 | } 412 | }); 413 | 414 | const fetchCacheResponse = eventRequest => { 415 | return caches.match(eventRequest) 416 | .then(responseCache => { 417 | if (responseCache) { 418 | return responseCache; 419 | } else { 420 | return fetchCacheResponse("index.html"); 421 | } 422 | }) 423 | .catch(error => { 424 | return fetchCacheResponse("index.html"); 425 | }); 426 | } 427 | 428 | const fetchNetworkResponse = (eventRequest, updateCache = false) => { 429 | return fetch(eventRequest) 430 | .then(responseNetwork => { 431 | if (updateCache) { 432 | const responseNetworkCache = responseNetwork.clone(); 433 | const responseNetworkReturn = responseNetwork.clone(); 434 | caches.open(cacheName) 435 | .then(cache => cache.put(eventRequest, responseNetworkCache)) 436 | .catch(error => console.warn(error)); 437 | return responseNetworkReturn; 438 | } else { 439 | return responseNetwork; 440 | } 441 | }) 442 | .catch(error => console.warn(error)); 443 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | Dog Safe Foods

Tips

When sharing food with dogs, it is important to keep a few things in mind:
  • Ask your veterinarian before feeding your dog anything—especially if they take medication or have any medical conditions (e.g. diabetes, allergies, etc.). Some medication can interact with foods, so don't take any chances!
  • Any foods dogs are eating for the first time should be introduced in small quantities. Monitor them after they eat it, and if they experience any negative symptoms, don't feed it to them again.
  • Any foods outside of your dog's normal diet should make up less than 10% of their daily food intake. For example, if your dog eats 5 cups of food per day, they shouldn't eat more than ½ cup of treats (e.g. ¼ cup of blueberries, and a couple of baby carrots). Any foods fed to dogs in large amounts can lead to digestive upset, so moderation is truly key.
  • Any foods shared with dogs should be cut into bite-size pieces, with all choking hazards removed (e.g. pits, peels, seeds). These choking hazards can even become obstructed in your dogs gastrointestinal tract. Never share these things with dogs, even if they are perfectly normal for humans to eat.
  • Dogs shouldn't eat foods with added salt, oils, sugar, seasonings, sauces, or any other variations. These additions, while delicious, often contain ingredients that are harmful, if not toxic to dogs, such as garlic, onions, or even xylitol. Dogs should not eat foods that were prepared with these ingredients, either.
  • Dogs shouldn't eat high fat foods, even if they are generally considered healthy for us. Dogs aren't built to eat large amounts of fat and these foods can cause dogs to get gastrointestinal upset and even pancreatitis, an inflammation of the pancreas which can range from mild to severe. Symptoms are usually vomiting, lethargy, diarrhea, and a loss of appetite. This can be an emergency situation and requires immediate veterinary treatment.
  • Dogs shouldn't eat processed foods such as baked foods, boxed snacks, fast food, etc. Aside from being unhealthy and high in fat, they often conceal ingredients that may be troublesome or even toxic for dogs, such as a sweetener called xylitol. Be sure to thoroughly read the ingredients of any processed foods you share with your dog, and if you can't determine the safety level of each ingredient, do not share it with your dog.
  • Dogs shouldn't eat canned versions of foods (e.g. fruits, vegetables, etc.), due to their added preservatives including excess salt and sugar, which can be harmful to dogs (and humans!). Choose fresh or even frozen versions instead.
  • Recent research suggests that several foods, such as peas, legumes, and potatoes, while not toxic, may not be appropriate for dogs to consume regularly, due to their correlation with the development of a heart condition called canine dilated cardiomyopathy (DCM). Read more about the DCM warning at FDA.gov.

-------------------------------------------------------------------------------- /gin.html: -------------------------------------------------------------------------------- 1 | Dog Safe Foods - Gin

Tips

When sharing food with dogs, it is important to keep a few things in mind:
  • Ask your veterinarian before feeding your dog anything—especially if they take medication or have any medical conditions (e.g. diabetes, allergies, etc.). Some medication can interact with foods, so don't take any chances!
  • Any foods dogs are eating for the first time should be introduced in small quantities. Monitor them after they eat it, and if they experience any negative symptoms, don't feed it to them again.
  • Any foods outside of your dog's normal diet should make up less than 10% of their daily food intake. For example, if your dog eats 5 cups of food per day, they shouldn't eat more than ½ cup of treats (e.g. ¼ cup of blueberries, and a couple of baby carrots). Any foods fed to dogs in large amounts can lead to digestive upset, so moderation is truly key.
  • Any foods shared with dogs should be cut into bite-size pieces, with all choking hazards removed (e.g. pits, peels, seeds). These choking hazards can even become obstructed in your dogs gastrointestinal tract. Never share these things with dogs, even if they are perfectly normal for humans to eat.
  • Dogs shouldn't eat foods with added salt, oils, sugar, seasonings, sauces, or any other variations. These additions, while delicious, often contain ingredients that are harmful, if not toxic to dogs, such as garlic, onions, or even xylitol. Dogs should not eat foods that were prepared with these ingredients, either.
  • Dogs shouldn't eat high fat foods, even if they are generally considered healthy for us. Dogs aren't built to eat large amounts of fat and these foods can cause dogs to get gastrointestinal upset and even pancreatitis, an inflammation of the pancreas which can range from mild to severe. Symptoms are usually vomiting, lethargy, diarrhea, and a loss of appetite. This can be an emergency situation and requires immediate veterinary treatment.
  • Dogs shouldn't eat processed foods such as baked foods, boxed snacks, fast food, etc. Aside from being unhealthy and high in fat, they often conceal ingredients that may be troublesome or even toxic for dogs, such as a sweetener called xylitol. Be sure to thoroughly read the ingredients of any processed foods you share with your dog, and if you can't determine the safety level of each ingredient, do not share it with your dog.
  • Dogs shouldn't eat canned versions of foods (e.g. fruits, vegetables, etc.), due to their added preservatives including excess salt and sugar, which can be harmful to dogs (and humans!). Choose fresh or even frozen versions instead.
  • Recent research suggests that several foods, such as peas, legumes, and potatoes, while not toxic, may not be appropriate for dogs to consume regularly, due to their correlation with the development of a heart condition called canine dilated cardiomyopathy (DCM). Read more about the DCM warning at FDA.gov.

-------------------------------------------------------------------------------- /ham.html: -------------------------------------------------------------------------------- 1 | Dog Safe Foods - Ham

Tips

When sharing food with dogs, it is important to keep a few things in mind:
  • Ask your veterinarian before feeding your dog anything—especially if they take medication or have any medical conditions (e.g. diabetes, allergies, etc.). Some medication can interact with foods, so don't take any chances!
  • Any foods dogs are eating for the first time should be introduced in small quantities. Monitor them after they eat it, and if they experience any negative symptoms, don't feed it to them again.
  • Any foods outside of your dog's normal diet should make up less than 10% of their daily food intake. For example, if your dog eats 5 cups of food per day, they shouldn't eat more than ½ cup of treats (e.g. ¼ cup of blueberries, and a couple of baby carrots). Any foods fed to dogs in large amounts can lead to digestive upset, so moderation is truly key.
  • Any foods shared with dogs should be cut into bite-size pieces, with all choking hazards removed (e.g. pits, peels, seeds). These choking hazards can even become obstructed in your dogs gastrointestinal tract. Never share these things with dogs, even if they are perfectly normal for humans to eat.
  • Dogs shouldn't eat foods with added salt, oils, sugar, seasonings, sauces, or any other variations. These additions, while delicious, often contain ingredients that are harmful, if not toxic to dogs, such as garlic, onions, or even xylitol. Dogs should not eat foods that were prepared with these ingredients, either.
  • Dogs shouldn't eat high fat foods, even if they are generally considered healthy for us. Dogs aren't built to eat large amounts of fat and these foods can cause dogs to get gastrointestinal upset and even pancreatitis, an inflammation of the pancreas which can range from mild to severe. Symptoms are usually vomiting, lethargy, diarrhea, and a loss of appetite. This can be an emergency situation and requires immediate veterinary treatment.
  • Dogs shouldn't eat processed foods such as baked foods, boxed snacks, fast food, etc. Aside from being unhealthy and high in fat, they often conceal ingredients that may be troublesome or even toxic for dogs, such as a sweetener called xylitol. Be sure to thoroughly read the ingredients of any processed foods you share with your dog, and if you can't determine the safety level of each ingredient, do not share it with your dog.
  • Dogs shouldn't eat canned versions of foods (e.g. fruits, vegetables, etc.), due to their added preservatives including excess salt and sugar, which can be harmful to dogs (and humans!). Choose fresh or even frozen versions instead.
  • Recent research suggests that several foods, such as peas, legumes, and potatoes, while not toxic, may not be appropriate for dogs to consume regularly, due to their correlation with the development of a heart condition called canine dilated cardiomyopathy (DCM). Read more about the DCM warning at FDA.gov.

-------------------------------------------------------------------------------- /rum.html: -------------------------------------------------------------------------------- 1 | Dog Safe Foods - Rum

Tips

When sharing food with dogs, it is important to keep a few things in mind:
  • Ask your veterinarian before feeding your dog anything—especially if they take medication or have any medical conditions (e.g. diabetes, allergies, etc.). Some medication can interact with foods, so don't take any chances!
  • Any foods dogs are eating for the first time should be introduced in small quantities. Monitor them after they eat it, and if they experience any negative symptoms, don't feed it to them again.
  • Any foods outside of your dog's normal diet should make up less than 10% of their daily food intake. For example, if your dog eats 5 cups of food per day, they shouldn't eat more than ½ cup of treats (e.g. ¼ cup of blueberries, and a couple of baby carrots). Any foods fed to dogs in large amounts can lead to digestive upset, so moderation is truly key.
  • Any foods shared with dogs should be cut into bite-size pieces, with all choking hazards removed (e.g. pits, peels, seeds). These choking hazards can even become obstructed in your dogs gastrointestinal tract. Never share these things with dogs, even if they are perfectly normal for humans to eat.
  • Dogs shouldn't eat foods with added salt, oils, sugar, seasonings, sauces, or any other variations. These additions, while delicious, often contain ingredients that are harmful, if not toxic to dogs, such as garlic, onions, or even xylitol. Dogs should not eat foods that were prepared with these ingredients, either.
  • Dogs shouldn't eat high fat foods, even if they are generally considered healthy for us. Dogs aren't built to eat large amounts of fat and these foods can cause dogs to get gastrointestinal upset and even pancreatitis, an inflammation of the pancreas which can range from mild to severe. Symptoms are usually vomiting, lethargy, diarrhea, and a loss of appetite. This can be an emergency situation and requires immediate veterinary treatment.
  • Dogs shouldn't eat processed foods such as baked foods, boxed snacks, fast food, etc. Aside from being unhealthy and high in fat, they often conceal ingredients that may be troublesome or even toxic for dogs, such as a sweetener called xylitol. Be sure to thoroughly read the ingredients of any processed foods you share with your dog, and if you can't determine the safety level of each ingredient, do not share it with your dog.
  • Dogs shouldn't eat canned versions of foods (e.g. fruits, vegetables, etc.), due to their added preservatives including excess salt and sugar, which can be harmful to dogs (and humans!). Choose fresh or even frozen versions instead.
  • Recent research suggests that several foods, such as peas, legumes, and potatoes, while not toxic, may not be appropriate for dogs to consume regularly, due to their correlation with the development of a heart condition called canine dilated cardiomyopathy (DCM). Read more about the DCM warning at FDA.gov.

-------------------------------------------------------------------------------- /tea.html: -------------------------------------------------------------------------------- 1 | Dog Safe Foods - Tea

Tips

When sharing food with dogs, it is important to keep a few things in mind:
  • Ask your veterinarian before feeding your dog anything—especially if they take medication or have any medical conditions (e.g. diabetes, allergies, etc.). Some medication can interact with foods, so don't take any chances!
  • Any foods dogs are eating for the first time should be introduced in small quantities. Monitor them after they eat it, and if they experience any negative symptoms, don't feed it to them again.
  • Any foods outside of your dog's normal diet should make up less than 10% of their daily food intake. For example, if your dog eats 5 cups of food per day, they shouldn't eat more than ½ cup of treats (e.g. ¼ cup of blueberries, and a couple of baby carrots). Any foods fed to dogs in large amounts can lead to digestive upset, so moderation is truly key.
  • Any foods shared with dogs should be cut into bite-size pieces, with all choking hazards removed (e.g. pits, peels, seeds). These choking hazards can even become obstructed in your dogs gastrointestinal tract. Never share these things with dogs, even if they are perfectly normal for humans to eat.
  • Dogs shouldn't eat foods with added salt, oils, sugar, seasonings, sauces, or any other variations. These additions, while delicious, often contain ingredients that are harmful, if not toxic to dogs, such as garlic, onions, or even xylitol. Dogs should not eat foods that were prepared with these ingredients, either.
  • Dogs shouldn't eat high fat foods, even if they are generally considered healthy for us. Dogs aren't built to eat large amounts of fat and these foods can cause dogs to get gastrointestinal upset and even pancreatitis, an inflammation of the pancreas which can range from mild to severe. Symptoms are usually vomiting, lethargy, diarrhea, and a loss of appetite. This can be an emergency situation and requires immediate veterinary treatment.
  • Dogs shouldn't eat processed foods such as baked foods, boxed snacks, fast food, etc. Aside from being unhealthy and high in fat, they often conceal ingredients that may be troublesome or even toxic for dogs, such as a sweetener called xylitol. Be sure to thoroughly read the ingredients of any processed foods you share with your dog, and if you can't determine the safety level of each ingredient, do not share it with your dog.
  • Dogs shouldn't eat canned versions of foods (e.g. fruits, vegetables, etc.), due to their added preservatives including excess salt and sugar, which can be harmful to dogs (and humans!). Choose fresh or even frozen versions instead.
  • Recent research suggests that several foods, such as peas, legumes, and potatoes, while not toxic, may not be appropriate for dogs to consume regularly, due to their correlation with the development of a heart condition called canine dilated cardiomyopathy (DCM). Read more about the DCM warning at FDA.gov.

-------------------------------------------------------------------------------- /acai.html: -------------------------------------------------------------------------------- 1 | Dog Safe Foods - Açaí

Tips

When sharing food with dogs, it is important to keep a few things in mind:
  • Ask your veterinarian before feeding your dog anything—especially if they take medication or have any medical conditions (e.g. diabetes, allergies, etc.). Some medication can interact with foods, so don't take any chances!
  • Any foods dogs are eating for the first time should be introduced in small quantities. Monitor them after they eat it, and if they experience any negative symptoms, don't feed it to them again.
  • Any foods outside of your dog's normal diet should make up less than 10% of their daily food intake. For example, if your dog eats 5 cups of food per day, they shouldn't eat more than ½ cup of treats (e.g. ¼ cup of blueberries, and a couple of baby carrots). Any foods fed to dogs in large amounts can lead to digestive upset, so moderation is truly key.
  • Any foods shared with dogs should be cut into bite-size pieces, with all choking hazards removed (e.g. pits, peels, seeds). These choking hazards can even become obstructed in your dogs gastrointestinal tract. Never share these things with dogs, even if they are perfectly normal for humans to eat.
  • Dogs shouldn't eat foods with added salt, oils, sugar, seasonings, sauces, or any other variations. These additions, while delicious, often contain ingredients that are harmful, if not toxic to dogs, such as garlic, onions, or even xylitol. Dogs should not eat foods that were prepared with these ingredients, either.
  • Dogs shouldn't eat high fat foods, even if they are generally considered healthy for us. Dogs aren't built to eat large amounts of fat and these foods can cause dogs to get gastrointestinal upset and even pancreatitis, an inflammation of the pancreas which can range from mild to severe. Symptoms are usually vomiting, lethargy, diarrhea, and a loss of appetite. This can be an emergency situation and requires immediate veterinary treatment.
  • Dogs shouldn't eat processed foods such as baked foods, boxed snacks, fast food, etc. Aside from being unhealthy and high in fat, they often conceal ingredients that may be troublesome or even toxic for dogs, such as a sweetener called xylitol. Be sure to thoroughly read the ingredients of any processed foods you share with your dog, and if you can't determine the safety level of each ingredient, do not share it with your dog.
  • Dogs shouldn't eat canned versions of foods (e.g. fruits, vegetables, etc.), due to their added preservatives including excess salt and sugar, which can be harmful to dogs (and humans!). Choose fresh or even frozen versions instead.
  • Recent research suggests that several foods, such as peas, legumes, and potatoes, while not toxic, may not be appropriate for dogs to consume regularly, due to their correlation with the development of a heart condition called canine dilated cardiomyopathy (DCM). Read more about the DCM warning at FDA.gov.

-------------------------------------------------------------------------------- /beef.html: -------------------------------------------------------------------------------- 1 | Dog Safe Foods - Beef

Tips

When sharing food with dogs, it is important to keep a few things in mind:
  • Ask your veterinarian before feeding your dog anything—especially if they take medication or have any medical conditions (e.g. diabetes, allergies, etc.). Some medication can interact with foods, so don't take any chances!
  • Any foods dogs are eating for the first time should be introduced in small quantities. Monitor them after they eat it, and if they experience any negative symptoms, don't feed it to them again.
  • Any foods outside of your dog's normal diet should make up less than 10% of their daily food intake. For example, if your dog eats 5 cups of food per day, they shouldn't eat more than ½ cup of treats (e.g. ¼ cup of blueberries, and a couple of baby carrots). Any foods fed to dogs in large amounts can lead to digestive upset, so moderation is truly key.
  • Any foods shared with dogs should be cut into bite-size pieces, with all choking hazards removed (e.g. pits, peels, seeds). These choking hazards can even become obstructed in your dogs gastrointestinal tract. Never share these things with dogs, even if they are perfectly normal for humans to eat.
  • Dogs shouldn't eat foods with added salt, oils, sugar, seasonings, sauces, or any other variations. These additions, while delicious, often contain ingredients that are harmful, if not toxic to dogs, such as garlic, onions, or even xylitol. Dogs should not eat foods that were prepared with these ingredients, either.
  • Dogs shouldn't eat high fat foods, even if they are generally considered healthy for us. Dogs aren't built to eat large amounts of fat and these foods can cause dogs to get gastrointestinal upset and even pancreatitis, an inflammation of the pancreas which can range from mild to severe. Symptoms are usually vomiting, lethargy, diarrhea, and a loss of appetite. This can be an emergency situation and requires immediate veterinary treatment.
  • Dogs shouldn't eat processed foods such as baked foods, boxed snacks, fast food, etc. Aside from being unhealthy and high in fat, they often conceal ingredients that may be troublesome or even toxic for dogs, such as a sweetener called xylitol. Be sure to thoroughly read the ingredients of any processed foods you share with your dog, and if you can't determine the safety level of each ingredient, do not share it with your dog.
  • Dogs shouldn't eat canned versions of foods (e.g. fruits, vegetables, etc.), due to their added preservatives including excess salt and sugar, which can be harmful to dogs (and humans!). Choose fresh or even frozen versions instead.
  • Recent research suggests that several foods, such as peas, legumes, and potatoes, while not toxic, may not be appropriate for dogs to consume regularly, due to their correlation with the development of a heart condition called canine dilated cardiomyopathy (DCM). Read more about the DCM warning at FDA.gov.

--------------------------------------------------------------------------------