/*
TinyAutoSave plugin for TinyMCE
Version: 2.1.3
http://tinyautosave.googlecode.com/
Copyright (c) 2008-2011 Todd Northrop
http://www.speednet.biz/
March 19, 2011
Adds auto-save capability to the TinyMCE text editor to rescue content
inadvertently lost.
Dual licensed under the MIT or GPL Version 2 licenses.
See mit-license.txt and gpl2-license.txt in the project root for details.
*/
// Wrap all code in function to create true private scope and prevent
// pollution of global namespace
(function() {
//************************************************************************
// PRIVATE VARIABLES
var version = "2.1.3",
// The name of the plugin, as specified to TinyMCE
pluginName = "tinyautosave",
// Specifies if localStorage (HTML 5) is available
useLocalStorage = false,
// Specifies if sessionStorage (HTML 5) is available
useSessionStorage = false,
// Specifies if UserData (IE client storage) is available
useUserData = false,
// Translation keys for encoding/decoding cookie values
cookieEncodeKey = {"%": "%1", "&": "%2", ";": "%3", "=": "%4", "<": "%5"},
cookieDecodeKey = {"%1": "%", "%2": "&", "%3": ";", "%4": "=", "%5": "<"},
// Internal storage for preloaded images
preloadImages = [],
// Internal storage of settings for each plugin instance
instanceSettings = {},
// Cached storage of callback function resolution, for performance
callbackLookup = {},
// Unique key used to test if HTML 5 storage methods are available
testKey = "TinyAutoSave_Test_",
// The HTML element that IE's UserData will be attached to
userDataElement = null,
// Default settings for each plugin instance
settingsTemplate = {
dataKey: "TinyAutoSave",
cookieFilter: null,
saveDelegate: null,
saveFinalDelegate: null,
restoreDelegate: null,
disposeDelegate: null,
restoreImage: "",
progressImage: "progress.gif",
intervalSeconds: 60,
retentionMinutes: 20,
minSaveLength: 50,
askBeforeUnload: false,
canRestore: false,
busy: false,
timer: null
};
//************************************************************************
// TEST STORAGE METHODS
// Determine best storage method by storing and retrieving test data
try {
localStorage.setItem(testKey, "OK");
if (localStorage.getItem(testKey) === "OK") {
localStorage.removeItem(testKey);
useLocalStorage = true;
}
}
catch (e) {
try {
sessionStorage.setItem(testKey, "OK");
if (sessionStorage.getItem(testKey) === "OK") {
sessionStorage.removeItem(testKey);
useSessionStorage = true;
}
}
catch (e) {
useUserData = tinymce.isIE;
}
}
//************************************************************************
// TINYMCE INTEROP
tinymce.PluginManager.requireLangPack(pluginName);
tinymce.create("tinymce.plugins.TinyAutoSavePlugin", {
///
/// Automatically saves the editor contents periodically and just before leaving the current page.
/// Allows the user to rescue the contents of the last autosave, in case they did not intend to
/// navigate away from the current page or the browser window was closed before posting the content.
///
///
/// A reference to the TinyMCE editor instance that contains this TinyAutoSave plugin instance.
///
///
/// The URL of the folder containing the TinyAutoSave plugin. Does not include a trailing slash.
///
///
/// A string value identifying the storage and settings for the plugin, as set by tinyautosave_key.
///
///
/// (String) Name of a callback function that gets called before each auto-save is performed.
/// (Function) A function that gets called before each auto-save is performed.
/// The callback function must return a Boolean value of true if the auto-save is to proceed
/// normally, or false if the auto-save is to be canceled. The editor instance is the context of the
/// callback (assigned to 'this').
///
///
/// (String) Name of a callback function that gets called after each auto-save is performed.
/// (Function) A function that gets called after each auto-save is performed.
/// Any return value from the callback function is ignored. The editor instance is the context of
/// the callback (assigned to 'this').
///
///
/// (String) Name of a callback function that gets called each time an auto-save fails in an error condition.
/// (Function) A function that gets called each time an auto-save fails in an error condition.
/// The editor instance is the context of the callback (assigned to 'this').
///
///
/// (String) Name of a callback function that gets called before a restore request is performed.
/// (Function) A function that gets called before a restore request is performed.
/// The callback function must return a Boolean value of true if the restore is to proceed normally,
/// or false if the restore is to be canceled. The editor instance is the context of the callback
/// (assigned to 'this').
///
///
/// (String) Name of a callback function that gets called after a restore request is performed.
/// (Function) A function that gets called after a restore request is performed.
/// Any return value from the callback function is ignored. The editor instance is the context of
/// the callback (assigned to 'this').
///
///
/// (String) Name of a callback function that gets called each time a restore request fails in an error condition.
/// (Function) A function that gets called each time a restore request fails in an error condition.
/// The editor instance is the context of the callback (assigned to 'this').
///
///
/// Number of milliseconds that the progress image is displayed after an auto-save. The default is
/// 1200, which is the equivalent of 1.2 seconds.
///
///
/// Receives the Boolean value specified in the tinyautosave_showsaveprogress configuration option,
/// or true if none is specified. This is a public read/write property, and the behavior of the
/// toolbar button throbber/progress can be altered dynamically by changing this property.
///
///
///
/// CONFIGURATION OPTIONS:
///
/// tinyautosave_key - (String, default = editor id) A string value used to identify the autosave
/// storage and settings to use for the plug instance. If tinyautosave_key is not specified, then
/// the editor's id property is used. If you set the tinyautosave_key for all editors to the same value,
/// that would create a single autosave storage instance and a single set of autosave settings to use
/// with all editors. Because each key maintains its own plugin settings, tinyautosave_key can also be
/// used to apply a different UI or behavior to individual editors. For example, two editors on the same
/// page could use different progress images, or they could autosave at different intervals.
///
/// tinyautosave_interval_seconds - (Number, default = 60) The number of seconds between automatic saves.
/// When the editor is first displayed, an autosave will not occur for at least this amount of time.
///
/// tinyautosave_minlength - (Number, default = 50) The minimum number of characters that must be in the
/// editor before an autosave will occur. The character count includes all non-visible characters,
/// such as HTML tags. Although this can be set to 0 (zero), it is not recommended. Doing so would
/// open the possibility that if the user accidentally refreshes the page, the empty editor contents
/// would overwrite the rescue content, effectively defeating the purpose of the plugin.
///
/// tinyautosave_retention_minutes - (Number, default = 20) The number of minutes since the last autosave
/// that content will remain in the rescue storage space before it is automatically expired.
///
/// tinyautosave_oninit - (String, default = null) The name of a function to call immediately after the
/// TinyAutoSave plugin instance is initialized. Can include dot-notation, e.g., "myObject.myFunction".
/// The context of the function call (the value of 'this') is the plugin instance. This function is
/// a good place to set any of the public properties that you want to configure.
///
/// tinyautosave_showsaveprogress - (Boolean, default = true) When true, the toolbar button will show a
/// brief animation every time an autosave occurs.
///
/// COMMANDS:
///
/// Available TinyMCE commands are:
/// mceTinyAutoSave - Perform an auto-save
/// mceTinyAutoSaveRestore - Restore auto-saved content into the editor
///
/// PUBLIC PROPERTIES:
///
/// Available public properties of the TinyAutoSave plugin are:
/// editor (Object)
/// url (String)
/// key (String)
/// onPreSave (String)
/// onPostSave (String)
/// onSaveError (String)
/// onPreRestore (String)
/// onPostRestore (String)
/// onRestoreError (String)
/// progressDisplayTime (Number)
/// showSaveProgress (Boolean)
///
/// See [field] definitions above for detailed descriptions of the public properties.
///
/// PUBLIC METHODS:
///
/// Available public methods of the TinyAutoSave plugin are:
/// init() - [Called by TinyMCE]
/// getInfo() - [Called by TinyMCE]
/// clear() - Clears any auto-saved content currently stored, and "dims" the Restore toolbar button.
/// hasSavedContent() - Returns true if there is auto-save content available to be restored, or false if not.
/// setProgressImage() - Sets the URL of the image that will be displayed every time an auto-save occurs.
///
/// TECHNOLOGY DISCUSSION:
///
/// The plugin attempts to use the most advanced features available in the current browser to save
/// as much content as possible. There are a total of four different methods used to autosave the
/// content. In order of preference, they are:
///
/// 1. localStorage - A new feature of HTML 5, localStorage can store megabytes of data per domain
/// on the client computer. Data stored in the localStorage area has no expiration date, so we must
/// manage expiring the data ourselves. localStorage is fully supported by IE8, and it is supposed
/// to be working in Firefox 3 and Safari 3.2, but in reality is is flaky in those browsers. As
/// HTML 5 gets wider support, the TinyAutoSave plugin will use it automatically. In Windows Vista/7,
/// localStorage is stored in the following folder:
/// C:\Users\[username]\AppData\Local\Microsoft\Internet Explorer\DOMStore\[tempFolder]
///
/// 2. sessionStorage - A new feature of HTML 5, sessionStorage works similarly to localStorage,
/// except it is designed to expire after a certain amount of time. Because the specification
/// around expiration date/time is very loosely-described, it is preferrable to use locaStorage and
/// manage the expiration ourselves. sessionStorage has similar storage characteristics to
/// localStorage, although it seems to have better support by Firefox 3 at the moment. (That will
/// certainly change as Firefox continues getting better at HTML 5 adoption.)
///
/// 3. UserData - A very under-exploited feature of Microsoft Internet Explorer, UserData is a
/// way to store up to 128K of data per "document", or up to 1MB of data per domain, on the client
/// computer. The feature is available for IE 5+, which makes it available for every version of IE
/// supported by TinyMCE. The content is persistent across browser restarts and expires on the
/// date/time specified, just like a cookie. However, the data is not cleared when the user clears
/// cookies on the browser, which makes it well-suited for rescuing autosaved content. UserData,
/// like other Microsoft IE browser technologies, is implemented as a behavior attached to a
/// specific DOM object, so in this case we attach the behavior to the same DOM element that the
/// TinyMCE editor instance is attached to.
///
/// 4. Cookies - When none of the above methods is available, the autosave content is stored in a
/// cookie. This limits the total saved content to around 4,000 characters, but we use every bit
/// of that space as we can. To maximize space utilization, before saving the content, we remove
/// all newlines and other control characters less than ASCII code 32, change instances to
/// a regular space character, and do some minor compression techniques. (TO-DO: add more
/// compressiion techniques.) Unfortunately, because the data is stored in a cookie, we have to
/// waste some space encoding certain characters to avoid server warnings about dangerous content
/// (as well as overcoming some browser bugs in Safari). Instead of using the built-in escape()
/// function, we do a proprietary encoding that only encodes the bare minimum characters, and uses
/// only two bytes per encoded character, rather than 3 bytes like escape() does. escape() encodes
/// most non-alpha-numeric characters because it is designed for encoding URLs, not for encoding
/// cookies. It is a huge space-waster in cookies, and in this case would have cut the amount
/// of autosaved content by at least half.
///
///
//************************************************************************
// PUBLIC PROPERTIES
editor: null,
url: "",
key: "",
onPreSave: null,
onPostSave: null,
onSaveError: null,
onPreRestore: null,
onPostRestore: null,
onRestoreError: null,
showSaveProgress: true,
progressDisplayTime: 1200, // Milliseconds
//************************************************************************
// PUBLIC METHODS
init: function (ed, url) {
///
/// Initialization function called by TinyMCE.
///
var t = this,
is = tinymce.is,
resolve = tinymce.resolve,
s, onInit, f;
t.editor = ed;
t.id = ed.id;
t.url = url;
t.key = ed.getParam(pluginName + "_key", ed.id);
s = newInstanceSettings(t);
s.restoreImage = url + "/images/restore." + (tinymce.isIE6? "gif" : "png");
t.setProgressImage(url + "/images/" + settingsTemplate.progressImage);
// Get the auto-save interval from the TinyMCE config. (i.e., auto-save every 'x' seconds.)
// Integer value. If not specified in config, default is 60 seconds; minimum is 1 second.
// Either 'tinyautosave_interval_seconds' or 'tinyautosave_interval' can be used, but 'tinyautosave_interval_seconds' provides better clarity.
s.intervalSeconds = Math.max(1, parseInt(ed.getParam(pluginName + "_interval_seconds", null) || ed.getParam(pluginName + "_interval", s.intervalSeconds)));
// Get the rescue content retention time from the TinyMCE config. (i.e., rescue content available for 'x' minutes after navigating from page.)
// Integer value. If not specified in config, default is 20 minutes; minimum is 1 minute.
// Don't make this too long; users will get weirded out if content from long ago is still hanging around.
// Either 'tinyautosave_retention_minutes' or 'tinyautosave_retention' can be used, but 'tinyautosave_retention_minutes' provides better clarity.
s.retentionMinutes = Math.max(1, parseInt(ed.getParam(pluginName + "_retention_minutes", null) || ed.getParam(pluginName + "_retention", s.retentionMinutes)));
// Get the minimum content length from the TinyMCE config. (i.e., minimum number of characters in the editor before an auto-save can occur.)
// Integer value. If not specified in config, default is 50 characters; minimum is 1 character.
// Prevents situation where user accidentally hits Refresh, then their rescue content is wiped out when the editor auto-saves the blank editor on the refreshed page. No need to auto-save a few characters.
// Specified as 'tinyautosave_minlength' in the config.
s.minSaveLength = Math.max(1, parseInt(ed.getParam(pluginName + "_minlength", s.minSaveLength)));
// Determine if progress animation should occur by reading TinyMCE config.
// Boolean value. If not specified in config, default is true, progress animation will be displayed after each auto-save.
// Specified as 'tinyautosave_showsaveprogress' in the config.
t.showSaveProgress = ed.getParam(pluginName + "_showsaveprogress", t.showSaveProgress);
s.askBeforeUnload = ed.getParam(pluginName + "_ask_beforeunload", s.askBeforeUnload);
// Save action delegates with context
s.saveDelegate = createDelegate(t, save);
s.saveFinalDelegate = createDelegate(t, saveFinal);
s.restoreDelegate = createDelegate(t, restore);
// Register commands
ed.addCommand("mceTinyAutoSave", s.saveDelegate);
ed.addCommand("mceTinyAutoSaveRestore", s.restoreDelegate);
// Register restore button
ed.addButton(pluginName, {
title: pluginName + ".restore_content",
cmd: "mceTinyAutoSaveRestore",
image: s.restoreImage
});
// Set save interval
s.timer = window.setInterval(s.saveDelegate, s.intervalSeconds * 1000);
// Ensures content is autosaved before window closes or navigates to new page
tinymce.dom.Event.add(window, "unload", s.saveFinalDelegate);
// Save when editor is removed (may be different than window's onunload event, so we need to do both)
ed.onRemove.add(s.saveFinalDelegate);
// Add ask before unload dialog
if (s.askBeforeUnload) {
tinymce.dom.Event.add(window, "unload", tinymce.plugins.AutoSavePlugin._beforeUnloadHandler);
}
ed.onInit.add(function() {
if (useUserData) {
if (!userDataElement) {
userDataElement = ed.getElement();
}
userDataElement.style.behavior = "url('#default#userData')";
}
s.canRestore = t.hasSavedContent();
// Call tinyautosave_oninit, if specified
// This config option is a String value specifying the name of a function to call. Can include dot-notation, e.g., "myObject.myFunction".
// The context of the function call (the value of 'this') is the plugin instance.
onInit = ed.getParam(pluginName + "_oninit", null);
if (is(onInit, "string")) {
f = resolve(onInit);
if (is(f, "function")) {
f.apply(t);
}
}
// Set initial state of restore button
ed.controlManager.setDisabled(pluginName, !s.canRestore);
});
},
getInfo: function() {
///
/// Called by TinyMCE, returns standard information about the plugin
/// to display in the About box.
///
return {
longname: "TinyAutoSave",
author: "Speednet",
authorurl: "http://www.speednet.biz/",
infourl: "http://tinyautosave.googlecode.com/",
version: version
};
},
clear: function () {
///
/// Removes the autosave content from storage. Disables the 'tinyautosave' toolbar button.
///
var t = this,
ed = t.editor,
s = getInstanceSettings(t);
if (useLocalStorage) {
localStorage.removeItem(s.dataKey);
}
else if (useSessionStorage) {
sessionStorage.removeItem(s.dataKey);
}
else if (useUserData) {
removeUserData(t);
}
else {
tinymce.util.Cookie.remove(s.dataKey);
}
s.canRestore = false;
ed.controlManager.setDisabled(pluginName, t);
},
hasSavedContent: function () {
///
/// Returns true if there is unexpired autosave content available to be restored.
///
///
var t = this,
s = getInstanceSettings(t),
now = new Date(),
content, i;
try {
if (useLocalStorage || useSessionStorage) {
content = ((useLocalStorage? localStorage.getItem(s.dataKey) : sessionStorage.getItem(s.dataKey)) || "").toString(),
i = content.indexOf(",");
if ((i > 8) && (i < content.length - 1)) {
if ((new Date(content.slice(0, i))) > now) {
return true;
}
// Remove expired content
if (useLocalStorage) {
localStorage.removeItem(s.dataKey);
}
else {
sessionStorage.removeItem(s.dataKey);
}
}
return false;
}
else if (useUserData) {
return ((getUserData(t) || "").length > 0);
}
return ((tinymce.util.Cookie.get(s.dataKey) || "").length > 0);
}
catch (e) {
return false;
}
},
setProgressImage: function (url) {
///
/// Sets the progress image/throbber to a specified URL. The progress image
/// temporarily replaces the image on the TinyAutoSave toolbar button every
/// time an auto-save occurs. The default value is
/// "[tinymce]/plugins/tinyautosave/images/progress.gif". Can be set any time
/// after the plugin initializes. The progress image is normally an animated GIF,
/// but it can be any image type. Because the image will be displayed on a toolbar
/// button, so the recommended size is 20 x 20 (using a centered 16 x 16 image).
///
///
/// The URL of the image that will be displayed on the restore toolbar button
/// every time an auto-save occurs.
///
if (tinymce.is(url, "string")) {
preloadImage(getInstanceSettings(this).progressImage = url);
}
},
"static": {
_beforeUnloadHandler: function () {
var msg;
tinymce.each(tinyMCE.editors, function (ed) {
if (ed.getParam("fullscreen_is_enabled")) {
return;
}
if (ed.isDirty()) {
msg = ed.getLang("autosave.unload_msg");
return false;
}
});
return msg;
}
}
});
//************************************************************************
// PRIVATE FUNCTIONS
function dispose() {
///
/// Called just before the current page unloads. Cleans up memory, releases
/// timers and events.
///
///
/// Must be called with context ("this" keyword) set to plugin instance
///
var t = this,
s = getInstanceSettings(t);
if (s.timer) {
window.clearInterval(s.timer);
}
tinymce.dom.Event.remove(window, "unload", s.saveFinalDelegate);
if (s.askBeforeUnload) {
tinymce.dom.Event.remove(window, "unload", tinymce.plugins.AutoSavePlugin._beforeUnloadHandler);
}
t.editor.onRemove.remove(s.saveFinalDelegate);
removeInstanceSettings(t);
}
function execCallback(n) {
///
/// Executes a callback function. The callback function can be specified
/// either as a string or a function.
///
///
/// Must be called with context ("this" keyword) set to plugin instance
///
if (!n) {
return true;
}
var c, f,
is = tinymce.is;
if (is(n, "string")) {
c = callbackLookup[n];
if (c) {
f = c[n];
}
else {
callbackLookup[n] = f = tinymce.resolve(n);
}
}
else if (is(n, "function")) {
f = n;
}
else {
return true;
}
return f.apply(this);
}
function saveFinal() {
///
/// Called just before the current page is unloaded. Performs a final save, then
/// cleans up memory to prevent leaks.
///
///
/// Must be called with context ("this" keyword) set to plugin instance
///
var s = getInstanceSettings(this);
s.saveDelegate();
s.disposeDelegate();
}
function save() {
///
/// Performs a single, one-time autosave. Checks to be sure there is at least the
/// specified minimum number of characters in the editor before saving. Briefly
/// animates the toolbar button. Enables the 'tinyautosave' button to indicate
/// autosave content is available.
///
///
/// Returns true if content was saved, or false if not.
///
///
/// Must be called with context ("this" keyword) set to plugin instance
///
var t = this,
ed = t.editor,
s = getInstanceSettings(t),
is = tinymce.is,
saved = false,
now = new Date(),
content, exp, a, b, cm, img;
if ((ed) && (!s.busy)) {
s.busy = true;
content = ed.getContent();
if (is(content, "string") && (content.length >= s.minSaveLength)) {
if (!execCallback.call(t, t.onPreSave)) {
s.busy = false;
return false;
}
exp = new Date(now.getTime() + (s.retentionMinutes * 60 * 1000));
try {
if (useLocalStorage) {
localStorage.setItem(s.dataKey, exp.toString() + "," + encodeStorage(content)); // Uses local time for expiration
}
else if (useSessionStorage) {
sessionStorage.setItem(s.dataKey, exp.toString() + "," + encodeStorage(content)); // Uses local time for expiration
}
else if (useUserData) {
setUserData(t, content, exp);
}
else {
a = s.dataKey + "=";
b = "; expires=" + exp.toUTCString();
document.cookie = a + encodeCookie(content).slice(0, 4096 - a.length - b.length) + b;
}
saved = true;
}
catch (e) {
execCallback.call(t, t.onSaveError);
}
if (saved) {
cm = ed.controlManager;
s.canRestore = true;
cm.setDisabled(pluginName, false);
if (t.showSaveProgress) {
b = tinymce.DOM.get(cm.get(pluginName).id);
if (b) {
img = s.restoreImage;
b.firstChild.src = s.progressImage;
window.setTimeout(
function () {
b.firstChild.src = img;
},
Math.min(t.progressDisplayTime, s.intervalSeconds * 1000 - 100)
);
}
}
execCallback.call(t, t.onPostSave);
}
}
s.busy = false;
}
return saved;
}
function restore() {
///
/// Called when the user clicks the 'tinyautosave' button on the toolbar.
/// Replaces the contents of the editor with the autosaved content. If the editor
/// contains more than just whitespace, the user is warned and given the option
/// to abort. The autosaved content remains in storage.
///
///
/// Must be called with context ("this" keyword) set to plugin instance
///
var t = this,
ed = t.editor,
s = getInstanceSettings(t),
content = null,
is = tinymce.is,
i, m;
if ((ed) && (s.canRestore) && (!s.busy)) {
s.busy = true;
if (!execCallback.call(t, t.onPreRestore)) {
s.busy = false;
return;
}
try {
if (useLocalStorage || useSessionStorage) {
content = ((useLocalStorage? localStorage.getItem(s.dataKey) : sessionStorage.getItem(s.dataKey)) || "").toString();
i = content.indexOf(",");
if (i == -1) {
content = null;
}
else {
content = decodeStorage(content.slice(i + 1, content.length));
}
}
else if (useUserData) {
content = getUserData(t);
}
else {
m = s.cookieFilter.exec(document.cookie);
if (m) {
content = decodeCookie(m[1]);
}
}
if (!is(content, "string")) {
ed.windowManager.alert(pluginName + ".no_content");
}
else {
// If current contents are empty or whitespace, the confirmation is unnecessary
if (ed.getContent().replace(/\s| |<\/?p[^>]*>|
]*>/gi, "").length === 0) {
ed.setContent(content);
execCallback.call(t, t.onPostRestore);
}
else {
ed.windowManager.confirm(
pluginName + ".warning_message",
function (ok) {
if (ok) {
ed.setContent(content);
execCallback.call(t, t.onPostRestore);
}
s.busy = false;
},
t
);
}
}
}
catch (e) {
execCallback.call(t, t.onRestoreError);
}
s.busy = false;
}
}
function setUserData(inst, str, exp) {
///
/// IE browsers only. Saves a string to the 'UserData' storage area.
///
///
/// Plugin instance for which to set the UserData
///
///
/// String value to save.
///
///
/// Date object specifying the expiration date of the content
///
///
/// Maximum size of the autosave data is 128K for regular Internet Web sites or
/// 512KB for intranet sites. Total size of all data for one domain is 1MB for
/// Internet sites and 10MB for intranet sites.
///
userDataElement.setAttribute(getInstanceSettings(inst).dataKey, str);
userDataElement.expires = exp.toUTCString();
userDataElement.save("TinyMCE");
}
function getUserData(inst) {
///
/// IE browsers only. Retrieves a string from the 'UserData' storage area.
///
///
/// Plugin instance from which to get the UserData
///
///
userDataElement.load("TinyMCE");
return userDataElement.getAttribute(getInstanceSettings(inst).dataKey);
}
function removeUserData(inst) {
///
/// IE browsers only. Removes a string from the 'UserData' storage area.
///
///
/// Plugin instance from which to remove the UserData
///
userDataElement.removeAttribute(getInstanceSettings(inst).dataKey);
}
function encodeCookie(str) {
///
/// Encodes a string value intended for storage in a cookie. Used instead of
/// escape() to be more space-efficient and to apply some minor compression.
///
///
/// String to encode for cookie storage
///
///
///
/// Depends on the existence of the cookieEncodeKey property. Used as a lookup table.
/// TO DO: Implement additional compression techniques.
///
return str.replace(/[\x00-\x1f]+| | /gi, " ")
.replace(/(.)\1{5,}|[%&;=<]/g,
function (c) {
if (c.length > 1) {
return ("%0" + c.charAt(0) + c.length.toString() + "%");
}
return cookieEncodeKey[c];
}
);
}
function decodeCookie(str) {
///
/// Decodes a string value that was previously encoded with encodeCookie().
///
///
/// String that was previously encoded with encodeCookie()
///
///
///
/// Depends on the existence of the cookieDecodeKey property. Used as a lookup table.
/// TO DO: Implement additional compression techniques.
///
return str.replace(/%[1-5]|%0(.)(\d+)%/g,
function (c, m, d) {
var a, i, l;
if (c.length == 2) {
return cookieDecodeKey[c];
}
for (a=[], i=0, l=parseInt(d); i
/// Encodes a string value intended for storage in either localStorage or sessionStorage.
///
///
/// String to encode for localStorage or sessionStorage
///
///
///
/// Necessary because a bug in Safari truncates the string at the first comma.
///
return str.replace(/,/g, ",");
}
function decodeStorage(str) {
///
/// Decodes a string value that was previously encoded with encodeStorage().
///
///
/// String that was previously encoded with encodeStorage()
///
///
return str.replace(/,/g, ",");
}
function preloadImage(imageURL) {
///
/// Preloads an image so it will be instantly displayed the first time it's needed.
///
var i = preloadImages.length;
preloadImages[i] = new Image();
preloadImages[i].src = imageURL;
}
function createDelegate(t, method) {
///
/// Returns a delegate function, used for callbacks. Ensures 'this' refers
/// to the desired object.
///
///
/// Object that will be 'this' within the callback function.
///
///
/// Callback function
///
///
return function () {
return method.apply(t);
};
}
function newInstanceSettings(inst) {
///
/// Creates new settings storage for a plugin instance.
///
///
/// The plugin instance for which to create the settings storage.
///
///
var key = inst.key,
s = instanceSettings[key];
if (!s) {
s = instanceSettings[key] = tinymce.extend({}, settingsTemplate, {
dataKey: settingsTemplate.dataKey + key,
saveDelegate: createDelegate(inst, save),
saveFinalDelegate: createDelegate(inst, saveFinal),
restoreDelegate: createDelegate(inst, restore),
disposeDelegate: createDelegate(inst, dispose),
cookieFilter: new RegExp("(?:^|;\\s*)" + settingsTemplate.dataKey + key + "=([^;]*)(?:;|$)", "i")
});
}
return s;
}
function getInstanceSettings(inst) {
///
/// Retrieves the settings for a plugin instance.
///
///
/// The plugin instance for which to retrieve the settings.
///
///
return instanceSettings[inst.key];
}
function removeInstanceSettings(inst) {
///
/// Deletes the settings for a plugin instance.
///
///
/// The plugin instance for which to delete the settings.
///
delete instanceSettings[inst.key];
}
//************************************************************************
// REGISTER PLUGIN
tinymce.PluginManager.add(pluginName, tinymce.plugins.TinyAutoSavePlugin);
})();