Blame | Letzte Änderung | Log anzeigen | RSS feed
/*** editor_plugin_src.js** Copyright 2009, Moxiecode Systems AB* Released under LGPL License.** License: http://tinymce.moxiecode.com/license* Contributing: http://tinymce.moxiecode.com/contributing** Adds auto-save capability to the TinyMCE text editor to rescue content* inadvertently lost. This plugin was originally developed by Speednet* and that project can be found here: http://code.google.com/p/tinyautosave/** 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 AutoSave 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.*/(function(tinymce) {// Setup constants to help the compressor to reduce script sizevar PLUGIN_NAME = 'autosave',RESTORE_DRAFT = 'restoredraft',TRUE = true,undefined,unloadHandlerAdded,Dispatcher = tinymce.util.Dispatcher;/*** This plugin adds auto-save capability to the TinyMCE text editor to rescue content* inadvertently lost. By using localStorage.** @class tinymce.plugins.AutoSave*/tinymce.create('tinymce.plugins.AutoSave', {/*** Initializes the plugin, this will be executed after the plugin has been created.* This call is done before the editor instance has finished it's initialization so use the onInit event* of the editor instance to intercept that event.** @method init* @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.* @param {string} url Absolute URL to where the plugin is located.*/init : function(ed, url) {var self = this, settings = ed.settings;self.editor = ed;// Parses the specified time string into a milisecond number 10m, 10s etc.function parseTime(time) {var multipels = {s : 1000,m : 60000};time = /^(\d+)([ms]?)$/.exec('' + time);return (time[2] ? multipels[time[2]] : 1) * parseInt(time);};// Default configtinymce.each({ask_before_unload : TRUE,interval : '30s',retention : '20m',minlength : 50}, function(value, key) {key = PLUGIN_NAME + '_' + key;if (settings[key] === undefined)settings[key] = value;});// Parse timessettings.autosave_interval = parseTime(settings.autosave_interval);settings.autosave_retention = parseTime(settings.autosave_retention);// Register restore buttoned.addButton(RESTORE_DRAFT, {title : PLUGIN_NAME + ".restore_content",onclick : function() {if (ed.getContent({draft: true}).replace(/\s| |<\/?p[^>]*>|<br[^>]*>/gi, "").length > 0) {// Show confirm dialog if the editor isn't emptyed.windowManager.confirm(PLUGIN_NAME + ".warning_message",function(ok) {if (ok)self.restoreDraft();});} elseself.restoreDraft();}});// Enable/disable restoredraft button depending on if there is a draft stored or noted.onNodeChange.add(function() {var controlManager = ed.controlManager;if (controlManager.get(RESTORE_DRAFT))controlManager.setDisabled(RESTORE_DRAFT, !self.hasDraft());});ed.onInit.add(function() {// Check if the user added the restore button, then setup auto storage logicif (ed.controlManager.get(RESTORE_DRAFT)) {// Setup storage engineself.setupStorage(ed);// Auto save contents each interval timesetInterval(function() {self.storeDraft();ed.nodeChanged();}, settings.autosave_interval);}});/*** This event gets fired when a draft is stored to local storage.** @event onStoreDraft* @param {tinymce.plugins.AutoSave} sender Plugin instance sending the event.* @param {Object} draft Draft object containing the HTML contents of the editor.*/self.onStoreDraft = new Dispatcher(self);/*** This event gets fired when a draft is restored from local storage.** @event onStoreDraft* @param {tinymce.plugins.AutoSave} sender Plugin instance sending the event.* @param {Object} draft Draft object containing the HTML contents of the editor.*/self.onRestoreDraft = new Dispatcher(self);/*** This event gets fired when a draft removed/expired.** @event onRemoveDraft* @param {tinymce.plugins.AutoSave} sender Plugin instance sending the event.* @param {Object} draft Draft object containing the HTML contents of the editor.*/self.onRemoveDraft = new Dispatcher(self);// Add ask before unload dialog only add one unload handlerif (!unloadHandlerAdded) {window.onbeforeunload = tinymce.plugins.AutoSave._beforeUnloadHandler;unloadHandlerAdded = TRUE;}},/*** Returns information about the plugin as a name/value array.* The current keys are longname, author, authorurl, infourl and version.** @method getInfo* @return {Object} Name/value array containing information about the plugin.*/getInfo : function() {return {longname : 'Auto save',author : 'Moxiecode Systems AB',authorurl : 'http://tinymce.moxiecode.com',infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autosave',version : tinymce.majorVersion + "." + tinymce.minorVersion};},/*** Returns an expiration date UTC string.** @method getExpDate* @return {String} Expiration date UTC string.*/getExpDate : function() {return new Date(new Date().getTime() + this.editor.settings.autosave_retention).toUTCString();},/*** This method will setup the storage engine. If the browser has support for it.** @method setupStorage*/setupStorage : function(ed) {var self = this, testKey = PLUGIN_NAME + '_test', testVal = "OK";self.key = PLUGIN_NAME + ed.id;// Loop though each storage engine type until we find one that workstinymce.each([function() {// Try HTML5 Local Storageif (localStorage) {localStorage.setItem(testKey, testVal);if (localStorage.getItem(testKey) === testVal) {localStorage.removeItem(testKey);return localStorage;}}},function() {// Try HTML5 Session Storageif (sessionStorage) {sessionStorage.setItem(testKey, testVal);if (sessionStorage.getItem(testKey) === testVal) {sessionStorage.removeItem(testKey);return sessionStorage;}}},function() {// Try IE userDataif (tinymce.isIE) {ed.getElement().style.behavior = "url('#default#userData')";// Fake localStorage on old IEreturn {autoExpires : TRUE,setItem : function(key, value) {var userDataElement = ed.getElement();userDataElement.setAttribute(key, value);userDataElement.expires = self.getExpDate();try {userDataElement.save("TinyMCE");} catch (e) {// Ignore, saving might fail if "Userdata Persistence" is disabled in IE}},getItem : function(key) {var userDataElement = ed.getElement();try {userDataElement.load("TinyMCE");return userDataElement.getAttribute(key);} catch (e) {// Ignore, loading might fail if "Userdata Persistence" is disabled in IEreturn null;}},removeItem : function(key) {ed.getElement().removeAttribute(key);}};}},], function(setup) {// Try executing each function to find a suitable storage enginetry {self.storage = setup();if (self.storage)return false;} catch (e) {// Ignore}});},/*** This method will store the current contents in the the storage engine.** @method storeDraft*/storeDraft : function() {var self = this, storage = self.storage, editor = self.editor, expires, content;// Is the contents dirtyif (storage) {// If there is no existing key and the contents hasn't been changed since// it's original value then there is no point in saving a draftif (!storage.getItem(self.key) && !editor.isDirty())return;// Store contents if the contents if longer than the minlength of characterscontent = editor.getContent({draft: true});if (content.length > editor.settings.autosave_minlength) {expires = self.getExpDate();// Store expiration date if needed IE userData has auto expire built inif (!self.storage.autoExpires)self.storage.setItem(self.key + "_expires", expires);self.storage.setItem(self.key, content);self.onStoreDraft.dispatch(self, {expires : expires,content : content});}}},/*** This method will restore the contents from the storage engine back to the editor.** @method restoreDraft*/restoreDraft : function() {var self = this, storage = self.storage, content;if (storage) {content = storage.getItem(self.key);if (content) {self.editor.setContent(content);self.onRestoreDraft.dispatch(self, {content : content});}}},/*** This method will return true/false if there is a local storage draft available.** @method hasDraft* @return {boolean} true/false state if there is a local draft.*/hasDraft : function() {var self = this, storage = self.storage, expDate, exists;if (storage) {// Does the item exist at allexists = !!storage.getItem(self.key);if (exists) {// Storage needs autoexpireif (!self.storage.autoExpires) {expDate = new Date(storage.getItem(self.key + "_expires"));// Contents hasn't expiredif (new Date().getTime() < expDate.getTime())return TRUE;// Remove it if it hasself.removeDraft();} elsereturn TRUE;}}return false;},/*** Removes the currently stored draft.** @method removeDraft*/removeDraft : function() {var self = this, storage = self.storage, key = self.key, content;if (storage) {// Get current contents and remove the existing draftcontent = storage.getItem(key);storage.removeItem(key);storage.removeItem(key + "_expires");// Dispatch remove event if we had any contentsif (content) {self.onRemoveDraft.dispatch(self, {content : content});}}},"static" : {// Internal unload handler will be called before the page is unloaded_beforeUnloadHandler : function(e) {var msg;tinymce.each(tinyMCE.editors, function(ed) {// Store a draft for each editor instanceif (ed.plugins.autosave)ed.plugins.autosave.storeDraft();// Never ask in fullscreen modeif (ed.getParam("fullscreen_is_enabled"))return;// Setup a return message if the editor is dirtyif (!msg && ed.isDirty() && ed.getParam("autosave_ask_before_unload"))msg = ed.getLang("autosave.unload_msg");});return msg;}}});tinymce.PluginManager.add('autosave', tinymce.plugins.AutoSave);})(tinymce);