├── .gitignore ├── README.md ├── bugreport.html ├── css ├── bootstrap.css └── noplugin.css ├── img ├── icon128.png ├── icon16.png ├── icon19.png ├── icon20-dark.png ├── icon32.png ├── icon38.png └── icon48.png ├── js ├── background.js ├── blocklist.js ├── bugreport.js ├── noplugin-compat.js ├── noplugin.js ├── playlist-viewer.js ├── popper.min.js ├── purify.js ├── tippy-bundle.umd.min.js └── welcome.js ├── license.txt ├── manifest.json ├── media-info.html ├── playlist-viewer.html ├── store-resources ├── chrome-promo-tile.png ├── chrome-promo-tile.xcf ├── chrome-screenshot-1.png ├── chrome-screenshot-1.xcf ├── chrome-screenshot-2.png ├── chrome-screenshot-2.xcf ├── firefox-screenshot-1.png ├── firefox-screenshot-2.png ├── opera-screenshot-1.png └── opera-screenshot-2.png └── welcome.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.psd 2 | *.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NoPlugin is now discontinued, see [this blog post](https://corbin.io/ending-development-of-noplugin/) for more information. 2 | 3 | NoPlugin is the missing compatibility layer for modern web browers, allowing you to view most legacy content designed for browser plugins like Adobe Flash, QuickTime, and Windows Media Player. All browsers have phrased out the use of browser plugins, due to performance and security problems, so NoPlugin was created to maintain some compatibility with outdated/archived sites. 4 | 5 | NoPlugin searches webpages for embedded plugin objects and converts them into native player elements, wherever possible. If the content can't be played in-browser (e.g. most Flash embeds), NoPlugin will help you download the file and play it in a separate application. 6 | 7 | [Download for Chrome](https://chrome.google.com/webstore/detail/noplugin-previously-quick/llpahfhchhlfdigfpeimeagojnkgeice) 8 | 9 | [Download for Firefox](https://addons.mozilla.org/en-US/firefox/addon/noplugin/) 10 | 11 | ## Fully supported players 12 | 13 | These embedded players are converted into HTML5 automatically by NoPlugin. 14 | 15 | - [YouTube](https://youtube.com) Flash embeds 16 | - [Twitch](https://twitch.tv) Flash embeds 17 | - [Vimeo](https://vimeo.com) Flash embeds 18 | - [Zanorg Player](https://radio.zanorg.com/zplayer_eng.htm) embeds 19 | - [Viddler](viddler.com) Flash embeds 20 | 21 | ## Partially supported players 22 | 23 | These embedded players sometimes contain files that can't play inside the browser. In cases where the file isn't supported by the browser, NoPlugin displays steps for opening the file in a separate application (like Windows Media Player, VLC, or Adobe Flash Projector). 24 | 25 | - QuickTime Player embeds 26 | - Windows Media Player embeds 27 | - RealPlayer embeds 28 | - VLC Plugin embeds 29 | - Media streams (`mms://`, `rtsp://`, `.ram`) 30 | - Miscellaneous Flash embeds 31 | 32 | ## Compatibility Mode 33 | 34 | NoPlugin has an optional Compatibility Mode, accessible by right-clicking on a page and clicking 'Toggle NoPlugin Compatibility Mode' (or by adding `?noplugin_compat=true` to a tab's URL). This changes some behaviors, listed below. 35 | 36 | - Modifies values for `navigator.plugins` and `navigator.mimeTypes` to trick some pages into thinking Flash player is availalable, to bypass some "Flash is not installed" errors 37 | - Creates a shim API for [flashembed.js](https://github.com/jquerytools/jquerytools/blob/master/src/toolbox/toolbox.flashembed.js) 38 | -------------------------------------------------------------------------------- /bugreport.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | NoPlugin 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |

19 |

Send bug report

20 |

21 |

If NoPlugin isn't loading plugin objects on a certain page correctly, you can create an issue on GitHub. This might help the developers improve NoPlugin.

22 |

23 | 24 |

25 |
26 |
27 | 28 |
29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /css/noplugin.css: -------------------------------------------------------------------------------- 1 | /* Form elements */ 2 | 3 | .noplugin-content button, .tippy-box[data-theme~='noplugin-tippy-theme'] button { 4 | color: #FFFFFF !important; 5 | font-family: Arial, Helvetica, sans-serif !important; 6 | background: #4b4b4b !important; 7 | border: 1px solid #272727 !important; 8 | border-radius: 5px !important; 9 | } 10 | 11 | .noplugin-content button:active, .tippy-box[data-theme~='noplugin-tippy-theme'] button:active { 12 | background: #3f3f3f !important; 13 | } 14 | 15 | /* Content dialogs */ 16 | 17 | .noplugin { 18 | font-family: Arial, Helvetica, sans-serif !important; 19 | overflow: auto !important; 20 | box-shadow: inset 0px 0px 0px 1px #000000 !important; 21 | box-sizing: border-box !important; 22 | border: 0px !important; 23 | background: #333333 !important; 24 | margin: 0px !important; 25 | min-height: 200px !important; 26 | min-width: 250px !important; 27 | display: table !important; 28 | -webkit-user-select: none !important; 29 | cursor: default !important; 30 | } 31 | .noplugin-content { 32 | display: table-cell !important; 33 | vertical-align: middle !important; 34 | text-align: center !important; 35 | padding: 3% !important; 36 | font-size: 100% !important; 37 | color: #FFFFFF !important; 38 | font-style: normal !important; 39 | } 40 | .noplugin-content button { 41 | margin-top: 10px !important; 42 | padding: 5px 10px !important; 43 | } 44 | 45 | /* Tippy-specific styles */ 46 | 47 | .tippy-box[data-theme~='noplugin-tippy-theme'] { 48 | font-family: Arial, Helvetica, sans-serif !important; 49 | line-height: 30px; 50 | } 51 | 52 | .tippy-box[data-theme~='noplugin-tippy-theme'] button { 53 | margin-left: 8px !important; 54 | } -------------------------------------------------------------------------------- /img/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corbindavenport/noplugin/beb0846339b10fad72ed6182baaa93c68bcdde7c/img/icon128.png -------------------------------------------------------------------------------- /img/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corbindavenport/noplugin/beb0846339b10fad72ed6182baaa93c68bcdde7c/img/icon16.png -------------------------------------------------------------------------------- /img/icon19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corbindavenport/noplugin/beb0846339b10fad72ed6182baaa93c68bcdde7c/img/icon19.png -------------------------------------------------------------------------------- /img/icon20-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corbindavenport/noplugin/beb0846339b10fad72ed6182baaa93c68bcdde7c/img/icon20-dark.png -------------------------------------------------------------------------------- /img/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corbindavenport/noplugin/beb0846339b10fad72ed6182baaa93c68bcdde7c/img/icon32.png -------------------------------------------------------------------------------- /img/icon38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corbindavenport/noplugin/beb0846339b10fad72ed6182baaa93c68bcdde7c/img/icon38.png -------------------------------------------------------------------------------- /img/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corbindavenport/noplugin/beb0846339b10fad72ed6182baaa93c68bcdde7c/img/icon48.png -------------------------------------------------------------------------------- /js/background.js: -------------------------------------------------------------------------------- 1 | // Function for generating UUID for analytics 2 | // Source: https://stackoverflow.com/a/2117523 3 | function uuidv4() { 4 | return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => 5 | (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) 6 | ) 7 | } 8 | 9 | // Function for downloading VLC Media Player 10 | function downloadVLC(platform) { 11 | if (platform === 'mac') { 12 | // macOS download 13 | chrome.tabs.create({ url: 'http://www.videolan.org/vlc/download-macosx.html' }) 14 | } else if (platform === 'win') { 15 | // Windows download 16 | chrome.tabs.create({ url: 'http://www.videolan.org/vlc/download-windows.html' }) 17 | } else if (platform === 'cros') { 18 | // Chrome OS download 19 | chrome.tabs.create({ url: 'market://details?id=org.videolan.vlc' }) 20 | } else { 21 | // Other downloads 22 | chrome.tabs.create({ url: 'http://www.videolan.org/vlc/#download' }) 23 | } 24 | } 25 | 26 | // Function for downloading Adobe Flash Projector 27 | function downloadProjector(platform) { 28 | var download = '' 29 | if (platform === 'linux') { 30 | download = 'https://web.archive.org/web/20201122011204id_/https://fpdownload.macromedia.com/pub/flashplayer/updaters/32/flash_player_sa_linux.x86_64.tar.gz' 31 | } else if (platform === 'mac') { 32 | download = 'https://web.archive.org/web/20201122011204id_/https://fpdownload.macromedia.com/pub/flashplayer/updaters/32/flashplayer_32_sa.dmg' 33 | } else if (platform === 'win') { 34 | download = 'https://web.archive.org/web/20201122011204id_/https://fpdownload.macromedia.com/pub/flashplayer/updaters/32/flashplayer_32_sa.exe' 35 | } 36 | chrome.downloads.download({ 37 | url: download 38 | }) 39 | } 40 | 41 | // Welcome page 42 | chrome.storage.local.get(function (data) { 43 | // Show welcome page on new version 44 | if (data.version) { 45 | if (!(data.version === chrome.runtime.getManifest().version)) { 46 | chrome.tabs.create({ 'url': chrome.extension.getURL('welcome.html') }) 47 | chrome.storage.local.set({ 48 | version: chrome.runtime.getManifest().version 49 | }) 50 | } 51 | } else { 52 | chrome.tabs.create({ 'url': chrome.extension.getURL('welcome.html') }) 53 | chrome.storage.local.set({ 54 | version: chrome.runtime.getManifest().version 55 | }) 56 | } 57 | // Generate UUID for analytics 58 | if (!data.uuid) { 59 | var uuid = uuidv4() 60 | chrome.storage.local.set({ 61 | uuid: uuid 62 | }) 63 | } 64 | }) 65 | 66 | // Create context menu for compatibility mode 67 | chrome.contextMenus.create({ 68 | title: 'Toggle NoPlugin Compatibility Mode', 69 | contexts: ['page'], 70 | documentUrlPatterns: ['http://*/*', 'https://*/*'], 71 | id: 'toggle-compat-mode' 72 | }) 73 | chrome.contextMenus.onClicked.addListener(function (itemData) { 74 | if (itemData.menuItemId == 'toggle-compat-mode') { 75 | chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { 76 | var obj = new URL(tabs[0].url) 77 | if (obj.searchParams.has('noplugin_compat')) { 78 | obj.searchParams.delete('noplugin_compat') 79 | } else { 80 | obj.searchParams.append('noplugin_compat', 'true') 81 | } 82 | chrome.tabs.update(tabs[0].id, { url: obj.toString() }) 83 | }) 84 | } 85 | }) 86 | 87 | chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { 88 | if (request.method == 'getPlatform') { 89 | chrome.runtime.getPlatformInfo(function (info) { 90 | sendResponse(info[request.key]) 91 | }) 92 | } else if (request.method === 'downloadProjector') { 93 | chrome.runtime.getPlatformInfo(function (info) { 94 | downloadProjector(info.os) 95 | }) 96 | } else if (request.method === 'downloadVLC') { 97 | chrome.runtime.getPlatformInfo(function (info) { 98 | downloadVLC(info.os) 99 | }) 100 | } else { 101 | sendResponse({}) 102 | } 103 | return true 104 | }) 105 | -------------------------------------------------------------------------------- /js/blocklist.js: -------------------------------------------------------------------------------- 1 | // This is where NoPlugin stores its blacklists. 2 | 3 | // URL patterns of sites that NoPlugin shouldn't run on 4 | // Examples: Sites that heavily rely on Flash, sites that host tracking scripts 5 | var siteBlocks = [ 6 | //'^https:\/\/corbin.io', (for testing) 7 | '^https:\/\/www\.xfinity\.com\/stream', // Xfinity Stream (#70) 8 | ] 9 | 10 | // URL patterns of files NoPlugin shouldn't load 11 | // Examples: Tracking scripts, fallbacks for HTML5 players, etc. 12 | var embedBlocks = [ 13 | 'fp.swf', // Tracking (#66) 14 | 'swfupload.swf', // Upload button that won't work outside the browser 15 | 'visitCounter105.swf', // Used for tracking 16 | 'soundmanager2.swf', // Fallback for HTML5 player: http://www.schillmania.com/projects/soundmanager2/ 17 | 'VPAIDFlash.swf', // Broken DRM: https://docs.viblast.com/player/videojs-vast 18 | 'pmfso.swf', // Security risk: https://github.com/offensive-security/exploitdb/blob/master/exploits/java/webapps/44634.txt#L63 19 | 'ZeroClipboard.swf', // Security risk: https://www.acunetix.com/vulnerabilities/web/cross-site-scripting-vulnerability-in-zeroclipboard-swf/ 20 | ] 21 | 22 | // Generate regexes 23 | const globalSiteBlockList = new RegExp(siteBlocks.join('|'), 'i') 24 | const globalEmbedBlockList = new RegExp(embedBlocks.join('|'), 'i') -------------------------------------------------------------------------------- /js/bugreport.js: -------------------------------------------------------------------------------- 1 | function getUrlVars() { 2 | var vars = {} 3 | var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) { 4 | vars[key] = value 5 | }) 6 | return vars 7 | } 8 | 9 | document.querySelector('.github-button').addEventListener('click', function () { 10 | window.open('https://github.com/corbindavenport/noplugin/issues/new', '_blank') 11 | }) -------------------------------------------------------------------------------- /js/noplugin-compat.js: -------------------------------------------------------------------------------- 1 | alert('NoPlugin Compatibility Mode is enabled for this page. Compatibility Mode tricks sites into thinking you have Adobe Flash Player and other plugins installed, allowing you to potentially view legacy plugin content. The mode may not work for all pages.\n\nCompatibility Mode will automatically turn off when you leave this page.') 2 | 3 | if (!navigator.plugins.namedItem('Shockwave Flash')) { 4 | 5 | console.log('Compatibilty mode enabled.') 6 | 7 | // Credit: https://stackoverflow.com/a/23456845 8 | var actualCode = '(' + function () { 9 | 'use strict' 10 | var navigator = window.navigator 11 | var modifiedNavigator 12 | if ('userAgent' in Navigator.prototype) { 13 | // Chrome 43+ moved all properties from navigator to the prototype, 14 | // so we have to modify the prototype instead of navigator. 15 | modifiedNavigator = Navigator.prototype 16 | } else { 17 | // Chrome 42- defined the property on navigator. 18 | modifiedNavigator = Object.create(navigator) 19 | Object.defineProperty(window, 'navigator', { 20 | value: modifiedNavigator, 21 | configurable: false, 22 | enumerable: false, 23 | writable: false 24 | }) 25 | } 26 | 27 | // Define list of plugins and supported MIME types 28 | 29 | Object.defineProperties(modifiedNavigator, { 30 | plugins: { 31 | value: { 32 | 'Shockwave Flash': { 33 | name: 'Shockwave Flash', 34 | description: 'Shockwave Flash 50.0 r0 (NoPlugin)', 35 | version: '50.0', 36 | filename: 'NoPlugin' 37 | }, 38 | 'QuickTime Plug-in': { 39 | name: 'QuickTime Plug-in 50.0', 40 | description: 'Shockwave Flash 50.0 r0 (NoPlugin)', 41 | version: '50.0', 42 | filename: 'NoPlugin' 43 | }, 44 | 'Windows Media Player Plug-in Dynamic Link Library': { 45 | name: 'Windows Media Player Plug-in Dynamic Link Library 5.0', 46 | description: 'Windows Media Player Plug-in Dynamic Link Library', 47 | version: '50.0', 48 | filename: 'NoPlugin' 49 | } 50 | }, 51 | configurable: false, 52 | enumerable: true, 53 | writable: false 54 | } 55 | }) 56 | 57 | // Define list of supported MIME types and their linked plugins 58 | 59 | Object.defineProperties(modifiedNavigator, { 60 | mimeTypes: { 61 | value: { 62 | 'application/x-shockwave-flash': { 63 | type: 'application/x-shockwave-flash', 64 | suffixes: 'swf', 65 | description: 'Shockwave Flash', 66 | enabledPlugin: navigator.plugins['Shockwave Flash'] 67 | }, 68 | 'application/futuresplash': { 69 | type: 'application/futuresplash', 70 | suffixes: 'spl', 71 | description: 'Shockwave Flash', 72 | enabledPlugin: navigator.plugins['Shockwave Flash'] 73 | }, 74 | 'video/mpeg': { 75 | type: 'video/mpeg', 76 | suffixes: 'mpeg,mpg,m1s,m1v,m1a,m75,m15,mp2,mpm,mpv,mpa', 77 | description: 'MPEG media', 78 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 79 | }, 80 | 'video/x-mpeg': { 81 | type: 'video/x-mpeg', 82 | suffixes: 'mpeg,mpg,m1s,m1v,m1a,m75,m15,mp2,mpm,mpv,mpa', 83 | description: 'MPEG media', 84 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 85 | }, 86 | 'audio/mpeg': { 87 | type: 'audio/mpeg', 88 | suffixes: 'mpeg,mpg,m1s,m1a,mp2,mpm,mpa,m2a', 89 | description: 'MPEG audio', 90 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 91 | }, 92 | 'audio/x-mpeg': { 93 | type: 'audio/x-mpeg', 94 | suffixes: 'mpeg,mpg,m1s,m1a,mp2,mpm,mpa,m2a', 95 | description: 'MPEG audio', 96 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 97 | }, 98 | 'video/3gpp': { 99 | type: 'video/3gpp', 100 | suffixes: '3gp,3gpp', 101 | description: '3GPP media', 102 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 103 | }, 104 | 'audio/3gpp': { 105 | type: 'audio/3gpp', 106 | suffixes: '3gp,3gpp', 107 | description: '3GPP media', 108 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 109 | }, 110 | 'video/3gpp2': { 111 | type: 'video/3gpp2', 112 | suffixes: '3g2,3gp2', 113 | description: '3GPP2 media', 114 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 115 | }, 116 | 'audio/3gpp2': { 117 | type: 'audio/3gpp2', 118 | suffixes: '3g2,3gp2', 119 | description: '3GPP2 media', 120 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 121 | }, 122 | 'audio/x-gsm': { 123 | type: 'audio/x-gsm', 124 | suffixes: 'gsm', 125 | description: 'GSM audio', 126 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 127 | }, 128 | 'audio/amr': { 129 | type: 'audio/amr', 130 | suffixes: 'AMR', 131 | description: 'AMR audio', 132 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 133 | }, 134 | 'audio/aac': { 135 | type: 'audio/aac', 136 | suffixes: 'aac,adts', 137 | description: 'AAC audio', 138 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 139 | }, 140 | 'audio/x-aac': { 141 | type: 'audio/x-aac', 142 | suffixes: 'aac,adts', 143 | description: 'AAC audio', 144 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 145 | }, 146 | 'audio/x-m4a': { 147 | type: 'audio/x-m4a', 148 | suffixes: 'aac,adts', 149 | description: 'AAC audio', 150 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 151 | }, 152 | 'audio/x-m4p': { 153 | type: 'audio/x-m4p', 154 | suffixes: 'm4p', 155 | description: 'AAC audio (protected)', 156 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 157 | }, 158 | 'audio/x-m4b': { 159 | type: 'audio/x-m4b', 160 | suffixes: 'm4b', 161 | description: 'AAC audio book', 162 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 163 | }, 164 | 'audio/x-caf': { 165 | type: 'audio/x-caf', 166 | suffixes: 'caf', 167 | description: 'CAF audio', 168 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 169 | }, 170 | 'audio/ac3': { 171 | type: 'audio/ac3', 172 | suffixes: 'ac3', 173 | description: 'AC3 audio', 174 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 175 | }, 176 | 'audio/x-ac3': { 177 | type: 'audio/x-ac3', 178 | suffixes: 'ac3', 179 | description: 'AC3 audio', 180 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 181 | }, 182 | 'image/jpeg2000': { 183 | type: 'image/jpeg2000', 184 | suffixes: 'jp2', 185 | description: 'JPEG2000 image', 186 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 187 | }, 188 | 'image/jpeg2000-image': { 189 | type: 'image/jpeg2000-image', 190 | suffixes: 'jp2', 191 | description: 'JPEG2000 image', 192 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 193 | }, 194 | 'image/x-jpeg2000-image': { 195 | type: 'image/x-jpeg2000-image', 196 | suffixes: 'jp2', 197 | description: 'JPEG2000 image', 198 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 199 | }, 200 | 'image/jp2': { 201 | type: 'image/jp2', 202 | suffixes: 'jp2', 203 | description: 'JPEG2000 image', 204 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 205 | }, 206 | 'video/x-m4v': { 207 | type: 'video/x-m4v', 208 | suffixes: 'm4v', 209 | description: 'Video (protected)', 210 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 211 | }, 212 | 'image/x-macpaint': { 213 | type: 'image/x-macpaint', 214 | suffixes: 'pntg,pnt,mac', 215 | description: 'MacPaint image', 216 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 217 | }, 218 | 'image/pict': { 219 | type: 'image/pict', 220 | suffixes: 'pict,pic,pct', 221 | description: 'PCT image', 222 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 223 | }, 224 | 'image/x-pict': { 225 | type: 'image/x-pict', 226 | suffixes: 'pict,pic,pct', 227 | description: 'PCT image', 228 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 229 | }, 230 | 'image/x-quicktime': { 231 | type: 'image/x-quicktime', 232 | suffixes: 'qtif,qti', 233 | description: 'QuickTime image', 234 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 235 | }, 236 | 'image/x-sgi': { 237 | type: 'image/x-sgi', 238 | suffixes: 'sgi,rgb', 239 | description: 'SGI image', 240 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 241 | }, 242 | 'image/x-targa': { 243 | type: 'image/x-targa', 244 | suffixes: 'targa,tga', 245 | description: 'TGA image', 246 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 247 | }, 248 | 'image/x-targa': { 249 | type: 'image/x-targa', 250 | suffixes: 'targa,tga', 251 | description: 'TGA image', 252 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 253 | }, 254 | 'video/sd-video': { 255 | type: 'video/sd-video', 256 | suffixes: 'sdv', 257 | description: 'SD video', 258 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 259 | }, 260 | 'application/x-mpeg': { 261 | type: 'application/x-mpeg', 262 | suffixes: 'amc', 263 | description: 'AMC media', 264 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 265 | }, 266 | 'video/mp4': { 267 | type: 'video/mp4', 268 | suffixes: 'mp4', 269 | description: 'MPEG-4 media', 270 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 271 | }, 272 | 'audio/mp4': { 273 | type: 'audio/mp4', 274 | suffixes: 'mp4', 275 | description: 'MPEG-4 media', 276 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 277 | }, 278 | 'audio/aiff': { 279 | type: 'audio/aiff', 280 | suffixes: 'aiff,aif,aifc,cdda', 281 | description: 'AIFF audio', 282 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 283 | }, 284 | 'audio/x-aiff': { 285 | type: 'audio/x-aiff', 286 | suffixes: 'aiff,aif,aifc,cdda', 287 | description: 'AIFF audio', 288 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 289 | }, 290 | 'audio/basic': { 291 | type: 'audio/basic', 292 | suffixes: 'au,snd,ulw', 293 | description: 'uLaw/AU audio', 294 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 295 | }, 296 | 'audio/mid': { 297 | type: 'audio/mid', 298 | suffixes: 'mid,midi,smf,kar', 299 | description: 'MIDI', 300 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 301 | }, 302 | 'audio/x-midi': { 303 | type: 'audio/x-midi', 304 | suffixes: 'mid,midi,smf,kar', 305 | description: 'MIDI', 306 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 307 | }, 308 | 'audio/midi': { 309 | type: 'audio/midi', 310 | suffixes: 'mid,midi,smf,kar', 311 | description: 'MIDI', 312 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 313 | }, 314 | 'audio/vnd.qcelp': { 315 | type: 'audio/vnd.qcelp', 316 | suffixes: 'qcp', 317 | description: 'QUALCOMM PureVoice audio', 318 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 319 | }, 320 | 'application/sdp': { 321 | type: 'application/sdp', 322 | suffixes: 'sdp', 323 | description: 'SDP stream descriptor', 324 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 325 | }, 326 | 'application/x-sdp': { 327 | type: 'application/x-sdp', 328 | suffixes: 'sdp', 329 | description: 'SDP stream descriptor', 330 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 331 | }, 332 | 'application/x-rtsp': { 333 | type: 'application/x-rtsp', 334 | suffixes: 'rtsp,rts', 335 | description: 'RTSP stream descriptor', 336 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 337 | }, 338 | 'video/quicktime': { 339 | type: 'video/quicktime', 340 | suffixes: 'mov,qt,mqv', 341 | description: 'QuickTime Movie', 342 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 343 | }, 344 | 'video/flc': { 345 | type: 'video/flc', 346 | suffixes: 'flc,fli,cel', 347 | description: 'AutoDesk Animator (FLC)', 348 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 349 | }, 350 | 'audio/x-wav': { 351 | type: 'audio/x-wav', 352 | suffixes: 'wav,bwf', 353 | description: 'WAVE audio', 354 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 355 | }, 356 | 'audio/wav': { 357 | type: 'audio/wav', 358 | suffixes: 'wav,bwf', 359 | description: 'WAVE audio', 360 | enabledPlugin: navigator.plugins['QuickTime Plug-in'] 361 | }, 362 | 'application/asx': { 363 | type: 'application/asx', 364 | suffixes: '*', 365 | description: 'Media Files', 366 | enabledPlugin: navigator.plugins['Windows Media Player Plug-in Dynamic Link Library'] 367 | }, 368 | 'video/x-ms-asf-plugin': { 369 | type: 'video/x-ms-asf-plugin', 370 | suffixes: '*', 371 | description: 'Media Files', 372 | enabledPlugin: navigator.plugins['Windows Media Player Plug-in Dynamic Link Library'] 373 | }, 374 | 'application/x-mplayer2': { 375 | type: 'application/x-mplayer2', 376 | suffixes: '*', 377 | description: 'Media Files', 378 | enabledPlugin: navigator.plugins['Windows Media Player Plug-in Dynamic Link Library'] 379 | }, 380 | 'video/x-ms-asf': { 381 | type: 'video/x-ms-asf', 382 | suffixes: 'asf,asx,*', 383 | description: 'Media Files', 384 | enabledPlugin: navigator.plugins['Windows Media Player Plug-in Dynamic Link Library'] 385 | }, 386 | 'video/x-ms-wm': { 387 | type: 'video/x-ms-wm', 388 | suffixes: 'wm,*', 389 | description: 'Media Files', 390 | enabledPlugin: navigator.plugins['Windows Media Player Plug-in Dynamic Link Library'] 391 | }, 392 | 'audio/x-ms-wma': { 393 | type: 'audio/x-ms-wma', 394 | suffixes: 'wma,*', 395 | description: 'Media Files', 396 | enabledPlugin: navigator.plugins['Windows Media Player Plug-in Dynamic Link Library'] 397 | }, 398 | 'audio/x-ms-wax': { 399 | type: 'audio/x-ms-wax', 400 | suffixes: 'wax,*', 401 | description: 'Media Files', 402 | enabledPlugin: navigator.plugins['Windows Media Player Plug-in Dynamic Link Library'] 403 | }, 404 | 'video/x-ms-wmv': { 405 | type: 'video/x-ms-wmv', 406 | suffixes: 'wmv,*', 407 | description: 'Media Files', 408 | enabledPlugin: navigator.plugins['Windows Media Player Plug-in Dynamic Link Library'] 409 | }, 410 | 'video/x-ms-wmv': { 411 | type: 'video/x-ms-wmv', 412 | suffixes: 'wmv,*', 413 | description: 'Media Files', 414 | enabledPlugin: navigator.plugins['Windows Media Player Plug-in Dynamic Link Library'] 415 | } 416 | 417 | }, 418 | configurable: false, 419 | enumerable: true, 420 | writable: false 421 | }, 422 | // Set user agent to Windows 423 | platform: { 424 | value: 'Win32', 425 | configurable: false, 426 | enumerable: true, 427 | writable: false 428 | }, 429 | userAgent: { 430 | value: 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko', 431 | configurable: false, 432 | enumerable: true, 433 | writable: false 434 | } 435 | }) 436 | 437 | // Define functions for navigator.plugins 438 | Object.defineProperties(modifiedNavigator.plugins, { 439 | namedItem: { 440 | // The navigator.plugins.namedItem() function is used to obtain information about the plugin specified in the argument 441 | value: function (plugin) { 442 | var info = navigator.plugins.hasOwnProperty(plugin) 443 | if (info) { 444 | // If there is a valid JSON object, return it 445 | return info 446 | } else { 447 | // If the desired plugin is not implemented by NoPlugin, try to generate something the page will accept 448 | var array = { 449 | name: plugin, 450 | description: 'Simulated ' + plugin + ' 50.0 (NoPlugin)', 451 | version: '50.0', 452 | filename: 'NoPlugin' 453 | } 454 | return array 455 | } 456 | }, 457 | configurable: false, 458 | enumerable: true, 459 | writable: false 460 | }, refresh: { 461 | // The navigator.plugins.refresh() function is used to refresh the list of currently-installed applications 462 | value: function () { 463 | return 464 | }, 465 | configurable: false, 466 | enumerable: true, 467 | writable: false 468 | }, length: { 469 | // The navigator.plugins.length function returns the number of plugins installed 470 | value: Object.keys(navigator.plugins).length, 471 | configurable: false, 472 | enumerable: true, 473 | writable: false 474 | } 475 | }) 476 | 477 | // Define functions for navigator.mimeTypes 478 | 479 | Object.defineProperties(modifiedNavigator.mimeTypes, { 480 | length: { 481 | // The navigator.mimeTypes.length function returns the number of mimeTypes supported 482 | value: function () { 483 | return Object.keys(navigator.mimeTypes).length 484 | }, 485 | configurable: false, 486 | enumerable: true, 487 | writable: false 488 | } 489 | }) 490 | 491 | // Wrapper for FlashEmbed library 492 | // Original code: https://github.com/jquerytools/jquerytools/blob/master/src/toolbox/toolbox.flashembed.js 493 | // Documentation: http://jquerytools.github.io/documentation/toolbox/flashembed.html 494 | // Test page: http://jquerytools.org/demos/toolbox/flashembed/index.html?noplugin_compat=true 495 | 496 | Object.defineProperty(window, 'flashembed', { 497 | // TODO: Implement asString(Object), getHTML(options, config), getVersion(), and isSupported(version) 498 | value: function (container, embedOptions, flashConfiguration) { 499 | console.log('Detected flashembed() call:', { container, embedOptions, flashConfiguration }) 500 | // The "container" param represents the ID of an element in the page 501 | container.replace('#', '') 502 | if (document.getElementById(container)) { 503 | container = document.getElementById(container) 504 | // Create Flash object 505 | var el = document.createElement('object') 506 | el.setAttribute('type', 'application/x-shockwave-flash') 507 | // Set embed options 508 | if (typeof embedOptions === 'string') { 509 | // embedOptions parameter is just a string of the media URL 510 | el.setAttribute('data', embedOptions) 511 | } else if (typeof embedOptions === 'object') { 512 | // embedOptions parameter is an object 513 | el.setAttribute('data', embedOptions.src) 514 | el.setAttribute('id', embedOptions.id) 515 | el.setAttribute('width', embedOptions.width) 516 | el.setAttribute('height', embedOptions.height) 517 | el.style.backgroundColor = embedOptions.bgcolor 518 | } else { 519 | console.error('Could not parse embedOptions') 520 | } 521 | // Set Flash configuration 522 | // This doesn't work for configurations with nested JSON 523 | if (typeof flashConfiguration === 'object') { 524 | var data = '' 525 | for (var key in flashConfiguration) { 526 | data += encodeURIComponent(key) + '=' + encodeURIComponent(flashConfiguration[key]) + '&' 527 | } 528 | data = data.slice(0, -1) 529 | el.setAttribute('flashvars', data) 530 | } 531 | // Inject object 532 | container.appendChild(el) 533 | } else { 534 | console.error('Could not find element with ID ' + container) 535 | } 536 | }, 537 | configurable: false, 538 | enumerable: true, 539 | writable: false 540 | }) 541 | 542 | } + ')()' 543 | 544 | var s = document.createElement('script') 545 | s.textContent = actualCode 546 | document.documentElement.appendChild(s) 547 | s.remove() 548 | } -------------------------------------------------------------------------------- /js/noplugin.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | How NoPlugin works: 4 | 5 | 1 - The loadDOM() function runs when the page is loaded and looks for and tags that match plugin content. 6 | 2 - Plugin objects and embeds are passed to the replaceObject() and replaceEmbed() functions, respectively, which parse information from the objects/embeds including size, ID, CSS styles, etc 7 | 3- Both replaceObject() and replaceEmbed() pass the data to injectPlayer(), which replaces the plugin HTML with either an HTML5 player if the media is supported or a prompt to download it 8 | 9 | */ 10 | 11 | // Detect Flash support 12 | const flashSupported = navigator.plugins.namedItem('Shockwave Flash') 13 | 14 | // YouTube ID regex 15 | const youtubeRegex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/ 16 | // Regex for detecting livestream links https://regex101.com/r/So4qWf/1 17 | const streamDetectRegex = /(\:\d{1,}$)|(\/$)/gm 18 | 19 | // Function for adding tooltip to replaced elements 20 | function addTooltip(element) { 21 | // Generate content 22 | var popupContent = document.createElement('div') 23 | popupContent.innerText = 'NoPlugin has loaded this legacy content.' 24 | // Add bug reporting button 25 | // var popupBtn = document.createElement('button') 26 | // popupBtn.innerText = 'Report Bug' 27 | // popupBtn.addEventListener('click', function () { 28 | // createPopup(chrome.extension.getURL("bugreport.html") + '?url=' + encodeURIComponent(window.location)) 29 | // }) 30 | // popupContent.appendChild(popupBtn) 31 | // Add donate button 32 | // var popupBtn = document.createElement('button') 33 | // popupBtn.innerText = 'Support NoPlugin' 34 | // popupBtn.addEventListener('click', function () { 35 | // window.open('https://www.patreon.com/corbindavenport', '_blank') 36 | // }) 37 | // popupContent.appendChild(popupBtn) 38 | // Show popup 39 | tippy(element, { 40 | content: popupContent, 41 | allowHTML: true, 42 | interactive: true, 43 | theme: 'noplugin-tippy-theme', 44 | maxWidth: 600 45 | }) 46 | } 47 | 48 | // Function for finding full path of URL 49 | function getFullURL(url) { 50 | // Fix URLs that start at the site root 51 | if (url.startsWith('/')) { 52 | url = window.location.protocol + "//" + window.location.host + url 53 | } 54 | // Regex to parse Internet Archive URLs: https://regex101.com/r/4F12w7/3 55 | var regex = /(?:web\.archive\.org\/web\/)(\d*)(\/)(.*)/ 56 | // Fix Internet Archive links 57 | if (url.includes('//web.archive.org/')) { 58 | // Get date 59 | var date = regex.exec(url)[1] 60 | // Get original URL 61 | var originalURL = regex.exec(url)[3] 62 | // Append '_id' to the end of the date, so the Internet Archive returns the original file and not an HTML file 63 | url = 'https://web.archive.org/web/' + date + 'id_/' + originalURL 64 | } 65 | // Get full URL 66 | var img = document.createElement('img') 67 | img.src = url 68 | url = img.src 69 | img.src = null 70 | return url 71 | } 72 | 73 | // Get the file name of a given URL 74 | function getFileName(url) { 75 | // Remove everything before the last slash, and everything after the ? symbol (if it exists) 76 | return url.split("?")[0].split('/').pop() 77 | } 78 | 79 | // Function for checking if a given object/embed is being used as an HTML5 fallback (e.g. inside a