|
1993 | 1993 | color: #111111;
|
1994 | 1994 | `,
|
1995 | 1995 | linkFont: `
|
1996 |
| - color: #5784FF; |
| 1996 | + color: #7295F6; |
1997 | 1997 | `,
|
1998 | 1998 | buttonBackground: `
|
1999 | 1999 | background: #5784FF;
|
| 2000 | + `, |
| 2001 | + buttonBackgroundHover: ` |
| 2002 | + background: #557FF3; |
| 2003 | + `, |
| 2004 | + buttonBackgroundPress: ` |
| 2005 | + background: #3969EF; |
2000 | 2006 | `,
|
2001 | 2007 | toggleButtonText: `
|
2002 | 2008 | color: #EEEEEE;
|
|
2017 | 2023 | `,
|
2018 | 2024 | buttonBackground: `
|
2019 | 2025 | background: #3969EF;
|
| 2026 | + `, |
| 2027 | + buttonBackgroundHover: ` |
| 2028 | + background: #2B55CA; |
| 2029 | + `, |
| 2030 | + buttonBackgroundPress: ` |
| 2031 | + background: #1E42A4; |
2020 | 2032 | `,
|
2021 | 2033 | toggleButtonText: `
|
2022 | 2034 | color: #666666;
|
|
2036 | 2048 | `,
|
2037 | 2049 | buttonFont: `
|
2038 | 2050 | color: #222222;
|
| 2051 | + `, |
| 2052 | + buttonBackgroundHover: ` |
| 2053 | + background: rgba(0, 0, 0, 0.12); |
| 2054 | + `, |
| 2055 | + buttonBackgroundPress: ` |
| 2056 | + background: rgba(0, 0, 0, 0.18); |
2039 | 2057 | `
|
2040 | 2058 | },
|
2041 | 2059 | button: `
|
|
2089 | 2107 | margin-top: 10px;
|
2090 | 2108 | z-index: 2147483647;
|
2091 | 2109 | position: absolute;
|
| 2110 | + line-height: normal; |
2092 | 2111 | `,
|
2093 | 2112 | textBubbleWidth: 360, // Should match the width rule in textBubble
|
2094 | 2113 | textBubbleLeftShift: 100, // Should match the CSS left: rule in textBubble
|
|
2212 | 2231 | margin: 10px auto;
|
2213 | 2232 | text-align: center;
|
2214 | 2233 | border: none;
|
2215 |
| - padding: 0; |
| 2234 | + padding: 0px 32px; |
2216 | 2235 | `,
|
2217 | 2236 | modalContentText: `
|
2218 | 2237 | font-family: DuckDuckGoPrivacyEssentials;
|
|
2953 | 2972 | // },
|
2954 | 2973 | // informationalModal: {
|
2955 | 2974 | // icon: blockedYTVideo,
|
2956 |
| - // messageTitle: 'Enable YouTube previews and reduce privacy?', |
| 2975 | + // messageTitle: 'Enable all YouTube previews?', |
2957 | 2976 | // messageBody: 'Showing previews will allow Google (which owns YouTube) to see some of your device’s information, but is still more private than playing the video.',
|
2958 |
| - // confirmButtonText: 'Enable Previews', |
| 2977 | + // confirmButtonText: 'Enable All Previews', |
2959 | 2978 | // rejectButtonText: 'No Thanks'
|
2960 | 2979 | // }
|
2961 | 2980 | // }
|
|
3609 | 3628 | element.style.cssText += cssText;
|
3610 | 3629 | }
|
3611 | 3630 |
|
| 3631 | + /** |
| 3632 | + * Create a `<style/>` element with DDG font-face styles/CSS |
| 3633 | + * to be attached to DDG wrapper elements |
| 3634 | + * @returns HTMLStyleElement |
| 3635 | + */ |
| 3636 | + function makeFontFaceStyleElement () { |
| 3637 | + // Put our custom font-faces inside the wrapper element, since |
| 3638 | + // @font-face does not work inside a shadowRoot. |
| 3639 | + // See https://github.com/mdn/interactive-examples/issues/887. |
| 3640 | + const fontFaceStyleElement = document.createElement('style'); |
| 3641 | + fontFaceStyleElement.textContent = styles.fontStyle; |
| 3642 | + return fontFaceStyleElement |
| 3643 | + } |
| 3644 | + |
| 3645 | + /** |
| 3646 | + * Create a `<style/>` element with base styles for DDG social container and |
| 3647 | + * button to be attached to DDG wrapper elements/shadowRoot, also returns a wrapper |
| 3648 | + * class name for Social Container link styles |
| 3649 | + * @param {"lightMode" | "darkMode"} mode Light or Dark mode value |
| 3650 | + * @returns {{wrapperClass: string, styleElement: HTMLStyleElement; }} |
| 3651 | + */ |
| 3652 | + function makeBaseStyleElement (mode = 'lightMode') { |
| 3653 | + // Style element includes our font & overwrites page styles |
| 3654 | + const styleElement = document.createElement('style'); |
| 3655 | + const wrapperClass = 'DuckDuckGoSocialContainer'; |
| 3656 | + styleElement.textContent = ` |
| 3657 | + .${wrapperClass} a { |
| 3658 | + ${styles[mode].linkFont} |
| 3659 | + font-weight: bold; |
| 3660 | + } |
| 3661 | + .${wrapperClass} a:hover { |
| 3662 | + ${styles[mode].linkFont} |
| 3663 | + font-weight: bold; |
| 3664 | + } |
| 3665 | + .DuckDuckGoButton { |
| 3666 | + ${styles.button} |
| 3667 | + } |
| 3668 | + .DuckDuckGoButton > div { |
| 3669 | + ${styles.buttonTextContainer} |
| 3670 | + } |
| 3671 | + .DuckDuckGoButton.primary { |
| 3672 | + ${styles[mode].buttonBackground} |
| 3673 | + } |
| 3674 | + .DuckDuckGoButton.primary > div { |
| 3675 | + ${styles[mode].buttonFont} |
| 3676 | + } |
| 3677 | + .DuckDuckGoButton.primary:hover { |
| 3678 | + ${styles[mode].buttonBackgroundHover} |
| 3679 | + } |
| 3680 | + .DuckDuckGoButton.primary:active { |
| 3681 | + ${styles[mode].buttonBackgroundPress} |
| 3682 | + } |
| 3683 | + .DuckDuckGoButton.secondary { |
| 3684 | + ${styles.cancelMode.buttonBackground} |
| 3685 | + } |
| 3686 | + .DuckDuckGoButton.secondary > div { |
| 3687 | + ${styles.cancelMode.buttonFont} |
| 3688 | + } |
| 3689 | + .DuckDuckGoButton.secondary:hover { |
| 3690 | + ${styles.cancelMode.buttonBackgroundHover} |
| 3691 | + } |
| 3692 | + .DuckDuckGoButton.secondary:active { |
| 3693 | + ${styles.cancelMode.buttonBackgroundPress} |
| 3694 | + } |
| 3695 | + `; |
| 3696 | + return { wrapperClass, styleElement } |
| 3697 | + } |
| 3698 | + |
3612 | 3699 | function makeTextButton (linkText, mode) {
|
3613 | 3700 | const linkElement = document.createElement('a');
|
3614 | 3701 | linkElement.style.cssText = styles.headerLink + styles[mode].linkFont;
|
3615 | 3702 | linkElement.textContent = linkText;
|
3616 | 3703 | return linkElement
|
3617 | 3704 | }
|
3618 | 3705 |
|
3619 |
| - function makeButton (buttonText, mode) { |
| 3706 | + /** |
| 3707 | + * Create a button element. |
| 3708 | + * @param {string} buttonText Text to be displayed inside the button |
| 3709 | + * @param {'lightMode' | 'darkMode' | 'cancelMode'} mode Key for theme value to determine the styling of the button. Key matches `styles[mode]` keys. |
| 3710 | + * - `'lightMode'`: Primary colors styling for light theme |
| 3711 | + * - `'darkMode'`: Primary colors styling for dark theme |
| 3712 | + * - `'cancelMode'`: Secondary colors styling for all themes |
| 3713 | + * @returns {HTMLButtonElement} Button element |
| 3714 | + */ |
| 3715 | + function makeButton (buttonText, mode = 'lightMode') { |
3620 | 3716 | const button = document.createElement('button');
|
3621 |
| - button.style.cssText = styles.button + styles[mode].buttonBackground; |
3622 |
| - const textContainer = document.createElement('div'); |
3623 |
| - textContainer.style.cssText = styles.buttonTextContainer + styles[mode].buttonFont; |
3624 |
| - textContainer.textContent = buttonText; |
3625 |
| - button.appendChild(textContainer); |
| 3717 | + button.classList.add('DuckDuckGoButton'); |
| 3718 | + button.classList.add(mode === 'cancelMode' ? 'secondary' : 'primary'); |
| 3719 | + if (buttonText) { |
| 3720 | + const textContainer = document.createElement('div'); |
| 3721 | + textContainer.textContent = buttonText; |
| 3722 | + button.appendChild(textContainer); |
| 3723 | + } |
3626 | 3724 | return button
|
3627 | 3725 | }
|
3628 | 3726 |
|
|
3700 | 3798 | function makeLoginButton (buttonText, mode, hoverTextTitle, hoverTextBody, icon, originalElement) {
|
3701 | 3799 | const container = document.createElement('div');
|
3702 | 3800 | container.style.cssText = 'position: relative;';
|
| 3801 | + container.appendChild(makeFontFaceStyleElement()); |
| 3802 | + |
| 3803 | + const shadowRoot = container.attachShadow({ mode: devMode ? 'open' : 'closed' }); |
3703 | 3804 | // inherit any class styles on the button
|
3704 | 3805 | container.className = 'fb-login-button FacebookLogin__button';
|
3705 |
| - const styleElement = document.createElement('style'); |
3706 |
| - styleElement.textContent = ` |
| 3806 | + const { styleElement } = makeBaseStyleElement(mode); |
| 3807 | + styleElement.textContent += ` |
3707 | 3808 | #DuckDuckGoPrivacyEssentialsHoverableText {
|
3708 | 3809 | display: none;
|
3709 | 3810 | }
|
3710 | 3811 | #DuckDuckGoPrivacyEssentialsHoverable:hover #DuckDuckGoPrivacyEssentialsHoverableText {
|
3711 | 3812 | display: block;
|
3712 | 3813 | }
|
3713 | 3814 | `;
|
3714 |
| - container.appendChild(styleElement); |
| 3815 | + shadowRoot.appendChild(styleElement); |
3715 | 3816 |
|
3716 | 3817 | const hoverContainer = document.createElement('div');
|
3717 | 3818 | hoverContainer.id = 'DuckDuckGoPrivacyEssentialsHoverable';
|
3718 | 3819 | hoverContainer.style.cssText = styles.hoverContainer;
|
3719 |
| - container.appendChild(hoverContainer); |
| 3820 | + shadowRoot.appendChild(hoverContainer); |
3720 | 3821 |
|
3721 | 3822 | // Make the button
|
3722 | 3823 | const button = makeButton(buttonText, mode);
|
|
3745 | 3846 | const hoverText = document.createElement('div');
|
3746 | 3847 | hoverText.style.cssText = styles.hoverTextBody;
|
3747 | 3848 | hoverText.textContent = hoverTextBody + ' ';
|
3748 |
| - hoverText.appendChild(getLearnMoreLink()); |
| 3849 | + hoverText.appendChild(getLearnMoreLink(mode)); |
3749 | 3850 | hoverBox.appendChild(hoverText);
|
3750 | 3851 |
|
3751 | 3852 | hoverContainer.appendChild(hoverBox);
|
|
3786 | 3887 | modalContainer.setAttribute('data-key', 'modal');
|
3787 | 3888 | modalContainer.style.cssText = styles.modalContainer;
|
3788 | 3889 |
|
| 3890 | + modalContainer.appendChild(makeFontFaceStyleElement()); |
| 3891 | + |
3789 | 3892 | const closeModal = () => {
|
3790 | 3893 | document.body.removeChild(modalContainer);
|
3791 | 3894 | cancelModal(entity);
|
|
3794 | 3897 | // Protect the contents of our modal inside a shadowRoot, to avoid
|
3795 | 3898 | // it being styled by the website's stylesheets.
|
3796 | 3899 | const shadowRoot = modalContainer.attachShadow({ mode: devMode ? 'open' : 'closed' });
|
| 3900 | + const { styleElement } = makeBaseStyleElement('lightMode'); |
| 3901 | + shadowRoot.appendChild(styleElement); |
3797 | 3902 |
|
3798 | 3903 | const pageOverlay = document.createElement('div');
|
3799 | 3904 | pageOverlay.style.cssText = styles.overlay;
|
|
3805 | 3910 | const modalTitle = createTitleRow('DuckDuckGo', null, closeModal);
|
3806 | 3911 | modal.appendChild(modalTitle);
|
3807 | 3912 |
|
3808 |
| - // Content |
3809 |
| - const modalContent = document.createElement('div'); |
3810 |
| - modalContent.style.cssText = styles.modalContent; |
3811 |
| - |
3812 | 3913 | const iconElement = document.createElement('img');
|
3813 | 3914 | iconElement.style.cssText = styles.icon + styles.modalIcon;
|
3814 | 3915 | iconElement.setAttribute('src', icon);
|
|
3818 | 3919 | title.style.cssText = styles.modalContentTitle;
|
3819 | 3920 | title.textContent = entityData[entity].modalTitle;
|
3820 | 3921 |
|
| 3922 | + // Content |
| 3923 | + const modalContent = document.createElement('div'); |
| 3924 | + modalContent.style.cssText = styles.modalContent; |
| 3925 | + |
3821 | 3926 | const message = document.createElement('div');
|
3822 | 3927 | message.style.cssText = styles.modalContentText;
|
3823 | 3928 | message.textContent = entityData[entity].modalText + ' ';
|
|
3903 | 4008 | const contentBlock = document.createElement('div');
|
3904 | 4009 | contentBlock.style.cssText = styles.wrapperDiv;
|
3905 | 4010 |
|
3906 |
| - // Put our custom font-faces inside the wrapper element, since |
3907 |
| - // @font-face does not work inside a shadowRoot. |
3908 |
| - // See https://github.com/mdn/interactive-examples/issues/887. |
3909 |
| - const fontFaceStyleElement = document.createElement('style'); |
3910 |
| - fontFaceStyleElement.textContent = styles.fontStyle; |
3911 |
| - contentBlock.appendChild(fontFaceStyleElement); |
| 4011 | + contentBlock.appendChild(makeFontFaceStyleElement()); |
3912 | 4012 |
|
3913 | 4013 | // Put everything else inside the shadowRoot of the wrapper element to
|
3914 | 4014 | // reduce the chances of the website's stylesheets messing up the
|
|
3917 | 4017 | const shadowRoot = contentBlock.attachShadow({ mode: shadowRootMode });
|
3918 | 4018 |
|
3919 | 4019 | // Style element includes our font & overwrites page styles
|
3920 |
| - const styleElement = document.createElement('style'); |
3921 |
| - const wrapperClass = 'DuckDuckGoSocialContainer'; |
3922 |
| - styleElement.textContent = ` |
3923 |
| - .${wrapperClass} a { |
3924 |
| - ${styles[widget.getMode()].linkFont} |
3925 |
| - font-weight: bold; |
3926 |
| - } |
3927 |
| - .${wrapperClass} a:hover { |
3928 |
| - ${styles[widget.getMode()].linkFont} |
3929 |
| - font-weight: bold; |
3930 |
| - } |
3931 |
| - `; |
| 4020 | + const { wrapperClass, styleElement } = makeBaseStyleElement(widget.getMode()); |
3932 | 4021 | shadowRoot.appendChild(styleElement);
|
3933 | 4022 |
|
3934 | 4023 | // Create overall grid structure
|
|
4054 | 4143 | youTubePreview.id = `yt-ctl-preview-${widget.widgetID}`;
|
4055 | 4144 | youTubePreview.style.cssText = styles.wrapperDiv + styles.placeholderWrapperDiv;
|
4056 | 4145 |
|
4057 |
| - // Put our custom font-faces inside the wrapper element, since |
4058 |
| - // @font-face does not work inside a shadowRoot. |
4059 |
| - // See https://github.com/mdn/interactive-examples/issues/887. |
4060 |
| - const fontFaceStyleElement = document.createElement('style'); |
4061 |
| - fontFaceStyleElement.textContent = styles.fontStyle; |
4062 |
| - youTubePreview.appendChild(fontFaceStyleElement); |
| 4146 | + youTubePreview.appendChild(makeFontFaceStyleElement()); |
4063 | 4147 |
|
4064 | 4148 | // Size the placeholder element to match the original video element styles.
|
4065 | 4149 | // If no styles are in place, it will get its current size
|
|
4069 | 4153 | // Protect the contents of our placeholder inside a shadowRoot, to avoid
|
4070 | 4154 | // it being styled by the website's stylesheets.
|
4071 | 4155 | const shadowRoot = youTubePreview.attachShadow({ mode: devMode ? 'open' : 'closed' });
|
| 4156 | + const { wrapperClass, styleElement } = makeBaseStyleElement(widget.getMode()); |
| 4157 | + shadowRoot.appendChild(styleElement); |
4072 | 4158 |
|
4073 | 4159 | const youTubePreviewDiv = document.createElement('div');
|
4074 | 4160 | youTubePreviewDiv.style.cssText = styles.youTubeDialogDiv;
|
| 4161 | + youTubePreviewDiv.classList.add(wrapperClass); |
4075 | 4162 | shadowRoot.appendChild(youTubePreviewDiv);
|
4076 | 4163 |
|
4077 | 4164 | /** Preview Image */
|
|
4099 | 4186 | topSection.appendChild(titleElement);
|
4100 | 4187 |
|
4101 | 4188 | /** Text Button on top section */
|
4102 |
| - const textButton = makeTextButton(widget.replaceSettings.buttonText, widget.getMode()); |
| 4189 | + // Use darkMode styles because the preview background is dark and causes poor contrast |
| 4190 | + // with lightMode button, making it hard to read. |
| 4191 | + const textButton = makeTextButton(widget.replaceSettings.buttonText, 'darkMode'); |
4103 | 4192 | textButton.id = titleID + 'TextButton';
|
4104 | 4193 |
|
4105 | 4194 | textButton.addEventListener(
|
|
4112 | 4201 | const playButtonRow = document.createElement('div');
|
4113 | 4202 | playButtonRow.style.cssText = styles.youTubePlayButtonRow;
|
4114 | 4203 |
|
4115 |
| - const playButton = document.createElement('button'); |
4116 |
| - playButton.style.cssText = styles.button + styles.youTubePlayButton + styles[widget.getMode()].buttonBackground; |
| 4204 | + const playButton = makeButton('', widget.getMode()); |
| 4205 | + playButton.style.cssText += styles.youTubePlayButton; |
4117 | 4206 |
|
4118 | 4207 | const videoPlayImg = document.createElement('img');
|
4119 | 4208 | const videoPlayIcon = widget.replaceSettings.placeholder.videoPlayIcon[widget.getMode()];
|
|
4141 | 4230 | );
|
4142 | 4231 | previewToggle.addEventListener(
|
4143 | 4232 | 'click',
|
4144 |
| - () => sendMessage('setYoutubePreviewsEnabled', { |
4145 |
| - name: 'youtubePreviewsEnabled', |
4146 |
| - value: false |
4147 |
| - }) |
| 4233 | + () => sendMessage('setYoutubePreviewsEnabled', false) |
4148 | 4234 | );
|
4149 | 4235 |
|
4150 | 4236 | /** Preview Info Text */
|
4151 | 4237 | const previewText = document.createElement('div');
|
4152 | 4238 | previewText.style.cssText = styles.contentText + styles.toggleButtonText + styles.youTubePreviewInfoText;
|
4153 | 4239 | previewText.innerText = widget.replaceSettings.placeholder.previewInfoText + ' ';
|
4154 |
| - previewText.appendChild(getLearnMoreLink()); |
| 4240 | + // Use darkMode styles because of preview background |
| 4241 | + previewText.appendChild(getLearnMoreLink('darkMode')); |
4155 | 4242 |
|
4156 | 4243 | previewToggleRow.appendChild(previewToggle);
|
4157 | 4244 | previewToggleRow.appendChild(previewText);
|
|
4205 | 4292 | isYoutubePreviewsEnabled = resp;
|
4206 | 4293 | },
|
4207 | 4294 | setYoutubePreviewsEnabled: function (resp) {
|
4208 |
| - if (!resp.messageType || resp.value === undefined) { return } |
4209 |
| - originalWindowDispatchEvent(new OriginalCustomEvent(resp.messageType, { detail: resp.value })); |
| 4295 | + if (resp?.messageType && typeof resp?.value === 'boolean') { |
| 4296 | + originalWindowDispatchEvent(new OriginalCustomEvent(resp.messageType, { detail: resp.value })); |
| 4297 | + } |
4210 | 4298 | },
|
4211 | 4299 | getYouTubeVideoDetails: function (resp) {
|
4212 |
| - if (!resp.status || !resp.videoURL) { return } |
4213 |
| - originalWindowDispatchEvent(new OriginalCustomEvent('ddg-ctp-youTubeVideoDetails', { detail: resp })); |
| 4300 | + if (resp?.status && typeof resp.videoURL === 'string') { |
| 4301 | + originalWindowDispatchEvent(new OriginalCustomEvent('ddg-ctp-youTubeVideoDetails', { detail: resp })); |
| 4302 | + } |
4214 | 4303 | },
|
4215 | 4304 | enableSocialTracker: function (resp) {
|
4216 | 4305 | originalWindowDispatchEvent(new OriginalCustomEvent('ddg-ctp-enableSocialTracker-complete', { detail: resp }));
|
|
0 commit comments