您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Retains text entered into textareas and contentEditables, and expires after certain time span.
当前为
// ==UserScript== // @author Crend King // @version 2.4 // @name Textarea Backup with expiry // @namespace http://users.soe.ucsc.edu/~kjin // @description Retains text entered into textareas and contentEditables, and expires after certain time span. // @include http://* // @include https://* // @homepage http://userscripts.org/scripts/show/42879 // ==/UserScript== // this script was originally based on http://userscripts.org/scripts/review/7671 /* version history 2.4 on 08/15/2013: - Support Google Chrome. 2.3 on 07/20/2013: - Support dynamically created textareas. To restore, such textareas need to be created first and then use the script command. 2.2.1 on 01/06/2013: - Remove restriction for textarea under a form. 2.2 on 01/03/2013: - Add support for elements with "contentEditable" attribute. 2.1 on 05/09/2011: - Add user menu command to restore all textarea in the page. 2.0 on 05/06/2011: - Completely rewrite the script. New script should be faster, stronger and more standard-compliant. - Fix bugs in previous versions and the original script. 1.0.4 on 04/22/2009: - Synchronize with the original Textarea Backup script. 1.0.3 on 03/08/2009: - Add "ask overwrite" option. 1.0.2 on 03/04/2009: - Add "keep after submission" option. 1.0.1 on 02/22/2009: - Extract the expiry time stamp codes to stand-alone functions. 1.0 on 02/21/2009: - Initial version. */ ///// preference section ///// // backup when element loses focus var blur_backup = true; // interval for timely backup, in millisecond. 0 disables timely backup var timely_backup_interval = 0; // keep backup even form is submitted // make sure expiration is enabled or backup will never be deleted var keep_after_submission = false; // set true to display a confirmation window for restoration // otherwise restore unconditionally var confirm_overwrite = true; // auxiliary variable to compute expiry_timespan // set all 0 to disable expiration var expire_after_days = 0; var expire_after_hours = 0; var expire_after_minutes = 30; ///// code section ///// // expiry time for a backup, in millisecond var expiry_timespan = (((expire_after_days * 24) + expire_after_hours) * 60 + expire_after_minutes) * 60000; // how many times to flash. must be a even number, or the border style will not revert var flash_count = 6; // how fast is the flash var flash_frequency = 100; // array of all backed up elements in the page var targets = []; // element_id: whether this element is prompted for restoration var prompted = {}; // CSS selector for backup-able elements var target_selector = 'textarea, *[contentEditable]'; var get_element_id = function(element) { /* return the reference ID of the element multiple elements with no name or id will collide but element without either would be useless */ return element.name || element.id || ''; }; var get_element_key = function(element) { // Greasemonkey key for the backup // take URI into consideration return element.baseURI + ';' + get_element_id(element); }; var append_timestamp = function(str) { return str + '@' + (new Date()).getTime(); }; var remove_timestamp = function(str) { return str.replace(/@\d+$/, ''); }; var get_timestamp = function(str) { var time_pos = str.lastIndexOf('@'); return str.substr(time_pos + 1); }; var get_element_value = function(element) { if (element.nodeName == 'TEXTAREA') return element.value; else return element.innerHTML; }; var set_element_value = function(element, value) { if (element.nodeName == 'TEXTAREA') element.value = value; else element.innerHTML = value; }; var commit_backup = function(element) { var element_value = get_element_value(element); // backup if value is not empty if (!/^\s*$/.test(element_value)) { var bak_payload = append_timestamp(element_value); GM_setValue(get_element_key(element), bak_payload); } }; var confirm_restore; var get_backup_content = function(element) { // backup payload is in format of "backup_text@save_time", // where save_time is the millisecond from Javascript Date object's getTime() var bak_payload = GM_getValue(get_element_key(element)); if (!bak_payload) return false; var bak_content = remove_timestamp(bak_payload); // ignore if backup text is identical to current value if (bak_content == get_element_value(element)) return false; else return bak_content; }; var restore_backup = function(elements, index) { // check with user before overwriting existing content with backup // asynchronized when confirmation is enabled, synchronized otherwise if (confirm_overwrite) { var bak_content = get_backup_content(elements[index]); if (bak_content !== false) { confirm_restore(elements, index, bak_content); } } else { for (var i = 0; i < elements.length; ++i) { var element = elements[i]; set_element_value(element, get_backup_content(element)); } } }; confirm_restore = function(elements, index, bak_content) { var element = elements[index]; element.scrollIntoView(false); // flash the element var ori_border = element.style.border; var new_border = '2px solid red'; var toggle = true; var flashed = flash_count; var interval_id; var toggle_border = function() { element.style.border = (toggle ? new_border : ori_border); toggle = !toggle; --flashed; if (flashed == 0) { clearInterval(interval_id); var msg = "[Textarea Backup] Backup exists for this element, proceed to overwrite with this backup?\n\n"; msg += bak_content.length > 750 ? bak_content.substr(0, 500) + "\n..." : bak_content; if (confirm(msg)) set_element_value(element, bak_content); if (index + 1 < elements.length) { // setTimeout is an asynchronized operation // need recursion to serialize restoration on elements restore_backup(elements, index + 1); } } }; interval_id = setInterval(toggle_border, flash_frequency); }; var on_focus = function(event) { var element = event.target; var element_id = get_element_id(element); if (!prompted[element_id]) { // set prompted status disregarding user's choice of overwriting prompted[element_id] = true; restore_backup([element], 0); } }; var on_blur = function(event) { commit_backup(event.target); }; var on_submit = function(event) { for (var i = 0; i < targets.length; ++i) GM_deleteValue(get_element_key(targets[i])); }; var init_backup = function(element) { prompted[get_element_id(element)] = false; element.addEventListener('focus', on_focus, true); // save buffer when the element loses focus if (blur_backup) element.addEventListener('blur', on_blur, true); // delete buffer when the form is submitted if (!keep_after_submission && element.form) element.form.addEventListener('submit', on_submit, true); }; var restore_all = function() { // restore all targets and set prompted status for (var i = 0; i < targets.length; ++i) { var target = targets[i]; var target_id = get_element_id(target); if (!prompted[target_id]) prompted[target_id] = true; restore_backup(targets, i); } }; var backup_dynamic = function(evt) { if (evt.target.querySelectorAll == undefined) return; var new_textareas = evt.target.querySelectorAll(target_selector); for (var i = 0; i < new_textareas.length; ++i) { var new_textarea = new_textareas.item(i); targets.push(new_textarea); init_backup(new_textarea); } }; // expiration check routine if (expiry_timespan > 0) { // get all associated backups for this page, and compare timestamp now and then var curr_time = new Date().getTime(); var stored_bak = GM_listValues(); for (var stored_bak_index = 0; stored_bak_index < stored_bak.length; ++stored_bak_index) { var bak_payload = GM_getValue(stored_bak[stored_bak_index]); var bak_content = remove_timestamp(bak_payload); var bak_time = get_timestamp(bak_payload); if (curr_time - bak_time >= expiry_timespan) GM_deleteValue(stored_bak[stored_bak_index]); } } var query_result = document.querySelectorAll(target_selector); for (var query_result_index = 0; query_result_index < query_result.length; ++query_result_index) { var query_item = query_result.item(query_result_index); targets.push(query_item); init_backup(query_item); } if (targets.length > 0) { // save buffer in interval fashion if (timely_backup_interval > 0) { var backup_all = function() { for (var i = 0; i < targets.length; ++i) { var target = targets[i]; if (prompted[get_element_id(target)]) commit_backup(target); } }; setInterval(backup_all, timely_backup_interval); } } document.addEventListener('DOMNodeInserted', backup_dynamic); GM_registerMenuCommand('Restore all textareas in this page', restore_all);