/**

 * 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 size
 var 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 config
   tinymce.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 times
   settings.autosave_interval = parseTime(settings.autosave_interval);
   settings.autosave_retention = parseTime(settings.autosave_retention);
   // Register restore button
   ed.addButton(RESTORE_DRAFT, {
    title : PLUGIN_NAME + ".restore_content",
    onclick : function() {
     if (ed.getContent().replace(/\s|&nbsp;|<\/?p[^>]*>|<br[^>]*>/gi, "").length > 0) {
      // Show confirm dialog if the editor isn't empty
      ed.windowManager.confirm(
       PLUGIN_NAME + ".warning_message",
       function(ok) {
        if (ok)
         self.restoreDraft();
       }
      );
     } else
      self.restoreDraft();
    }
   });
   // Enable/disable restoredraft button depending on if there is a draft stored or not
   ed.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 logic
    if (ed.controlManager.get(RESTORE_DRAFT)) {
     // Setup storage engine
     self.setupStorage(ed);
     // Auto save contents each interval time
     setInterval(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 handler
   if (!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 works
   tinymce.each([
    function() {
     // Try HTML5 Local Storage
     if (localStorage) {
      localStorage.setItem(testKey, testVal);
      if (localStorage.getItem(testKey) === testVal) {
       localStorage.removeItem(testKey);
       return localStorage;
      }
     }
    },
    function() {
     // Try HTML5 Session Storage
     if (sessionStorage) {
      sessionStorage.setItem(testKey, testVal);
      if (sessionStorage.getItem(testKey) === testVal) {
       sessionStorage.removeItem(testKey);
       return sessionStorage;
      }
     }
    },
    function() {
     // Try IE userData
     if (tinymce.isIE) {
      ed.getElement().style.behavior = "url('#default#userData')";
      // Fake localStorage on old IE
      return {
       autoExpires : TRUE,
       setItem : function(key, value) {
        var userDataElement = ed.getElement();
        userDataElement.setAttribute(key, value);
        userDataElement.expires = self.getExpDate();
        userDataElement.save("TinyMCE");
       },
       getItem : function(key) {
        var userDataElement = ed.getElement();
        userDataElement.load("TinyMCE");
        return userDataElement.getAttribute(key);
       },
       removeItem : function(key) {
        ed.getElement().removeAttribute(key);
       }
      };
     }
    },
   ], function(setup) {
    // Try executing each function to find a suitable storage engine
    try {
     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 dirty
   if (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 draft
    if (!storage.getItem(self.key) && !editor.isDirty())
     return;
    // Store contents if the contents if longer than the minlength of characters
    content = editor.getContent();
    if (content.length > editor.settings.autosave_minlength) {
     expires = self.getExpDate();
     // Store expiration date if needed IE userData has auto expire built in
     if (!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;
   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 all
    exists = !!storage.getItem(self.key);
    if (exists) {
     // Storage needs autoexpire
     if (!self.storage.autoExpires) {
      expDate = new Date(storage.getItem(self.key + "_expires"));
      // Contents hasn't expired
      if (new Date().getTime() < expDate.getTime())
       return TRUE;
      // Remove it if it has
      self.removeDraft();
     } else
      return 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 draft
    content = storage.getItem(key);
    storage.removeItem(key);
    storage.removeItem(key + "_expires");
    // Dispatch remove event if we had any contents
    if (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 instance
     if (ed.plugins.autosave)
      ed.plugins.autosave.storeDraft();
     // Never ask in fullscreen mode
     if (ed.getParam("fullscreen_is_enabled"))
      return;
     // Setup a return message if the editor is dirty
     if (!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);
