Skip to content

Commit 3dcaa87

Browse files
author
John Doherty
committed
first commit with example and basic docs
1 parent c66a438 commit 3dcaa87

File tree

9 files changed

+302
-0
lines changed

9 files changed

+302
-0
lines changed

LICENSE

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright (c) 2016, John Doherty <[email protected]>
2+
3+
Permission to use, copy, modify, and/or distribute this software for any
4+
purpose with or without fee is hereby granted, provided that the above
5+
copyright notice and this permission notice appear in all copies.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

README.MD

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# node-iframe-replacement
2+
3+
Imagine the scenario: you've built a lovely web app for a business, who later insists it goes live within an iframe of their corp website. You resist, but you're ultimatley forced to bastardize your app to make it work.
4+
5+
[node-iframe-replacement](https://github.com/john-doherty/node-iframe-replacement) is the compromise. A small node server that grabs the source code of an external website and injects your content - thus allowing you to host your app on a subdomain and without an iframe. Changes to the external site _(navigation etc)_ are automatically picked up every 5 minutes and reflected in your app.
6+
7+
You can now argue that you're web app can remain synced with the corp website without an iframe.
8+
9+
10+
## Installation
11+
12+
```bash
13+
$ npm install --save node-iframe-replacement
14+
```
15+
16+
## Example usage
17+
18+
The example below will request the [BBC News](http://www.bbc.co.uk/news) home page and replace the contents of **div[data-entityid="container-top-stories#1"]** with the contents of the view
19+
20+
```js
21+
var iframeReplacement = require('node-iframe-replacement');
22+
23+
// replace res.render with iframe replacement version
24+
app.use(iframeReplacement);
25+
26+
// create a regular express route
27+
app.get('/', function(req, res){
28+
29+
// respond to this request with our fake-new content embedded within the BBC News home page
30+
res.merge('fake-news', {
31+
sourceUrl: 'http://www.bbc.co.uk/news', // external url to fetch
32+
sourcePlaceholder: 'div[data-entityid="container-top-stories#1"]' // css selector to inject our content into
33+
});
34+
});
35+
```
36+
37+
## fake-news html
38+
39+
```html
40+
<div style="margin: 0 0 0 20px; overflow: auto;">
41+
<div style="width: 210px; float: left;">
42+
<a href="https://github.com/john-doherty/node-iframe-replacement" target="_blank">
43+
<h3 style="color: #222; font-size: 32px; font-weight: bold; margin: 0 0 10px 0;">iframe replacement Node.js module lanuched</h3>
44+
</a>
45+
<p style="padding: 0; font-size: 14px;line-height: 22px">
46+
Open source project not only has the potential to replace iframes, but also brings a whole new meaning to the concept of fake news.
47+
</p>
48+
</div>
49+
<img src="https://cdn.colorlib.com/wp/wp-content/uploads/sites/2/nodejs-frameworks.png" alt="Node js logo" style="width: 500px; float: right;"/>
50+
</div>
51+
```
52+
53+
## Input
54+
55+
![alt text](docs/bbc-news-actual-homepage.png "BBC News actual homepage")
56+
57+
## Output
58+
59+
![alt text](docs/bbc-news-fake-homepage.png "BBC News actual homepage")
60+
61+
## Auto base tag injection
62+
63+
To ensure the source scripts, styles and images load witin your site a base tag is automatically injected ```<base href="http://www.bbc.co.uk/news" />```.
64+
65+
Because of this, you need to ensure all the links within your conent are absolute.
66+
67+
## Note on Patches/Pull Requests
68+
69+
* Fork the project
70+
* Make your feature addition or bug fix
71+
* Send me a pull request
72+
73+
## License
74+
75+
[ISC License](LICENSE) &copy; 2016 [John Doherty](https://twitter.com/CambridgeMVP)

docs/bbc-news-actual-homepage.png

405 KB
Loading

docs/bbc-news-fake-homepage.png

281 KB
Loading

example/server.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
var express = require('express'),
2+
handlebars = require('express-handlebars'),
3+
iframeReplacement = require('../index.js');
4+
5+
function Server() {
6+
7+
// create an instance of express
8+
var app = express();
9+
10+
// add iframe replacement to express as middleware (adds res.merge method)
11+
app.use(iframeReplacement);
12+
13+
// add handlebars view engine (you can use any)
14+
app.engine('.hbs', handlebars.create({
15+
extname: '.hbs'
16+
}).engine);
17+
18+
// let express know how to locate the views/templates
19+
app.set('views', './example/views');
20+
app.set('view engine', 'hbs');
21+
22+
// create simple route to test our fake news
23+
app.get('/', function(req, res){
24+
25+
// respond to this request with our fake-new content embedded within the BBC News home page
26+
res.merge('fake-news', {
27+
sourceUrl: 'http://www.bbc.co.uk/news', // external url to fetch
28+
sourcePlaceholder: 'div[data-entityid="container-top-stories#1"]' // css selector to inject our content into
29+
});
30+
});
31+
32+
// start the server
33+
app.listen(8080);
34+
}
35+
36+
module.exports = new Server();

example/views/fake-news.hbs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<div style="margin: 0 0 0 20px; overflow: auto;">
2+
<div style="width: 210px; float: left;">
3+
<a href="https://github.com/john-doherty/node-iframe-replacement" target="_blank">
4+
<h3 style="color: #222; font-size: 32px; font-weight: bold; margin: 0 0 10px 0;">iframe replacement Node.js module lanuched</h3>
5+
</a>
6+
<p style="padding: 0; font-size: 14px;line-height: 22px">
7+
Open source project not only has the potential to replace iframes, but also brings a whole new meaning to the concept of fake news.
8+
</p>
9+
</div>
10+
<img src="https://cdn.colorlib.com/wp/wp-content/uploads/sites/2/nodejs-frameworks.png" alt="Node js logo" style="width: 500px; float: right;"/>
11+
</div>

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./lib/node-iframe-replacement.js');

lib/node-iframe-replacement.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
'use strict';
2+
3+
var cheerio = require('cheerio'), // used to convert raw html into jQuery style object to we can use css selectors
4+
request = require('request-promise'), // promise based request object
5+
NodeCache = require( "node-cache" ), // auto expiring in memory caching collection
6+
cache = new NodeCache({ stdTTL: 300 }); // cache source HTML for 5 minutes, after which we fetch a new version
7+
8+
// override res.render to allow us to fetch template url or use html template string
9+
module.exports = function(req, res, next){
10+
11+
// replace the standard res.render function so we can intercept response rendering
12+
res.merge = function(view, model, callback){
13+
14+
if (!model.sourceUrl){
15+
// no .sourceUrl, therefore process this as normal
16+
res.render(view, model, callback);
17+
}
18+
else {
19+
20+
// if no template selector provided, use body tag
21+
var sourcePlaceholder = model.sourcePlaceholder || 'body';
22+
23+
// resolve the template, either url to jquery object or html
24+
resolveTemplate(model.sourceUrl).then(function($template){
25+
26+
// if a transform method is provided, execute
27+
if (model.transform){
28+
// pass a jquery version of the html along with a copy of the model
29+
model.transform($template, model);
30+
}
31+
32+
// convert view into html to be inserted
33+
res.render(view, model, function(err, html) {
34+
if (err) next(err);
35+
36+
// convert view into jquery object
37+
var $view = cheerio.load(html);
38+
39+
// if the view contains a head, append its contents to the original
40+
var $viewHead = $view('head');
41+
if ($viewHead && $viewHead.length>0){
42+
// append meta, link, script and noscript to head
43+
$template('head').append($viewHead.html());
44+
}
45+
46+
// if the view has a body, use its contents otherwise use the entire content
47+
var $viewBody = $view('body');
48+
if ($viewBody && $viewBody.length>0){
49+
// inject content into selector or body
50+
$template(sourcePlaceholder).html($viewBody.html());
51+
}
52+
else {
53+
// no body tag in view, insert all content
54+
$template(sourcePlaceholder).html($view.html());
55+
}
56+
57+
// return merged content
58+
res.status(200).send($template.html());
59+
});
60+
})
61+
.catch(function(err){
62+
// request failed, inform the user
63+
res.status(500).send(err).end();
64+
});
65+
}
66+
};
67+
68+
next();
69+
}
70+
71+
/**
72+
* Converts template url to $ object
73+
* @param {string} template - url to external template or html content
74+
*/
75+
function resolveTemplate(template){
76+
77+
return new Promise(function(resolve, reject){
78+
79+
// if its a url and we have the contents in cache
80+
if (template.isUrl() && cache.get(template)){
81+
82+
// get template html from cache
83+
var html = cache.get(template);
84+
85+
// covert html into jquery object
86+
var $ = cheerio.load(html);
87+
88+
// return template as a jquery style object
89+
resolve($);
90+
}
91+
else if (template.isUrl()){
92+
93+
// request the template url
94+
return request({ uri: template }).then(function(html){
95+
96+
// convert html into jquery style object so we can use selectors
97+
var $ = cheerio.load(html);
98+
99+
// add base url to ensure links/scripts/styles load correctly
100+
$('head').prepend('<base href="' + template + '">');
101+
102+
// cache result as HTML so we dont have to keep getting it for future requests and it remains clean
103+
cache.set(template, $.html());
104+
105+
// resolve with jquery object containing content
106+
resolve($);
107+
})
108+
.catch(function(err) {
109+
110+
// request failed
111+
reject('Unable to retrieve ' + template);
112+
});
113+
}
114+
else {
115+
// the template must contain markup, just return it
116+
resolve(template);
117+
}
118+
});
119+
}
120+
121+
// helper function to allow us to determine if the template is a url
122+
String.prototype.isUrl = function(){
123+
return /^http[s]?:\/\/([\w-]+\.)+[\w-]+([\w-./?%&=]*)?$/i.test(this);
124+
}

package.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "node-iframe-replacement",
3+
"version": "0.0.1",
4+
"description": "NodeJS + Express replacement for the HTML iframe",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified yet!\" && exit 1"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git://github.com/john-doherty/node-iframe-replacement.git"
12+
},
13+
"keywords": [
14+
"node",
15+
"iframe",
16+
"replacement",
17+
"express",
18+
"alternative"
19+
],
20+
"author": {
21+
"name": "John Doherty",
22+
"email": "[email protected]",
23+
"url": "https://twitter.com/CambridgeMVP"
24+
},
25+
"license": "ISC",
26+
"bugs": {
27+
"url": "https://github.com/john-doherty/node-iframe-replacement/issues"
28+
},
29+
"homepage": "https://github.com/john-doherty/node-iframe-replacement#readme",
30+
"engines": {
31+
"node": ">= 6.9.2"
32+
},
33+
"dependencies": {
34+
"cheerio": "^0.20.0",
35+
"node-cache": "^3.2.1",
36+
"request-promise": "^3.0.0"
37+
},
38+
"devDependencies": {
39+
"express": "^4.14.0",
40+
"express-handlebars": "^3.0.0"
41+
}
42+
}

0 commit comments

Comments
 (0)