Skip to content

Instantly share code, notes, and snippets.

@nightpool
Last active January 12, 2017 02:35
Show Gist options
  • Save nightpool/2e4cee4458301236c6ae11956cd1c8fe to your computer and use it in GitHub Desktop.
Save nightpool/2e4cee4458301236c6ae11956cd1c8fe to your computer and use it in GitHub Desktop.
//* TITLE Editable Reblogs **//
//* VERSION 3.3.2 **//
//* DESCRIPTION Restores ability to edit previous reblogs of a post **//
//* DEVELOPER new-xkit **//
//* FRAME false **//
//* BETA false **//
XKit.extensions.editable_reblogs = new Object({
running: false,
state: undefined,
post_types: {
PUBLISH: 0,
QUEUE: 1,
DRAFT: 2,
PRIVATE: 3,
SCHEDULE: 4
},
selected_post_type: "PUBLISH",
scheduled_date: "Next Tuesday, 10am",
post_date_metadata: null,
post_slug_metadata: null,
run: function() {
this.running = true;
XKit.interface.post_window_listener.add("editable_reblogs", this.post_window.bind(this));
XKit.tools.init_css("editable_reblogs");
var create_post_button_click_handler = this.make_post.bind(this);
$("body").on("click", ".create_post_button", create_post_button_click_handler);
// DOM nodes containing options disappear before a handler can be run
var record_post_settings_handler = this.record_post_settings.bind(this);
$("body").on("click", record_post_settings_handler);
var post_setting_keyup_handlers = {};
$.each({"#customUrl_input": "post_slug_metadata", "#postDate_input": "post_date_metadata"},
function(selector, property){
post_setting_keyup_handlers[selector] = function() {
XKit.extensions.editable_reblogs[property] = $(this).val();
};
$('body').on("keyup", selector, post_setting_keyup_handlers[selector]);
}
);
this.teardown_event_handlers = function() {
$("body").off("click", ".create_post_button", create_post_button_click_handler);
$("body").off("click", record_post_settings_handler);
$.each(post_setting_keyup_handlers, function(selector, handler){
$('body').off("keyup", selector, handler);
});
};
},
post_window: function() {
this.state = "initial";
if (!this.reblog_tree_exists()) {
return;
}
//also just let chat, link, and quote posts do what they do
var post_type = XKit.interface.post_window.post_type();
if (post_type.chat || post_type.quote) {
return;
}
var has_displayed_ER_warning = XKit.storage.get('editable_reblogs', 'has_displayed_ER_warning', '');
if (!has_displayed_ER_warning) {
XKit.window.show('Editable Reblogs Warning',
"WARNING: Editable Reblogs no longer preserves Tumblr's reblog " +
"structure due to changes on Tumblr's side. Any posts reblogged " +
"with Editable Reblogs will display the entire reblog tree as a " +
"single blockquoted post.<br><br>" +
"The XKit team deeply apologizes for any inconvenience this causes, "+
"and we're working to restore the original functionality, "+
"but Tumblr's updates are in this case beyond our control.<br><br>" +
"As always, this extension can be disabled at any time from the XKit preferences panel.",
'error', '<div id="xkit-close-message" class="xkit-button xkit-er-ack-blockquotes">OK</div>');
$(".xkit-er-ack-blockquotes").click(function(){
XKit.storage.set('editable_reblogs', 'has_displayed_ER_warning', 'true');
});
}
//disable on pages that don't include reblog_key and post_id in the URL
//for now until we've refactored more effectively
var location_path = window.location.pathname;
var location_items = location_path.split("/");
location_items.shift();
if (location_items[0] != "reblog" && location_items[0] != "edit") {
return;
}
this.initialize_selected_post_type();
this.scheduled_date = "Next Tuesday, 10am";
this.load_initial_metadata();
var element = this.add_edit_button();
$(element).one('click', function(){
this.edit_the_reblogs();
$(element).addClass('disabled');
}.bind(this));
},
add_edit_button: function() {
var post_margin = $(".post-form .post-margin");
post_margin.append(
'<div class="xkit-er-edit-button icon_edit_pencil"></div>'
);
if (!XKit.storage.get('editable_reblogs', 'has_dismissed_button_callout', '')) {
post_margin.append(
'<div class="xkit-er-callout--container">' +
'<div class="xkit-er-callout--header">Editable Reblogs</div>' +
'<div class="xkit-er-callout--body">click this button to trim or edit this post</div>' +
'</div>'
);
$('.xkit-er-callout--container').delay(800).slideDown(800);
$('.post-form .editor').one('keyup', function(){
$('.xkit-er-callout--container').delay(2000).slideUp(800);
});
$(".xkit-er-edit-button").click(function(){
XKit.storage.set('editable_reblogs', 'has_dismissed_button_callout', 'true');
$('.xkit-er-callout--container').slideUp(800);
});
$('.xkit-er-callout--container').click(function(){
XKit.storage.set('editable_reblogs', 'has_dismissed_button_callout', 'true');
$('.xkit-er-callout--container').slideUp(800);
});
}
return $('.xkit-er-edit-button')[0];
},
edit_the_reblogs: function() {
var save_button = $('.post-form--save-button [data-js-clickablesave]');
try {
// Prevent Tumblr's event handler from acting on the save button
save_button.removeAttr("data-js-clickablesave");
this.process_existing_content();
this.state = "success";
} catch(e) {
console.error(e);
save_button.attr("data-js-clickablesave", "");
if (!e.hide_popup) {
var github_url = XKit.tools.github_issue("Editable Reblogs "+this.state+" error",
{ state: this.state, "ER Version": XKit.installed.get("editable_reblogs").version },
e);
XKit.window.show('Editable Reblogs Error', 'ERROR: Editable Reblogs failed to proccess some part of your post safely. '+
'Therefore, it has been disabled to prevent unintentional side-effects that could potentially corrupt the post. '+
(!e.hide_url ? '<br><br><a target="_blank" href="'+github_url+'">You can report this issue on Github by clicking here</a>':''),
'error', '<div id="xkit-close-message" class="xkit-button">OK</div>');
}
}
},
load_initial_metadata: function() {
//if this is an edit, we need to load the custom date and slug metadata if there is any
//so we can maintain it if they don't change anything
this.state = "metadata";
this.post_date_metadata = "";
this.post_slug_metadata = "";
var location_path = window.location.pathname;
var location_items = location_path.split("/");
location_items.shift();
if (location_items[0] != "edit") {
return;
}
var post_fetch_request = {
id: parseInt(location_items[1]),
form_key: XKit.interface.form_key(),
post_type: false
};
XKit.interface.fetch(post_fetch_request, function(response) {
this.post_date_metadata = response.data.post.date;
this.post_slug_metadata = response.data.post.slug;
if (response.data.post.is_private === 1) {
this.selected_post_type = "PRIVATE";
}
}.bind(this));
},
process_existing_content: function() {
var reblog_tree = $(".post-form .reblog-list");
var all_quotes = [];
var old_content = '';
var all_quotes_text = '';
this.state = "processing existing content";
// Guard against double evaluation by marking the tree as processed
var processed_class = 'xkit-editable-reblogs-done';
if (reblog_tree.length > 0) {
this.state = "tree processing";
if (reblog_tree.hasClass(processed_class)) {
return;
}
reblog_tree.addClass(processed_class);
this.state = "processing reblog items";
reblog_tree.find(".reblog-list-item").each(function(index) {
var reblog_data = {
reblog_content: $(this).find('.reblog-content').html() ? $(this).find('.reblog-content').html() : '',
reblog_author: $(this).find('.reblog-tumblelog-name').text() ? $(this).find('.reblog-tumblelog-name').text().trim() : '',
reblog_url: $(this).find('.reblog-tumblelog-name').attr('href')
? $(this).find('.reblog-tumblelog-name').attr('href').trim()
: 'http://' + $(this).find('.reblog-tumblelog-name').text().trim() + '.tumblr.com/post/1/undefined'
};
all_quotes.push(reblog_data);
});
all_quotes.forEach(function(data, index, all) {
var reblog_content = data.reblog_content.replace("tmblr-truncated read_more_container", "");
if (!all_quotes_text && this.is_blockquote_reblog(data.reblog_content)) {
all_quotes_text = reblog_content;
} else {
all_quotes_text = "<p><a href='" + data.reblog_url + "'>" + data.reblog_author + "</a>:</p>"+
"<xkit></xkit>"+
"<blockquote>" + all_quotes_text + reblog_content + "</blockquote>";
}
}.bind(this));
}
try {
old_content = XKit.interface.post_window.get_content_html();
} catch (e) {
XKit.window.show('Invalid editor type', 'ERROR: Editable Reblogs cannot currently get content from your default editor type. '+
'To continue using editable reblogs, click <a target="_blank" href="https://www.tumblr.com/settings/dashboard">here</a> '+
'to edit your dashboard settings to use the rich text editor or HTML editor',
'error', "<div id=\"xkit-close-message\" class=\"xkit-button\">OK</div>");
var error = new Error("Editor Type");
error.hide_popup = true;
throw error;
}
this.state = "post processing";
// add 'tumblr_blog' class to all tumblr.com links,
// assuming that they're part of a reblog-structure that's not being parsed properly
var nodes = $(all_quotes_text + old_content);
// nodes.find('a[href*="tumblr.com"]').addClass('tumblr_blog');
var nodes_text = $('<div>').append($(nodes).clone()).html();
// var undefined_urls = nodes_text.match(new RegExp("<a .*post/1/undefined", "g")) || [];
// if (undefined_urls.length > 1) {
// var error = new Error("Too many bad urls");
// error.hide_url = true;
// throw error;
// }
this.state = "mutation";
var title = reblog_tree.find('.reblog-title');
$('.post-form--header').append(title);
XKit.interface.post_window.set_content_html(nodes_text);
$(".btn-remove-trail .icon").click();
$(".control-reblog-trail").hide();
},
is_blockquote_reblog: function(text) {
var elements = $(text);
elements = elements.filter(function(index, node){
if (!node.innerHTML || node.innerHTML == "<br>") {
return false;
}
return true;
});
if (elements.length != 2) {
return false;
}
var url = elements[0];
var is_url = url.tagName == "P" &&
url.childNodes[0].tagName == "A" &&
url.childNodes[0].href.match("/post/");
var blockquote = elements[1];
var is_blockquote = blockquote.tagName == "BLOCKQUOTE";
return is_url && is_blockquote;
},
reblog_tree_exists: function() {
//if we don't have a reblog tree to edit, gtfo
//this also applies to new posts
//which saves us from worrying about things like photo replies
return $(".post-form .reblog-list").length !== 0;
},
initialize_selected_post_type: function() {
var where = XKit.interface.where();
if (where.dashboard) {
this.selected_post_type = "PUBLISH";
} else if (where.drafts) {
this.selected_post_type = "DRAFT";
} else if (where.queue) {
this.selected_post_type = "QUEUE";
}
},
record_post_settings: function(e) {
var post_dropdown_exists = $(e.target).parents(".popover--save-post-dropdown").length > 0;
if (post_dropdown_exists) {
var clicked_li = e.target.tagName !== "LI" ? e.target.parentNode : e.target;
for (var i = 0; i < clicked_li.attributes.length; i++) {
// looking for attribute like "data-js-publish" and "data-js-draft"
var attribute = clicked_li.attributes[i].name;
if (attribute.startsWith("data-js-") && !attribute.endsWith("preview")) {
var type = attribute.substring(8).toUpperCase();
this.selected_post_type = type;
if (type === "SCHEDULE") {
$("[data-js-scheduletext]").off("blur");
$("[data-js-scheduletext]").on("blur", this.update_scheduled_date.bind(this));
}
return;
}
}
}
},
update_scheduled_date: function(e) {
this.scheduled_date = e.target.value;
},
make_post: function(event) {
if (!this.reblog_tree_exists() || this.state != "success") {
return;
}
try {
var post_types = this.post_types;
switch (post_types[this.selected_post_type]) {
case post_types.PUBLISH:
this.send_post_request(event);
break;
case post_types.QUEUE:
this.send_queue_request(event);
break;
case post_types.DRAFT:
this.send_draft_request(event);
break;
case post_types.PRIVATE:
this.send_private_request(event);
break;
case post_types.SCHEDULE:
this.send_schedule_request(event);
break;
}
this.state = "finished";
} catch (e) {
console.error(e);
var data = { state: this.state, "ER Version": XKit.installed.get("editable_reblogs").version };
data['User Agent'] = window.navigator.userAgent;
data['XKit Version'] = XKit.version;
data['Patches Version'] = XKit.installed.get("xkit_patches").version;
data['Extensions'] = XKit.installed.list().join(", ");
data['URL'] = window.location.toString();
var body = "\xA0\n*Please describe what actions we can take to reproduce the bug you found, " +
"including any links or screenshots that might help us figure out what's going on.*\n\n\n" +
"-----------\n\n";
if (e) {
body += "```\n" +
e.stack +
"\n```\n\n";
}
body += "System Data | \xA0 \n";
body += "----------- | -----------\n";
$.each(data, function(key, value){
body += key + " | " + value + "\n";
});
XKit.tools.make_gist(body, "ER post error").then(function(gist_url) {
XKit.window.show('Editable Reblogs Error',
'ERROR: Editable Reblogs failed to post some part of your post safely. ' +
'<br><br><a target="_blank" href="'+gist_url+'">View the error here</a>,',
'error', '<div id="xkit-close-message" class="xkit-button">OK</div>');
});
this.state = "error";
}
},
send_post_request: function(e) {
e.preventDefault();
var request = this.build_svc_request();
request["post[state]"] = "0";
this.send_request(request);
},
send_queue_request: function(e) {
e.preventDefault();
var request = this.build_svc_request();
request["post[state]"] = "2";
this.send_request(request);
},
send_draft_request: function(e) {
e.preventDefault();
var request = this.build_svc_request();
request["post[state]"] = "1";
this.send_request(request);
},
send_private_request: function(e) {
e.preventDefault();
var request = this.build_svc_request();
request["post[state]"] = "private";
this.send_request(request);
},
send_schedule_request: function(e) {
e.preventDefault();
var request = this.build_svc_request();
request["post[state]"] = "on.2";
request["post[publish_on]"] = this.scheduled_date;
this.send_request(request);
},
build_svc_request: function() {
var post_type = XKit.interface.post_window.post_type();
var request = this.build_common_svc_request();
if (post_type.text) {
request["post[type]"] = "regular";
//@TODO make title editable
request["post[one]"] = $('.post-form--header .reblog-title').text();
}
if (post_type.photo) {
request["post[type]"] = "photo";
}
if (post_type.video) {
request["post[type]"] = "video";
}
if (post_type.note) {
request["post[type]"] = "note";
request["post[three]"] = request["post[two]"];
request["post[two]"] = "";
}
if (post_type.audio) {
request["post[type]"] = "audio";
}
if (post_type.link) {
request["post[type]"] = "link";
request["post[three]"] = request["post[two]"];
request["post[two]"] = "";
}
return request;
},
build_common_svc_request: function() {
var request = {};
var location_path = window.location.pathname;
var location_items = location_path.split("/");
location_items.shift();
request.form_key = XKit.interface.form_key();
request.channel_id = $('.post-form--header .tumblelog-select .caption').text();
request.detached = true; //?
request.reblog = location_items[0] === "reblog";
if (location_items[0] === "reblog") {
request.reblog_id = parseInt(location_items[1]);
}
if (location_items[0] === "edit") {
request.post_id = parseInt(location_items[1]);
}
request.reblog_key = location_items[2];
request.errors = false;
request.silent = false;
request.context_id = "";
if (location_items[0] === "reblog") {
request.reblog_post_id = location_items[1];
}
if (location_items[0] === "edit") {
request.edit_post_id = location_items[1];
}
request.remove_reblog_tree = true;
request["is_rich_text[one]"] = "0";
request["is_rich_text[two]"] = "1";
request["is_rich_text[three]"] = "0";
request["post[slug]"] = this.post_slug_metadata;
request["post[draft_status]"] = "";
request["post[date]"] = this.post_date_metadata;
request["post[tags]"] = $.map($('.post-form--footer .tag-label'), function(element, index) {
return $(element).text();
}).join(",");
request["post[two]"] = this.parse_html(XKit.interface.post_window.get_content_html());
if ($('.post-forms--social-buttons .social-button.twitter').hasClass('checked')) {
request.send_to_twitter = "on";
}
if ($('.post-forms--social-buttons .social-button.facebook').hasClass('checked')) {
request.send_to_fbog = "on";
}
//@TODO maybe we can do this in the future
request.custom_tweet = false;
return request;
},
parse_html: function(data) {
var text = XKit.interface.post_window.get_content_html();
var nodes = $('<div>').append($(text));
nodes.find('.tmblr-truncated').replaceWith('[[MORE]]');
XKit.extensions.editable_reblogs.format_video_media(nodes);
// tumblr_blog must be wrapped in single quotes,
// not double, or the dash will nom the shit out of your post
//********ALL DOM MANIPULATION ABOVE THIS LINE*********
text = nodes.html();
// text = text.replace(/['"]tumblr_blog['"]/g, "tumblr_blog tumblr_blog");
// break up blockquote links that may confuse tumblr
text = text.replace(
/(<\/a>:<\/\w+>)(\s|<p><\/p>)*?(\n?<blockquote>)/g,
'$1<xkit></xkit>$3'
);
// also remove empty HTML if the user hasn't added anything
if (text.indexOf("<p><br></p>", text.length - 11) !== -1) {
text = text.substring(0, text.length - 11);
}
return text;
},
format_video_media: function(nodes) {
//parse items embedded in the current post
var embeds = nodes.find('.media-holder');
$.each(embeds, function(index, value) {
var element = $(value);
element.find('.media-killer').remove();
element.find('.media-mover').remove();
element.find('.thumbnail-preview').remove();
var figure = element.find('figure');
figure.removeClass('tmblr-embed-placeholder')
.removeClass('embed-thumbnail-preview')
.addClass('tmblr-embed')
.removeAttr('data-embed-code')
.unwrap();
});
//parse items embedded in earlier reblogs
var figures = nodes.find('figure.tmblr-embed');
$.each(figures, function(index, value) {
var figure = $(value);
var iframe = figure.find('iframe');
if (!iframe.is('[data-src]')) {
var src = iframe.attr('src');
iframe.attr('data-src', src);
}
if (!figure.is('[data-orig-height]')) {
var height = iframe.attr('height');
figure.attr('data-orig-height', height);
}
if (!figure.is('[data-orig-width]')) {
var width = iframe.attr('width');
figure.attr('data-orig-width', width);
}
if (!figure.is('[data-url]')) {
var tumblrSource = iframe.attr('data-src');
var embedId = iframe.attr('id');
if (tumblrSource && embedId) {
var segments = tumblrSource.split('/');
var url = segments[6].replace('#' + embedId, '');
figure.attr('data-url', url);
}
}
if (!figure.is('[data-provider]')) {
//it doesn't seem to matter what this value is as long as it exists
figure.attr('data-provider', 'youtube');
}
});
},
send_request: function(request) {
XKit.interface.kitty.get(function(kitty_data) {
GM_xmlhttpRequest({
method: "POST",
url: "http://www.tumblr.com/svc/post/update",
data: JSON.stringify(request),
json: true,
headers: {
"X-tumblr-puppies": kitty_data.kitten,
"X-tumblr-form-key": XKit.interface.form_key(),
},
onerror: function(response) {
XKit.interface.kitty.set("");
var github_url = XKit.tools.github_issue("Editable Reblogs posting error",
{ "ER Version": XKit.installed.get("editable_reblogs").version,
user: request.channel_id, body: request["post[two]"], response: JSON.stringify(response)},
e);
XKit.window.show("Error",
"Error: XER-SR.<br><br>There was an error reblogging your post. Please try again shortly. "+
'<br><br>'+
'<a target="_blank" href="'+github_url+'">You can report this issue on Github by clicking here</a>',
"error",
'<div class="xkit-button default" id="xkit-close-message">OK</div>');
console.error("editable_reblogs.send_request:\n", response);
},
onload: function(response) {
// We are done!
XKit.interface.kitty.set(response.getResponseHeader("X-tumblr-kittens"));
var redirect_url = XKit.tools.getParameterByName("redirect_to");
XKit.tools.add_function(function() {
Tumblr.Events.trigger("postForms:saved");
var redirect_url = add_tag;
if (redirect_url !== "") {
setTimeout(function(){
window.location.replace(redirect_url);
}, 500);
}
}, true, redirect_url);
}
});
});
},
destroy: function() {
this.running = false;
XKit.tools.remove_css("editable_reblogs_remove_content_tree");
this.teardown_event_handlers();
XKit.interface.post_window_listener.remove("editable_reblogs");
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment