Add support for oembeds

This commit is contained in:
Lance Stout 2014-01-01 16:24:11 -08:00
parent e09290e623
commit 9369b00665
13 changed files with 235 additions and 8771 deletions

View File

@ -0,0 +1,55 @@
/*global $, app*/
var _ = require('underscore');
var async = require('async');
var templates = require('../templates');
var embedQueue = async.cargo(function (links, cb) {
var urls = [];
_.each(links, function (link) {
urls.push({
value: link.href,
el: link
});
});
$.ajax({
// We have to massage the data into the URL ourselves because
// jQuery won't let us have unencoded commas between encoded URLs
url: '/oembed?' + $.param({
maxwidth: 500
}) + '&urls=' + _.map(urls, function (item) { return encodeURIComponent(item.value); }).join(','),
dataType: 'jsonp',
success: function (data) {
var maxWidth = 500;
data = _.filter(data, function (item, i) {
item.original = urls[i].value;
item.el = urls[i].el;
return item.type === 'video' || item.type === 'photo';
});
data.forEach(function (item) {
if (item.width && item.height && item.width > maxWidth) {
var ratio = maxWidth / item.width;
item.width = maxWidth;
item.height = parseInt(item.height * ratio, 10);
}
$(item.el).replaceWith(templates.includes.embeds(item));
});
}
});
}, 10);
module.exports = function ($html, cb) {
cb = cb || function () {};
//if (!app.settings.chatEmbeds) return cb();
if (!$html.jquery) cb('$html is not a jQuery collection.');
var $links;
var batches = [];
var allUrls = [];
var selector = 'a[target="_blank"]:not(".original")';
$links = $html.find(selector);
if (!$links.length) $links = $html.filter(selector);
$links.each(function (idx, link) {
embedQueue.push(link);
});
};

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,7 @@ var BasePage = require('./base');
var templates = require('../templates');
var Message = require('../views/message');
var MessageModel = require('../models/message');
var embedIt = require('../helpers/embedIt');
var attachMediaStream = require('attachmediastream');
@ -210,10 +211,12 @@ module.exports = BasePage.extend({
last = this.$messageList.find('li').last();
last.find('.messageWrapper').append(newEl);
last.addClass('chatGroup');
this.staydown.checkdown();
} else {
newEl = $(model.templateHtml);
this.staydown.append(newEl[0]);
}
embedIt(newEl);
this.lastModel = model;
},
handleEndClick: function (e) {

View File

@ -8,6 +8,7 @@ var templates = require('../templates');
var MUCRosterItem = require('../views/mucRosterItem');
var Message = require('../views/mucMessage');
var MessageModel = require('../models/message');
var embedIt = require('../helpers/embedIt');
module.exports = BasePage.extend({
@ -213,10 +214,12 @@ module.exports = BasePage.extend({
last = this.$messageList.find('li').last();
last.find('.messageWrapper').append(newEl);
last.addClass('chatGroup');
this.staydown.checkdown();
} else {
newEl = $(model.templateHtml);
this.staydown.append(newEl[0]);
}
embedIt(newEl);
this.lastModel = model;
},
refreshModel: function (model) {

View File

@ -83,6 +83,69 @@ exports.includes.contactRequest = function anonymous(locals) {
return buf.join("");
};
// embeds.jade compiled template
exports.includes.embeds = function anonymous(locals) {
var buf = [];
with (locals || {}) {
if (locals.type === "photo") {
buf.push("<a" + jade.attrs({
href: locals.original,
target: "_blank",
"class": "embed" + " " + "photo"
}, {
href: true,
target: true
}) + "><img" + jade.attrs({
width: locals.width,
height: locals.height,
src: locals.url,
alt: locals.title,
"class": "embedded"
}, {
width: true,
height: true,
src: true,
alt: true
}) + "/></a><br/><a" + jade.attrs({
href: locals.original,
target: "_blank",
"class": "embed" + " " + "original"
}, {
href: true,
target: true
}) + ">" + jade.escape(null == (jade.interp = locals.original) ? "" : jade.interp) + "</a>");
} else if (locals.type === "video" && locals.thumbnail_url) {
buf.push("<a" + jade.attrs({
href: locals.original,
target: "_blank",
"class": "embed" + " " + "preview"
}, {
href: true,
target: true
}) + "><img" + jade.attrs({
width: locals.width,
height: locals.height,
src: locals.thumbnail_url,
alt: locals.title,
"class": "embedded"
}, {
width: true,
height: true,
src: true,
alt: true
}) + "/></a><br/><a" + jade.attrs({
href: locals.original,
target: "_blank",
"class": "embed" + " " + "original"
}, {
href: true,
target: true
}) + ">" + jade.escape(null == (jade.interp = locals.original) ? "" : jade.interp) + "</a>");
}
}
return buf.join("");
};
// message.jade compiled template
exports.includes.message = function anonymous(locals) {
var buf = [];

View File

@ -0,0 +1,10 @@
- if (locals.type === 'photo')
a.embed.photo(href=locals.original, target="_blank")
img.embedded(width=locals.width, height=locals.height, src=locals.url, alt=locals.title)
br
a.embed.original(href=locals.original, target="_blank")= locals.original
- else if (locals.type === 'video' && locals.thumbnail_url)
a.embed.preview(href=locals.original, target="_blank")
img.embedded(width=locals.width, height=locals.height, src=locals.thumbnail_url, alt=locals.title);
br
a.embed.original(href=locals.original, target="_blank")= locals.original

View File

@ -16,6 +16,7 @@ module.exports = HumanView.extend({
},
events: {
'click a[href]': 'handleLinkClick',
'click a.embed img': 'handleEmbedClick',
'click .reconnect': 'handleReconnect',
'click .logout': 'handleLogout',
'blur #me .status': 'handleStatusChange'
@ -57,6 +58,12 @@ module.exports = HumanView.extend({
return false;
}
},
handleEmbedClick: function (e) {
if (e.shiftKey) {
e.preventDefault();
$(e.target).hide();
}
},
handleTitle: function (e) {
document.title = app.state.title;
app.desktop.updateBadge(app.state.badge);

View File

@ -6,6 +6,9 @@
"key": "./fakekeys/privatekey.pem",
"cert": "./fakekeys/certificate.pem"
},
"embedly": {
"key": ""
},
"session": {
"secret": "shhhhhh don't tell anyone ok?"
}

View File

@ -7,29 +7,30 @@
"url": "git@github.com:andyet/otalk.git"
},
"dependencies": {
"bows": "0.3.0",
"async": "0.2.9",
"attachmediastream": "1.0.1",
"backbone": "1.0.0",
"bows": "0.3.0",
"browserify": "2.25.1",
"crypto-browserify": "1.0.3",
"express": "3.3.7",
"getconfig": "0.0.5",
"jade": "0.35.0",
"moonboots": "1.0.0",
"getusermedia": "0.2.1",
"helmet": "0.1.0",
"node-uuid": "1.4.1",
"semi-static": "0.0.4",
"sound-effect-manager": "0.0.5",
"human-model": "1.4.0",
"human-view": "1.2.0",
"jade": "0.35.0",
"moonboots": "1.0.0",
"node-uuid": "1.4.1",
"notify.js": "0.0.3",
"oembed": "0.1.0",
"semi-static": "0.0.4",
"sound-effect-manager": "0.0.5",
"stanza.io": "2.10.0",
"staydown": "legastero/staydown",
"templatizer": "0.1.2",
"underscore": "1.5.1",
"stanza.io": "2.10.0",
"notify.js": "0.0.3",
"wildemitter": "0.0.5",
"attachmediastream": "1.0.1",
"getusermedia": "0.2.1",
"crypto-browserify": "1.0.3",
"browserify": "2.25.1",
"staydown": "1.0.2"
"wildemitter": "0.0.5"
},
"devDependencies": {
"precommit-hook": "0.3.6"

View File

@ -1112,6 +1112,16 @@ button.secondary:hover:not(:disabled) {
position: absolute;
width: 1px;
}
.messages li p a {
display: inline;
word-wrap: break-word;
word-break: break-all;
}
.messages li p img {
margin: 5px 0;
border-radius: 5px;
box-shadow: rgba(0,0,0,0.25) 0 2px 3px;
}
.messages .sender {
display: table-cell;
font-size: 12px;

View File

@ -219,6 +219,16 @@
position: absolute
width: 1px
p a
display: inline
word-wrap: break-word
word-break: break-all
p img
margin: 5px 0
border-radius: 5px
box-shadow: rgba(0, 0, 0, .25) 0 2px 3px
.sender
display: table-cell
font-size: 12px

View File

@ -1,5 +1,5 @@
CACHE MANIFEST
# 0.0.1 1388608475000
# 0.0.1 1388621917445
CACHE:
/app.js

View File

@ -5,6 +5,8 @@ var helmet = require('helmet');
var Moonboots = require('moonboots');
var config = require('getconfig');
var templatizer = require('templatizer');
var oembed = require('oembed');
var async = require('async');
var app = express();
@ -17,13 +19,15 @@ if (!config.isDev) {
app.use(helmet.iexss());
app.use(helmet.contentTypeOptions());
oembed.EMBEDLY_URL = config.embedly.url;
oembed.EMBEDLY_KEY = config.embedly.key;
var clientApp = new Moonboots({
main: __dirname + '/clientapp/app.js',
templateFile: __dirname + '/clientapp/templates/main.html',
developmentMode: config.isDev,
libraries: [
__dirname + '/clientapp/libraries/zepto.js',
__dirname + '/clientapp/libraries/jquery.js',
__dirname + '/clientapp/libraries/ui.js',
__dirname + '/clientapp/libraries/resampler.js',
__dirname + '/clientapp/libraries/IndexedDBShim.min.js'
@ -86,6 +90,52 @@ app.get('/manifest.cache', function (req, res, next) {
});
});
app.get('/oembed', function (req, res) {
var callback = req.query.callback;
if (req.query.url) {
oembed.fetch(req.query.url, req.query, function (err, result) {
if (err || !result) {
return res.status(500).send();
}
res.status(200);
res.set('Content-Type', oembed.MIME_OEMBED_JSON);
if (callback) {
res.send(callback + '(' + JSON.stringify(result) + ')');
} else {
res.send(JSON.stringify(result));
}
});
} else if (req.query.urls) {
var cache = {};
var urls = req.query.urls.split(',');
delete req.query.urls;
async.forEach(urls, function (url, cb) {
oembed.fetch(url, req.query, function (err, result) {
if (err || !result) {
result = {type: 'error'};
}
cache[url] = result;
cb();
});
}, function () {
res.status(200);
var results = [];
urls.forEach(function (url) {
results.push(cache[url]);
});
if (callback) {
res.set('Content-Type', 'application/javascript');
res.send(callback + '(' + JSON.stringify(results) + ')');
} else {
res.set('Content-Type', 'application/json');
res.send(JSON.stringify(results));
}
});
} else {
res.status(400).send();
}
});
// serves app on every other url
app.get('*', clientApp.html());