Skip to content

Instantly share code, notes, and snippets.

@martinisoft
Last active December 15, 2015 16:49
Show Gist options
  • Save martinisoft/5292447 to your computer and use it in GitHub Desktop.
Save martinisoft/5292447 to your computer and use it in GitHub Desktop.
My (current) Propane hacks via caveatPatchor.js
/**
Sample Propane caveatPatchor.js file based on tmm1's avatar hack.
You'll need at least version 1.1.1 to experiment with this:
http://propaneapp.com/appcast/Propane.1.1.1.zip
Once I'm sure exposing this hack-injection point doesn't cause problems
I'll do an official auto-updating version.
As of version 1.1.1, Propane will load and execute the contents of
~Library/Application Support/Propane/unsupported/caveatPatchor.js
immediately following the execution of its own enhancer.js file.
Please don't tinker with enhancer.js - it's the glue that binds Campfire's
javascript to Propane's UI and features.
Also: this is totally and utterly unsupported. By all means you can
ask me questions via Propane's help forums but I can't debug your scripts
for you. My javascript isn't that good, trust me.
Finally, if Propane is acting broken in general and you've got hacks in
caveatPatchor.js, *please* tell me about your hacks up-front when opening
an issue on the support site.
*/
var displayAvatars = true;
var displayLibratoMetrics = true;
var displayCloudAppImages = true;
var propaneYouTubeEmbeds = true;
var displaySpeakerDecks = true;
if (displayAvatars) {
Object.extend(Campfire.Message.prototype, {
addAvatar: function() {
if (this.actsLikeTextMessage()) {
var author = this.authorElement();
if (author.visible()) {
author.hide()
if (this.bodyCell.select('strong').length == 0) {
this.bodyCell.insert({top: '<strong>'+this.author()+'</strong><br>'})
author.insert({after: '<img alt="'+this.author()+'" width="32" height="32" align="top" style="margin-left: 5px; border-radius:3px" src="'+author.getAttribute('data-avatar')+'">'});
}
}
}
},
});
/* if you can wrap rather than rewrite, use swizzle like this: */
swizzle(Campfire.Message, {
setAuthorVisibilityInRelationTo: function($super, message) {
$super(message)
this.addAvatar();
},
});
/* defining a new responder is probably the best way to insulate your hacks from Campfire and Propane */
Campfire.AvatarMangler = Class.create({
initialize: function(chat) {
this.chat = chat;
chat.transcript.element.childElements().each(function(elem){
if (elem.match('tr')) {
var msg = new Campfire.Message(chat, elem)
msg.addAvatar()
}
})
this.chat.layoutmanager.layout();
this.chat.windowmanager.scrollToBottom();
},
onMessagesInserted: function(messages) {
var scrolledToBottom = this.chat.windowmanager.isScrolledToBottom();
for (var i = 0; i < messages.length; i++) {
var message = messages[i];
message.addAvatar();
}
if (scrolledToBottom)
this.chat.windowmanager.scrollToBottom();
},
});
/* Here is how to install your responder into the running chat */
Campfire.Responders.push("AvatarMangler");
window.chat.installPropaneResponder("AvatarMangler", "avatarmangler");
}
// Propane extension to expand Librato instrument links
if (displayLibratoMetrics) {
Campfire.LibratoExpander = Class.create({
initialize: function(chat) {
this.chat = chat;
var messages = this.chat.transcript.messages;
for (var i = 0; i < messages.length; i++) {
this.detectLibratoURL(messages[i]);
}
this.chat.windowmanager.scrollToBottom();
},
detectLibratoURL: function(message) {
if (!message.pending() && message.kind === 'text') {
var height = 400;
//var logs = message.bodyElement().select('a[href*="0.0.0.0:3000"]');
var logs = message.bodyElement().select('a[href*="metrics.librato.com"]');
// Bail unless there are logs
if (logs.length != 1) {
return;
}
// determine if this is a librato URL and parse out necessary
var elem = logs[0];
var href = elem.getAttribute('href');
//var regex = /^http:\/\/0.0.0.0:3000\/instruments\/(\d+)(\?.*)?$/;
var regex = /^https:\/\/metrics.librato.com\/instruments\/(\d+)(\?.*)?$/;
var match = href.match(regex);
// Bail unless there is a match
if (!match) {
return;
}
elem.onclick = (function(ev){
ev.preventDefault();
// Create the embed URL
var append_char = '?';
if (ev.target.href.match(/\?/) != null){
append_char = '&';
}
var embed_url = ev.target.href;
/// XXX: There's a bug in campfire, where URL's that end in '*'
// (e.g. https://metrics.librato.com/instruments/123?source=*foo*)
// get split into two elements, try to detect that and repair here.
//
// Have also been informed by 37S that this bug is currently
// not going to be fixed:
//
// "Right now, Campfire's URL detection isn't 100% perfect, so it breaks off at asterisks,
// like you noticed. When we tried updating that, overall Campfire
// performance went waaaaay down. Until we're able to fix this
// legitimately, I would recommend to use http://bitly.com to shorten
// your URLs."
// -37Signals Support
//
if(this.nextSibling != null) {
if (this.nextSibling.data == '*' || this.nextSibling.data == '=*') {
embed_url += this.nextSibling.data
}
}
embed_url = embed_url + append_char + "iframe=1";
var iframes = message.bodyElement().select('iframe');
if (iframes.length == 1) {
iframes[0].remove();
} else {
message.bodyElement().insert({bottom:"<iframe name='librato' style='border:0; margin-top: 5px;' height='"+height+"' width='98%' src='"+embed_url+"'></iframe>"});
setTimeout((function(message) {
return function() {
var iframes = message.bodyElement().select('iframe');
if (iframes.length == 1) {
iframes[0].remove();
message.bodyElement().insert({bottom:"<span> ... embed timed out.</span>"});
}
}
})(message), 1800000);
}
});
}
},
onMessagesInsertedBeforeDisplay: function(messages) {
var scrolledToBottom = this.chat.windowmanager.isScrolledToBottom();
for (var i = 0; i < messages.length; i++) {
this.detectLibratoURL(messages[i]);
}
if (scrolledToBottom) {
this.chat.windowmanager.scrollToBottom();
}
},
onMessageAccepted: function(message, messageID) {
this.detectLibratoURL(message);
}
});
Campfire.Responders.push("LibratoExpander");
window.chat.installPropaneResponder("LibratoExpander", "LibratoExpander");
}
/*
Display CloudApp images inline.
This responder illustrates using Propane's requestJSON service to request
JSON from remote (non-authenticated) servers and have the results passed
to a callback of your choosing.
*/
if (displayCloudAppImages) {
Campfire.CloudAppExpander = Class.create({
initialize: function(chat) {
this.chat = chat;
var messages = this.chat.transcript.messages;
for (var i = 0; i < messages.length; i++) {
this.detectCloudAppURL(messages[i]);
}
},
detectCloudAppURL: function(message) {
/* we are going to use the messageID to uniquely identify our requestJSON request
so we don't check pending messages */
if (!message.pending() && message.kind === 'text') {
var links = message.bodyElement().select('a:not(image)');
if (links.length != 1) {
return; }
var href = links[0].getAttribute('href');
var match = href.match(/^https?:\/\/cl.ly\/[A-Za-z0-9]+\/?$/);
if (!match) return;
window.propane.requestJSON(message.id(), href, 'window.chat.cloudappexpander', 'onEmbedDataLoaded', 'onEmbedDataFailed');
}
},
onEmbedDataLoaded: function(messageID, data) {
var message = window.chat.transcript.getMessageById(messageID);
if (!message) return;
if (data['item_type'] === 'image') {
var imageURL = data['content_url'];
message.resize((function() {
message.bodyCell.insert({bottom: '<div style="width:100%; margin-top:5px; padding-top: 5px; border-top:1px dotted #ccc;"><a href="'+imageURL+'" class="image loading" target="_blank">' + '<img src="'+imageURL+'" onload="$dispatch(&quot;inlineImageLoaded&quot;, this)" onerror="$dispatch(&quot;inlineImageLoadFailed&quot;, this)" /></a></div>'});
}).bind(this));
}
},
onEmbedDataFailed: function(messageID) {
/* No cleanup required, we only alter the HTML after we get back a succesful load from the data */
},
onMessagesInsertedBeforeDisplay: function(messages) {
for (var i = 0; i < messages.length; i++) {
this.detectCloudAppURL(messages[i]);
}
},
onMessageAccepted: function(message, messageID) {
this.detectCloudAppURL(message);
}
});
Campfire.Responders.push("CloudAppExpander");
window.chat.installPropaneResponder("CloudAppExpander", "cloudappexpander");
}
if (propaneYouTubeEmbeds) {
Campfire.YouTube = Class.create({
initialize: function(chat) {
this.chat = chat;
var messages = this.chat.transcript.messages;
for (var i = 0; i < messages.length; i++) {
this.detectYouTubeURL(messages[i]);
}
},
detectYouTubeURL: function(message) {
if (!message.pending() && message.kind === 'text') {
if(message.bodyElement().innerHTML.match(/http:\/\/www\.youtube\.com\//)){
setTimeout(function(){
var links = message.bodyElement().select('a');
if (!links.length) {
return;
}
var href = links[0].getAttribute('href');
var vid_id = href.split("v=")[1].split(/\&amp;|\&|,|%2C/)[0];
message.bodyElement().innerHTML = '<iframe width="420" height="315" src="http://www.youtube.com/embed/'+ vid_id+ '" frameborder="0" allowfullscreen></iframe>' ;
},300);
}
}
},
onMessagesInsertedBeforeDisplay: function(messages) {
for (var i = 0; i < messages.length; i++) {
this.detectYouTubeURL(messages[i]);
}
},
onMessageAccepted: function(message, messageID) {
this.detectYouTubeURL(message);
}
});
Campfire.Responders.push("YouTube");
window.chat.installPropaneResponder("YouTube", "youtube");
}
if (displaySpeakerDecks) {
Campfire.SpeakerDeckExpander = Class.create({
initialize: function(chat) {
this.chat = chat;
var messages = this.chat.transcript.messages;
for (var i = 0; i < messages.length; i++) {
this.detectSpeakerDeckURL(messages[i]);
}
},
detectSpeakerDeckURL: function(message) {
/* we are going to use the messageID to uniquely identify our requestJSON request
so we don't check pending messages */
if (!message.pending() && message.kind === 'text') {
var links = message.bodyElement().select('a:not(image)');
if (links.length != 1) {
return;
}
var href = links[0].getAttribute('href');
var match = href.match(/^https?:\/\/speakerdeck.com\/[A-Za-z0-9-]+\/[A-Za-z0-9-]+\/?$/);
if (!match) return;
window.propane.requestJSON(message.id(), 'https://speakerdeck.com/oembed.json?url=' + href, 'window.chat.speakerdeckexpander', 'onEmbedDataLoaded', 'onEmbedDataFailed');
}
},
onEmbedDataLoaded: function(messageID, data) {
var message = window.chat.transcript.getMessageById(messageID);
if (!message) return;
if (data['html']) {
var resized = data['html'].replace(/width="[0-9]+"/, 'width="320"').replace(/height="[0-9]+"/, 'height="302"');
message.resize((function() {
message.bodyCell.insert({bottom: resized});
}).bind(this));
}
},
onEmbedDataFailed: function(messageID) {
/* No cleanup required, we only alter the HTML after we get back a succesful load from the data */
},
onMessagesInsertedBeforeDisplay: function(messages) {
for (var i = 0; i < messages.length; i++) {
this.detectSpeakerDeckURL(messages[i]);
}
},
onMessageAccepted: function(message, messageID) {
this.detectSpeakerDeckURL(message);
}
});
Campfire.Responders.push("SpeakerDeckExpander");
window.chat.installPropaneResponder("SpeakerDeckExpander", "speakerdeckexpander");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment