-
Notifications
You must be signed in to change notification settings - Fork 322
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replaced defunct YQL with multiple CORS proxies
- Loading branch information
Showing
4 changed files
with
108 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,137 +1,115 @@ | ||
var HTMLPreview = { | ||
(function () { | ||
|
||
var previewForm = document.getElementById('previewform'); | ||
|
||
content: '', | ||
var url = location.search.substring(1).replace(/\/\/github\.com/, '//raw.githubusercontent.com').replace(/\/blob\//, '/'); //Get URL of the raw file | ||
|
||
previewform: document.getElementById('previewform'), | ||
|
||
file: function() { | ||
return location.search.substring(1); //Get everything after the ? | ||
}, | ||
|
||
raw: function() { | ||
return HTMLPreview.file().replace(/\/\/github\.com/, '//raw.githubusercontent.com').replace(/\/blob\//, '/'); //Get URL of the raw file | ||
}, | ||
|
||
replaceAssets: function() { | ||
var frame, a, link, script, i, href, src; | ||
var replaceAssets = function () { | ||
var frame, a, link, links = [], script, scripts = [], i, href, src; | ||
//Framesets | ||
if (document.querySelectorAll('frameset').length) | ||
return; //Don't replace CSS/JS if it's a frameset, because it will be erased by document.write() | ||
//Frames | ||
frame = document.querySelectorAll('iframe[src],frame[src]'); | ||
for(i = 0; i < frame.length; ++i) { | ||
for (i = 0; i < frame.length; ++i) { | ||
src = frame[i].src; //Get absolute URL | ||
if(src.indexOf('//raw.githubusercontent.com') > 0 || src.indexOf('//bitbucket.org') > 0) { //Check if it's from raw.github.com or bitbucket.org | ||
frame[i].src = '//' + location.hostname + location.pathname + '?' + src; //Then rewrite URL so it can be loaded using YQL | ||
if (src.indexOf('//raw.githubusercontent.com') > 0 || src.indexOf('//bitbucket.org') > 0) { //Check if it's from raw.github.com or bitbucket.org | ||
frame[i].src = '//' + location.hostname + location.pathname + '?' + src; //Then rewrite URL so it can be loaded using CORS proxy | ||
} | ||
} | ||
//Links | ||
a = document.querySelectorAll('a[href]'); | ||
for(i = 0; i < a.length; ++i) { | ||
for (i = 0; i < a.length; ++i) { | ||
href = a[i].href; //Get absolute URL | ||
if(href.indexOf('#') > 0) { //Check if it's an anchor | ||
if (href.indexOf('#') > 0) { //Check if it's an anchor | ||
a[i].href = '//' + location.hostname + location.pathname + location.search + '#' + a[i].hash.substring(1); //Then rewrite URL with support for empty anchor | ||
} | ||
else if((href.indexOf('//raw.githubusercontent.com') > 0 || href.indexOf('//bitbucket.org') > 0) && (href.indexOf('.html') > 0 || href.indexOf('.htm') > 0)) { //Check if it's from raw.github.com or bitbucket.org and to HTML files | ||
a[i].href = '//' + location.hostname + location.pathname + '?' + href; //Then rewrite URL so it can be loaded using YQL | ||
} else if ((href.indexOf('//raw.githubusercontent.com') > 0 || href.indexOf('//bitbucket.org') > 0) && (href.indexOf('.html') > 0 || href.indexOf('.htm') > 0)) { //Check if it's from raw.github.com or bitbucket.org and to HTML files | ||
a[i].href = '//' + location.hostname + location.pathname + '?' + href; //Then rewrite URL so it can be loaded using CORS proxy | ||
} | ||
} | ||
if(document.querySelectorAll('frameset').length) | ||
return; //Don't replace CSS/JS if it's a frameset, because it will be erased by document.write() | ||
//Stylesheets | ||
link = document.querySelectorAll('link[rel=stylesheet]'); | ||
for(i = 0; i < link.length; ++i) { | ||
for (i = 0; i < link.length; ++i) { | ||
href = link[i].href; //Get absolute URL | ||
if(href.indexOf('//raw.githubusercontent.com') > 0 || href.indexOf('//bitbucket.org') > 0) { //Check if it's from raw.github.com or bitbucket.org | ||
HTMLPreview.send(href, 'loadCSS'); //Then load it using YQL | ||
if (href.indexOf('//raw.githubusercontent.com') > 0 || href.indexOf('//bitbucket.org') > 0) { //Check if it's from raw.github.com or bitbucket.org | ||
links.push(fetchProxy(href, null, 0)); //Then add it to links queue and fetch using CORS proxy | ||
} | ||
} | ||
Promise.all(links).then(function (res) { | ||
for (i = 0; i < res.length; ++i) { | ||
loadCSS(res[i]); | ||
} | ||
}); | ||
//Scripts | ||
script = document.querySelectorAll('script[type="text/htmlpreview"]'); | ||
for(i = 0; i < script.length; ++i) { | ||
for (i = 0; i < script.length; ++i) { | ||
src = script[i].src; //Get absolute URL | ||
if(src.indexOf('//raw.githubusercontent.com') > 0 || src.indexOf('//bitbucket.org') > 0) { //Check if it's from raw.github.com or bitbucket.org | ||
HTMLPreview.send(src, 'loadJS'); //Then load it using YQL | ||
} | ||
else { //Append all inline scripts | ||
if (src.indexOf('//raw.githubusercontent.com') > 0 || src.indexOf('//bitbucket.org') > 0) { //Check if it's from raw.github.com or bitbucket.org | ||
scripts.push(fetchProxy(src, null, 0)); //Then add it to scripts queue and fetch using CORS proxy | ||
} else { | ||
script[i].removeAttribute('type'); | ||
document.write(script[i].outerHTML); | ||
scripts.push(script[i].innerHTML); //Add inline script to queue to eval in order | ||
} | ||
} | ||
}, | ||
Promise.all(scripts).then(function (res) { | ||
for (i = 0; i < res.length; ++i) { | ||
loadJS(res[i]); | ||
} | ||
}); | ||
}; | ||
|
||
loadHTML: function(data) { | ||
/* if(data | ||
&& data.query | ||
&& data.query.diagnostics | ||
&& data.query.diagnostics.redirect) { | ||
HTMLPreview.send(data.query.diagnostics.redirect.content, 'loadHTML'); | ||
} | ||
else */ | ||
if(data | ||
&& data.query | ||
&& data.query.results | ||
&& data.query.results.resources | ||
&& data.query.results.resources.content | ||
&& data.query.results.resources.status == 200) { | ||
HTMLPreview.content = data.query.results.resources.content.replace(/<head>/i, '<head><base href="' + HTMLPreview.raw() + '">').replace(/<script( type=["'](text|application)\/javascript["'])?/gi, '<script type="text/htmlpreview"').replace(/<\/body>/i, '<script src="//' + location.hostname + '/htmlpreview.min.js"></script><script>HTMLPreview.replaceAssets();</script></body>').replace(/<\/head>\s*<frameset/gi, '<script src="//' + location.hostname + '/htmlpreview.min.js"></script><script>document.addEventListener("DOMContentLoaded",HTMLPreview.replaceAssets,false);</script></head><frameset'); //Add <base> just after <head> and inject <script> just before </body> or </head> if <frameset> | ||
setTimeout(function() { | ||
var loadHTML = function (data) { | ||
if (data) { | ||
data = data.replace(/<head([^>]*)>/i, '<head$1><base href="' + url + '">').replace(/<script(\s*src=["'][^"']*["'])?(\s*type=["'](text|application)\/javascript["'])?/gi, '<script type="text/htmlpreview"$1'); //Add <base> just after <head> and replace <script type="text/javascript"> with <script type="text/htmlpreview"> | ||
setTimeout(function () { | ||
document.open(); | ||
document.write(HTMLPreview.content); | ||
document.write(data); | ||
document.close(); | ||
}, 50); //Delay updating document to have it cleared before | ||
} | ||
else if(data | ||
&& data.error | ||
&& data.error.description) { | ||
HTMLPreview.previewform.innerHTML = data.error.description; | ||
replaceAssets(); | ||
}, 10); //Delay updating document to have it cleared before | ||
} | ||
else | ||
HTMLPreview.previewform.innerHTML = 'Error: Cannot load file ' + HTMLPreview.raw(); | ||
}, | ||
}; | ||
|
||
loadCSS: function(data) { | ||
/* if(data | ||
&& data.query | ||
&& data.query.diagnostics | ||
&& data.query.diagnostics.redirect) { | ||
HTMLPreview.send(data.query.diagnostics.redirect.content, 'loadCSS'); | ||
var loadCSS = function (data) { | ||
if (data) { | ||
var style = document.createElement('style'); | ||
style.innerHTML = data; | ||
document.head.appendChild(style); | ||
} | ||
else */ | ||
if(data | ||
&& data.query | ||
&& data.query.results | ||
&& data.query.results.resources | ||
&& data.query.results.resources.content | ||
&& data.query.results.resources.status == 200) { | ||
document.write('<style>' + data.query.results.resources.content.replace(/url\((?:'|")?([^\/][^:'"\)]+)(?:'|")?\)/gi, 'url(' + data.query.results.resources.url.replace(/[^\/]+\.css.*$/gi, '') + '$1)') + '</style>'); //If relative URL in CSS background-image property, then concatenate URL to CSS directory | ||
} | ||
}, | ||
}; | ||
|
||
loadJS: function(data) { | ||
/* if(data | ||
&& data.query | ||
&& data.query.diagnostics | ||
&& data.query.diagnostics.redirect) { | ||
HTMLPreview.send(data.query.diagnostics.redirect.content, 'loadJS'); | ||
} else */ | ||
if(data | ||
&& data.query | ||
&& data.query.results | ||
&& data.query.results.resources | ||
&& data.query.results.resources.content | ||
&& data.query.results.resources.status == 200) { | ||
document.write('<script>' + data.query.results.resources.content + '</script>'); | ||
var loadJS = function (data) { | ||
if (data) { | ||
var script = document.createElement('script'); | ||
script.innerHTML = data; | ||
document.body.appendChild(script); | ||
} | ||
}, | ||
|
||
send: function(file, callback) { | ||
document.write('<script src="//query.yahooapis.com/v1/public/yql?q=select%20*%20from%20data.headers%20where%20url%3D%22' + encodeURIComponent(file) + '%22&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&diagnostics=true&callback=HTMLPreview.' + callback + '"></script>'); //Get content using YQL | ||
}, | ||
}; | ||
|
||
var fetchProxy = function (url, options, i) { | ||
var proxy = [ | ||
'https://cors.io/?', | ||
'https://jsonp.afeld.me/?url=', | ||
'https://cors-anywhere.herokuapp.com/' | ||
]; | ||
return fetch(proxy[i] + url, options).then(function (res) { | ||
if (!res.ok) | ||
throw new Error('Cannot load ' + url + ': ' + res.status + ' ' + res.statusText); | ||
return res.text(); | ||
}).catch(function (error) { | ||
if (i === proxy.length - 1) | ||
throw error; | ||
return fetchProxy(url, options, i + 1); | ||
}) | ||
}; | ||
|
||
submitform: function() { | ||
location.href = '/?' + document.getElementById('file').value; | ||
return false; | ||
}, | ||
if (url && url.indexOf(location.hostname) < 0) | ||
fetchProxy(url, null, 0).then(loadHTML).catch(function (error) { | ||
console.error(error); | ||
previewForm.style.display = 'block'; | ||
previewForm.innerText = error; | ||
}); | ||
else | ||
previewForm.style.display = 'block'; | ||
|
||
init: function() { | ||
HTMLPreview.previewform.onsubmit = HTMLPreview.submitform; | ||
if(HTMLPreview.file()) { | ||
HTMLPreview.previewform.innerHTML = '<p>Loading...</p>'; | ||
HTMLPreview.send(HTMLPreview.raw(), 'loadHTML'); | ||
} | ||
} | ||
} | ||
})() |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,21 @@ | ||
GitHub & BitBucket HTML Preview | ||
------------------------------- | ||
|
||
Many GitHub repositories don't use GitHub Pages to host their HTML files. **GitHub & BitBucket HTML Preview** allows you to render those files without cloning or downloading whole repositories. It is a client-side solution and does not involve any third party hosting servers (except for Yahoo! Query Language to fetch assets). | ||
Many GitHub repositories don't use GitHub Pages to host their HTML files. **GitHub & BitBucket HTML Preview** allows you to render those files without cloning or downloading whole repositories. It is a client-side solution using a CORS proxy to fetch assets. | ||
|
||
If you try to open raw versions of any HTML, CSS or JS files in a web browser directly from GitHub, all you will see are sources. GitHub forces them to use the "text/plain" content-type, so they cannot be interpreted. This script overrides it by using Yahoo! Query Language. | ||
If you try to open raw version of any HTML, CSS or JS file in a web browser directly from GitHub, all you will see is a source code. GitHub forces them to use the "text/plain" content-type, so they cannot be interpreted. This script overrides it by using a CORS proxy. | ||
|
||
In order to use it, just prepend this fragment to the URL of any HTML file: **[http://htmlpreview.github.io/?](http://htmlpreview.github.io/?)** e.g.: | ||
## Usage | ||
|
||
- http://htmlpreview.github.io/?https://github.com/twbs/bootstrap/gh-pages/2.3.2/index.html | ||
- http://htmlpreview.github.io/?https://github.com/documentcloud/backbone/blob/master/examples/todos/index.html | ||
In order to use it, just prepend this fragment to the URL of any HTML file: **[https://htmlpreview.github.io/?](https://htmlpreview.github.io/?)** e.g.: | ||
|
||
What it does is load HTML using YQL, then process all links, frames, scripts and styles, and load each of them using YQL, so they can be evaluted in the browser. Here is the workflow: | ||
``` | ||
HTMLPreview.init() -> HTMLPreview.send(HTML) -> YQL fetch HTML -> HTMLPreview.loadHTML(data) -> HTMLPreview.replaceAssets() -> HTMLPreview.send(CSS) -> YQL fetch CSS -> HTMLPreview.loadCSS(data) -> HTMLPreview.send(JS) -> YQL fetch JS -> HTMLPreview.loadJS(data) | ||
``` | ||
- https://htmlpreview.github.io/?https://github.com/twbs/bootstrap/gh-pages/2.3.2/index.html | ||
- https://htmlpreview.github.io/?https://github.com/documentcloud/backbone/blob/master/examples/todos/index.html | ||
|
||
**GitHub & BitBucket HTML Preview** was tested under Google Chrome, Apple Safari and Mozilla Firefox, and it should work with majority of websites, not only GitHub & BitBucket. | ||
What it does is: load HTML using CORS proxy, then process all links, frames, scripts and styles, and load each of them using CORS proxy, so they can be evaluted by the browser. | ||
|
||
**GitHub & BitBucket HTML Preview** was tested under the latest Google Chrome and Mozilla Firefox. | ||
|
||
## License | ||
|
||
© 2019 Jerzy Głowacki under Apache License 2.0. |