diff --git a/htmlpreview.js b/htmlpreview.js index 6f95205..a38e616 100644 --- a/htmlpreview.js +++ b/htmlpreview.js @@ -1,116 +1,114 @@ -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.github.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.github.com') > 0 || src.indexOf('//bitbucket.org') > 0) { //Check if it's from raw.github.com or bitbucket.org - frame[i].src = 'http://' + 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 - a[i].href = 'http://' + location.hostname + location.pathname + location.search + '#' + a[i].hash.substring(1); //Then rewrite URL with support for empty anchor - } - else if(href.indexOf('//raw.github.com') > 0 || href.indexOf('//bitbucket.org') > 0) { //Check if it's from raw.github.com or bitbucket.org - a[i].href = 'http://' + location.hostname + location.pathname + '?' + href; //Then rewrite URL so it can be loaded using YQL + 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 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.github.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 } } - script = document.querySelectorAll('script'); - for(i = 0; i < script.length; ++i) { - src = script[i].src; //Get absolute URL - if(src.indexOf('//raw.github.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 + Promise.all(links).then(function (res) { + for (i = 0; i < res.length; ++i) { + loadCSS(res[i]); } - else if(!src && script[i].innerHTML.indexOf('HTMLPreview') < 0) { //Move all inline scripts except HTMLPreview.replaceAssets() - document.write(script[i].outerHTML); + }); + //Scripts + script = document.querySelectorAll('script[type="text/htmlpreview"]'); + 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 + scripts.push(fetchProxy(src, null, 0)); //Then add it to scripts queue and fetch using CORS proxy + } else { + script[i].removeAttribute('type'); + 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]); + } + document.dispatchEvent(new Event('DOMContentLoaded', {bubbles: true, cancelable: true})); //Dispatch DOMContentLoaded event after loading all scripts + }); + }; - loadHTML: function(data) { - 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(//i, '').replace(/<\/body>/i, '').replace(/<\/head>\s* just after and inject '); + var loadJS = function (data) { + if (data) { + var script = document.createElement('script'); + script.innerHTML = data; + document.body.appendChild(script); } - }, - - send: function(file, callback) { - document.write(''); //Get content using YQL - }, + }; + + var fetchProxy = function (url, options, i) { + var proxy = [ + '', // try without proxy first + 'https://api.codetabs.com/v1/proxy/?quest=' + ]; + 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 = '

Loading...

'; - HTMLPreview.send(HTMLPreview.raw(), 'loadHTML'); - } - } -} +})() diff --git a/htmlpreview.min.js b/htmlpreview.min.js deleted file mode 100644 index 16b3f23..0000000 --- a/htmlpreview.min.js +++ /dev/null @@ -1 +0,0 @@ -var HTMLPreview={content:"",previewform:document.getElementById("previewform"),file:function(){return location.search.substring(1)},raw:function(){return HTMLPreview.file().replace(/\/\/github\.com/,"//raw.github.com").replace(/\/blob\//,"/")},replaceAssets:function(){var a,b,c;a=document.querySelectorAll("iframe[src],frame[src]");for(b=0;ba[b].innerHTML.indexOf("HTMLPreview")&&document.write(a[b].outerHTML)}},loadHTML:function(a){a&&a.query&&a.query.results&&a.query.results.resources&&a.query.results.resources.content&&200==a.query.results.resources.status?(HTMLPreview.content=a.query.results.resources.content.replace(//i,'').replace(/<\/body>/i,' -

-

Enter the HTML file to preview:

-

or prepend to the URL: http://htmlpreview.github.io/?https://raw.github.com/twbs/bootstrap/gh-pages/2.3.2/index.html

-

or use this bookmarklet while browsing GitHub or BitBucket: HTMLPreview

+

+ Enter URL of the HTML file to preview: + + +

+

or prepend to the URL: http://htmlpreview.github.io/?https://github.com/twbs/bootstrap/blob/gh-pages/2.3.2/index.html

- - + - \ No newline at end of file + diff --git a/readme.md b/readme.md index ec66bed..3721989 100644 --- a/readme.md +++ b/readme.md @@ -1,19 +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/?)** +## Usage -E.g.: -http://htmlpreview.github.io/?https://raw.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. \ No newline at end of file +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 evaluated 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.