/* autosavehack.js
*
* Autosave using HTML5 local storage for HTMLy CMS
*
* For changelog and latest version, visit
* http://www.mudeth.org/get/htmly-autosave-hack
*
* For instructions, visit
* http://www.mudeth.org/post/htmly-autosave-hack
*/
var ash_version = "0.1.3"; // Current version
var ash_save_interval = 10000; // Auto-save every this many milliseconds
var ash_page_id = 0; // unique ID generated from '/post/' in URL
var ash_status_box_h = 0; // handle to a span where we can display messages
var ash_input_h = 0; // handle to the editor's text box
var ash_enabled = false; // whether autosaving is enabled
var ash_time; // For informing the user
var ash_timer_h; // Timer handler
var ash_config_key; // Local storage key; true if autosaving is enabled for this site
var ash_this_domain; // Domain we're running on, so we can have per-domain settings and saves
var _ash_anim_delay = 60; // Eye can't see more than 15 FPS /s
var _ash_anim_frame; // Used internally
var _ash_anim_timer_h = 0; // Used internally
// Formats
var ash_storage_suffix = ".htmlyautosave"; // Autosaves are stored as /
var ash_enable_suffix = ".htmly_autosave_hack_enabled"; // ash_config_key is
function ash_escape_regex (str) {
// Escapes regex characters for using in regex matching
return str.replace (/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
function _ash_animate () {
// Used internally as a callback for animation
if (_ash_anim_frame > 0) {
// Set background colour according to which frame we're on
var shade = 255 - _ash_anim_frame;
shade = "rgb(" + shade + "," + shade + "," + shade + ")";
ash_status_box_h.style.background = shade;
--_ash_anim_frame;
return;
}
// Animation over; stop this nonsense
clearInterval (_ash_anim_timer_h);
_ash_anim_timer_h = 0;
ash_status_box_h.style.background = "initial";
}
function ash_notify (message) {
// Prints 'message' via status_box_h
if (ash_status_box_h)
ash_status_box_h.innerHTML = message;
// Animate this guy a bit
_ash_anim_frame = 30;
if (!_ash_anim_timer_h)
_ash_anim_timer_h = setInterval (_ash_animate, _ash_anim_delay);
}
function ash_update_button (btn_name, new_val) {
// Changes value of button with given name
// Only updates if exactly one element is found
ele = document.getElementsByName(btn_name);
if (ele.length == 1) {
ele[0].value = new_val;
}
}
function ash_init () {
// Assigns status ash_status_box_h and ash_page_id
// Checks whether local storage is enabled
// Sets ash_enabled to true and displays controls if all goes well
// Also loads existing auto-save
ash_status_box_h = document.getElementById ("autosave_hack_status");
ash_input_h = document.getElementById ("wmd-input");
if (!ash_input_h) {
// May be a different version of HTMLy? Let's not mess around
ash_notify ("Autosave error: couldn't find editor box.");
return;
}
// Generate config key for this domain
ash_this_domain = document.URL.split('/')[2];
ash_config_key = ash_this_domain + ash_enable_suffix;
// Check settings (if autosave is enabled)
var is_enabled = localStorage.getItem (ash_config_key);
if (!is_enabled)
// No setting found. By default, turn it off
is_enabled = "false";
if ( is_enabled == "true" ) {
ash_toggle (false); // No confirmation required
} else {
ash_notify ("Autosaving is off.");
}
}
function ash_load () {
// Loads stored autosave if it exists
// Displays status via ash_notify()
if (!ash_enabled)
return;
var saved_text = localStorage.getItem(ash_page_id);
if (!saved_text) {
// No autosave in local storage
ash_notify("No save yet for this page.");
return;
}
// Found, update editor
ash_input_h.value = saved_text;
ash_notify ("Loaded autosave.");
}
function ash_clear () {
// Confirms and clears all autosave data in local storage
if (!ash_enabled)
return;
confirmed = confirm ( "This will delete the local saves for ALL posts on this site:\n" +
ash_this_domain +
"\n\n" +
"Autosaving will also be disabled.\n" +
"When you refresh, you will load the saves last committed to HTMLy.\n" +
"\n" +
"Are you sure?"
);
if (confirmed) {
// Disable autosaving first
ash_toggle(false); // No confirmation
// Delete all keys matching /.*
var domain = ash_this_domain;
var re = new RegExp ("^" + ash_escape_regex (domain) + '\/.*' + ash_escape_regex (ash_storage_suffix) + "$");
var c = 0;
for (var key in localStorage) {
if (re.exec (key)) {
// Matches our format
localStorage.removeItem (key);
console.log ("ash_clear(): removed " + key);
++c;
}
}
ash_notify ("Deleted " + c + " local save(s). Please reload.");
} else {
ash_notify ("Autosave clear was cancelled.");
}
}
function ash_save () {
// Autosaves contents of editor
if (!ash_enabled)
return;
if (ash_time) {
ash_time.setTime(Date.now());
var hrs = ash_time.getHours(), mins = ash_time.getMinutes(), secs = ash_time.getSeconds();
time_str = ( (hrs < 10) ? ("0" + hrs) : hrs ) + ":" +
( (mins < 10) ? ("0" + mins) : mins ) + ":" +
( (secs < 10) ? ("0" + secs) : secs );
} else {
time_str = "(unknown)";
}
try {
localStorage.setItem (ash_page_id, ash_input_h.value);
} catch (err) {
// Couldn't save?
alert ( "WARNING: could not save to local storage:\n" +
err.message + "\n" +
"\n" +
"Commit to HTMLy and try increasing local storage in your browser settings."
);
ash_notify (err.message);
return;
}
ash_notify ("Last save at " + time_str);
}
function ash_toggle(ask_confirmation) {
// Toggles autosave functionality
// Does not restore default text
if (typeof(ask_confirmation) == "undefined")
ask_confirmation = true; // Opera 35.0 complained when I tried to assign default value instead
var confirmed;
if (!ash_enabled) {
if (ask_confirmation)
confirmed = confirm ( "Do you want to enable local autosaving?\n" +
"\n" +
"You will load the last autosave on this page, if it exists.\n" +
"\n" +
"Saving will be managed by your browser, separate from HTMLy's saves.\n" +
"At any point, you can still commit your save to HTMLy.\n" +
"\n" +
"If you disable autosave later, you will load the last version that was committed to the server.\n" +
"\n" +
"This will affect all posts on this server:\n" +
ash_this_domain
);
else
confirmed = true;
if (!confirmed)
return;
// letsdoit yo
// Check if browser supports HTML5 local storage
if (typeof (Storage) == "undefined") {
ash_notify ("Your browser does not support local saving.");
return;
}
// Parse post ID
var url_split = document.URL.split('/'), i;
var re = /^edit\?destination=.*/;
var url_post_index = -1;
for (i in url_split) {
if (0 == url_split[i].search(re) && i > 1) {
// Found match
url_post_index = i - 1;
break;
}
}
if (url_post_index == -1) {
// No 'POSTNAME/edit?destination=*'
ash_notify ("Autosave error: couldn't parse post name.");
return;
}
// Format key as /
ash_page_id = ash_this_domain + '/' + url_split [url_post_index] + ash_storage_suffix;
// Save settings
try {
localStorage.setItem (ash_config_key, "true");
} catch (err) {
alert ( "Something's weird.\nCould not write to localStorage\n" + err.message + "\nAutosaving has not been enabled" );
ash_notify (err.message);
// Clean up if necessary
return;
}
// Enable functionality and display controls
document.getElementById ("autosave_hack_controls").style.display = "inline";
document.getElementById ("autosave_hack_toggle").innerHTML = "Disable";
ash_time = new Date();
// Rename HTMLy buttons
ash_update_button ("publishdraft", "Commit and publish");
ash_update_button ("updatedraft", "Commit as draft");
ash_update_button ("updatepost", "Commit and update post");
ash_update_button ("revertpost", "Commit as draft");
// Force save when exiting page
window.onbeforeunload = ash_save;
// All set
ash_enabled = true;
ash_timer_h = setInterval(ash_save, ash_save_interval);
// Load existing autosave
ash_load();
} else {
if (ask_confirmation)
confirmed = confirm ( "Do you want to disable local autosaving?\n" +
"\n" +
"When you reload, you will see the last save that was commited to HTMLy.\n" +
"\n" +
"If you enable autosaving again, you will load back up into the last autosave made before now.\n" +
"\n" +
"This will affect all posts made on this domain:\n" +
ash_this_domain
);
else
confirmed = true;
if (!confirmed)
return;
// Disable autosave
if (ash_timer_h) {
// Clear timer
clearInterval (ash_timer_h);
ash_timer_h = 0;
delete ash_time;
}
// Hide controls and disable functionality
document.getElementById ("autosave_hack_controls").style.display = "none";
document.getElementById ("autosave_hack_toggle").innerHTML = "Enable";
ash_enabled = false;
try {
localStorage.setItem (ash_config_key, "false");
} catch (err) {
alert ( "Something's weird.\nCould not write to localStorage\n" + err.message +
"\nAutosaving disabled but the setting could not be made persistent\n" +
"When you reload, it'll be enabled again. Try increasing local storage\n" +
"in your browser settings"
);
ash_notify (err.message);
return;
}
ash_notify ("Autosaving disabled. Please reload this page.");
}
}