// ==UserScript== // @name Tumblr Post Saver // @namespace https://github.com/bab5470/tampermonkey-tumblr-post-saver // @version 0.2 // @description Save tumblr reblogs and drafts on a timer or manually. Prompts you to save new posts to draft (so they aren't lost). Restore posts automatically (after a browser crash). Options to import, export, or clear saved post data. // @author Brad Baker // // @match *://www.tumblr.com/* // // @noframes // // @grant GM_addStyle // @grant GM_listValues // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant unsafeWindow // @grant GM_registerMenuCommand // // @run-at document-end // // @require https://openuserjs.org/src/libs/sizzle/GM_config.js // @require https://greasyfork.org/scripts/2199-waitforkeyelements/code/waitForKeyElements.js?version=6349 // @require http://code.jquery.com/jquery-latest.js // // @downloadURL https://update.greasyfork.icu/scripts/35775/Tumblr%20Post%20Saver.user.js // @updateURL https://update.greasyfork.icu/scripts/35775/Tumblr%20Post%20Saver.meta.js // ==/UserScript== GM_config.init({ 'id': 'tumblr_post_saver', // The id used for this instance of GM_config 'title': 'Tumblr Post Saver Settings', 'fields': // Fields object { 'time_between_saves': // This is the id of the field { 'label': 'Time Between Saves', // Appears next to field 'section': ['Settings', 'Set options below.'], // Appears above the field 'type': 'select', // Makes this setting a text field 'options': ['5000', '30000', '60000'], // Possible choices 'default': '5000' // Default value if user doesn't change it }, 'days_to_keep': // This is the id of the field { 'label': 'Days to Keep', // Appears next to field 'type': 'select', // Makes this setting a text field 'options': ['183', '365', '730'], // Possible choices 'default': '730' // Default value if user doesn't change it }, 'backup_posts': { 'label': 'Backup Posts', // Appears on the button 'section': ['Post Management', 'Backup, Restore, Clear Posts'], // Appears above the field 'type': 'button', // Makes this setting a button input 'size': 100, // Control the size of the button (default is 25) 'click': function() { // Function to call when button is clicked download_data(); } }, 'restore_posts': { 'label': 'Restore Posts', // Appears on the button 'type': 'button', // Makes this setting a button input 'size': 100, // Control the size of the button (default is 25) 'click': function() { // Function to call when button is clicked upload_data(); } }, 'clear_data': { 'label': 'Clear Posts', // Appears on the button 'type': 'button', // Makes this setting a button input 'size': 100, // Control the size of the button (default is 25) 'click': function() { // Function to call when button is clicked clear_data(); } }, }, 'css': '#tumblr_post_saver_restore_posts_var #tumblr_post_saver_clear_data_var { width = 10px; float:left; }' // CSS that will hide the section }); var fireOnHashChangesToo = true; var pageURLCheckTimer = setInterval ( function () { if (this.lastPathStr !== location.pathname || this.lastQueryStr !== location.search || (fireOnHashChangesToo && this.lastHashStr !== location.hash)) { this.lastPathStr = location.pathname; this.lastQueryStr = location.search; this.lastHashStr = location.hash; Main (); } }, 1000 ); function Main () { 'use strict'; console.log ('A new page has loaded.'); // Add CSS GM_addStyle('#post_saver_button { font-size: 13px;font-weight: 700; padding-top: 5px;padding-bottom: 5px;border-radius: 2px 2px 2px 2px;padding-left: 10px;padding-right: 10px;border-color: #4a9aca;background-color: #4a9aca;color: hsla(0,0%,100%,.9);float:left;}'); GM_addStyle('#white_div {font-size: 13px;font-weight: 700;padding-top: 5px;padding-bottom: 5px;padding-left: 10px;padding-right: 10px;background-color: #ffffff;float:left;}'); // Register a menu entry for configuring post saver GM_registerMenuCommand('Tumblr Post Saver: Configuration', function() { GM_config.open(); }); if ((GM_config.get('time_between_saves'))) { console.log('Time between saves: ' + GM_config.get('time_between_saves')); } if ((GM_config.get('days_to_keep'))) { console.log('Days to keep: ' + GM_config.get('days_to_keep')); } // Get the current date stamp and the datestamp that the purge was last run. var current_datestamp = build_current_datestamp(); var last_time_purge_ran_datestamp = GM_getValue("last_ran_purge_datestamp"); // If the last ran purage datestamp isn't set this must be the first run. // Set it. if (last_time_purge_ran_datestamp === null) { // Write the latest run to the database GM_setValue("last_ran_purge_datestamp",current_datestamp); } // If a purge hasn't been run in the last date run it. if (last_time_purge_ran_datestamp < current_datestamp) { prune_old_posts(); } if(location.pathname.match(/reblog/)) { console.log("reblog"); reblog(); } else if (location.pathname.match(/edit/)) { console.log("edit"); edit(); } else if (location.pathname.match(/drafts/)) { console.log("drafts"); // Do nothing } else { console.log("couldn't find reblog, edit or drafts so polling visibility"); pollVisibility(); } } function pollVisibility() { // The post form is open (it could be a reblog or a new post. We'll check that below. if( ($('.post-forms-glass').is(':visible')) && (($('.reblog_name').length) === 0)) { console.log("new post"); newpost(); } else { console.log("poll visibility again"); if(location.pathname.match(/reblog/)) { console.log("reblog"); reblog(); } else if (location.pathname.match(/edit/)) { console.log("edit"); edit(); } else if (location.pathname.match(/drafts/)) { console.log("drafts"); // Do nothing } setTimeout(pollVisibility, 5000); } } function newpost () { // Every "time_between_saves" (see preferences) //setInterval(function() { if (!location.pathname.match(/drafts/)) { // Keep prompting the user to save the post as otherwise it may be lost during a browser crash/OS reboot etc. if (confirm("Warning: It appears you're starting a new post. Post saver can't save new posts until you save an initial draft. Would you like to do so now? (You won't be prompted again)")) { // Click the arrow drop down next to the post button document.getElementsByClassName('dropdown-area icon_arrow_carrot_down')[0].click(); // Find the save as draft list item and click it var save_as_draft = document.evaluate("//span[contains(., 'Save as draft')]", document, null, XPathResult.ANY_TYPE, null ); var save_as_draft_link = save_as_draft.iterateNext(); save_as_draft_link.click(); // Click the save button var save_button = $('.post-form--save-button [data-js-clickablesave]'); save_button.click(); } } //}, GM_config.get('time_between_saves')); } function reblog () { // Get the request ID from the URL request = get_request_id(); console.log(request); // Get all the saved posts var saved_posts = GM_listValues(); // Generate a regular expression to match the request id var regex = new RegExp(request, "g"); // Create variable to store the key var datestamp; // Iterate through the posts for (var i=0; i < saved_posts.length; i++) { var key = saved_posts[i]; // If there's a match then set the post content to the key data if (key.match(regex)) { console.log("Match found for " + regex); // Store the current key in a datestamp variable for use below datestamp = key; // Get the matching post from the database var postcontent = GM_getValue(saved_posts[i]); // Restore the post if (document.getElementsByClassName('editor editor-richtext')[0]) { document.getElementsByClassName('editor editor-richtext')[0].innerHTML = postcontent; } else { setTimeout(reblog, 1000); } } else { console.log("Match NOT found for " + regex); } } // Insert save button insert_save_button(datestamp); // Every "time_between_saves", save the post to the database setInterval(function() { var key; // If the datestamp is null (i.e. we've never seen this post before) then create a new key // otherwise reuse the existing key if(datestamp) { key = datestamp; } else { key = build_current_datestamp() + "_" + get_request_id(); } var postcontent = document.getElementsByClassName('editor editor-richtext')[0]; // If the post content is