PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/BlogEngine/BlogEngine.NET/editors/tiny_mce_3_4_3_1/plugins/autosave/editor_plugin_src.js

#
JavaScript | 431 lines | 218 code | 59 blank | 154 comment | 30 complexity | 488be19b108e54386d5059266d1efc9f MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. /**
  2. * editor_plugin_src.js
  3. *
  4. * Copyright 2009, Moxiecode Systems AB
  5. * Released under LGPL License.
  6. *
  7. * License: http://tinymce.moxiecode.com/license
  8. * Contributing: http://tinymce.moxiecode.com/contributing
  9. *
  10. * Adds auto-save capability to the TinyMCE text editor to rescue content
  11. * inadvertently lost. This plugin was originally developed by Speednet
  12. * and that project can be found here: http://code.google.com/p/tinyautosave/
  13. *
  14. * TECHNOLOGY DISCUSSION:
  15. *
  16. * The plugin attempts to use the most advanced features available in the current browser to save
  17. * as much content as possible. There are a total of four different methods used to autosave the
  18. * content. In order of preference, they are:
  19. *
  20. * 1. localStorage - A new feature of HTML 5, localStorage can store megabytes of data per domain
  21. * on the client computer. Data stored in the localStorage area has no expiration date, so we must
  22. * manage expiring the data ourselves. localStorage is fully supported by IE8, and it is supposed
  23. * to be working in Firefox 3 and Safari 3.2, but in reality is is flaky in those browsers. As
  24. * HTML 5 gets wider support, the AutoSave plugin will use it automatically. In Windows Vista/7,
  25. * localStorage is stored in the following folder:
  26. * C:\Users\[username]\AppData\Local\Microsoft\Internet Explorer\DOMStore\[tempFolder]
  27. *
  28. * 2. sessionStorage - A new feature of HTML 5, sessionStorage works similarly to localStorage,
  29. * except it is designed to expire after a certain amount of time. Because the specification
  30. * around expiration date/time is very loosely-described, it is preferrable to use locaStorage and
  31. * manage the expiration ourselves. sessionStorage has similar storage characteristics to
  32. * localStorage, although it seems to have better support by Firefox 3 at the moment. (That will
  33. * certainly change as Firefox continues getting better at HTML 5 adoption.)
  34. *
  35. * 3. UserData - A very under-exploited feature of Microsoft Internet Explorer, UserData is a
  36. * way to store up to 128K of data per "document", or up to 1MB of data per domain, on the client
  37. * computer. The feature is available for IE 5+, which makes it available for every version of IE
  38. * supported by TinyMCE. The content is persistent across browser restarts and expires on the
  39. * date/time specified, just like a cookie. However, the data is not cleared when the user clears
  40. * cookies on the browser, which makes it well-suited for rescuing autosaved content. UserData,
  41. * like other Microsoft IE browser technologies, is implemented as a behavior attached to a
  42. * specific DOM object, so in this case we attach the behavior to the same DOM element that the
  43. * TinyMCE editor instance is attached to.
  44. */
  45. (function(tinymce) {
  46. // Setup constants to help the compressor to reduce script size
  47. var PLUGIN_NAME = 'autosave',
  48. RESTORE_DRAFT = 'restoredraft',
  49. TRUE = true,
  50. undefined,
  51. unloadHandlerAdded,
  52. Dispatcher = tinymce.util.Dispatcher;
  53. /**
  54. * This plugin adds auto-save capability to the TinyMCE text editor to rescue content
  55. * inadvertently lost. By using localStorage.
  56. *
  57. * @class tinymce.plugins.AutoSave
  58. */
  59. tinymce.create('tinymce.plugins.AutoSave', {
  60. /**
  61. * Initializes the plugin, this will be executed after the plugin has been created.
  62. * This call is done before the editor instance has finished it's initialization so use the onInit event
  63. * of the editor instance to intercept that event.
  64. *
  65. * @method init
  66. * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
  67. * @param {string} url Absolute URL to where the plugin is located.
  68. */
  69. init : function(ed, url) {
  70. var self = this, settings = ed.settings;
  71. self.editor = ed;
  72. // Parses the specified time string into a milisecond number 10m, 10s etc.
  73. function parseTime(time) {
  74. var multipels = {
  75. s : 1000,
  76. m : 60000
  77. };
  78. time = /^(\d+)([ms]?)$/.exec('' + time);
  79. return (time[2] ? multipels[time[2]] : 1) * parseInt(time);
  80. };
  81. // Default config
  82. tinymce.each({
  83. ask_before_unload : TRUE,
  84. interval : '30s',
  85. retention : '20m',
  86. minlength : 50
  87. }, function(value, key) {
  88. key = PLUGIN_NAME + '_' + key;
  89. if (settings[key] === undefined)
  90. settings[key] = value;
  91. });
  92. // Parse times
  93. settings.autosave_interval = parseTime(settings.autosave_interval);
  94. settings.autosave_retention = parseTime(settings.autosave_retention);
  95. // Register restore button
  96. ed.addButton(RESTORE_DRAFT, {
  97. title : PLUGIN_NAME + ".restore_content",
  98. onclick : function() {
  99. if (ed.getContent({draft: true}).replace(/\s|&nbsp;|<\/?p[^>]*>|<br[^>]*>/gi, "").length > 0) {
  100. // Show confirm dialog if the editor isn't empty
  101. ed.windowManager.confirm(
  102. PLUGIN_NAME + ".warning_message",
  103. function(ok) {
  104. if (ok)
  105. self.restoreDraft();
  106. }
  107. );
  108. } else
  109. self.restoreDraft();
  110. }
  111. });
  112. // Enable/disable restoredraft button depending on if there is a draft stored or not
  113. ed.onNodeChange.add(function() {
  114. var controlManager = ed.controlManager;
  115. if (controlManager.get(RESTORE_DRAFT))
  116. controlManager.setDisabled(RESTORE_DRAFT, !self.hasDraft());
  117. });
  118. ed.onInit.add(function() {
  119. // Check if the user added the restore button, then setup auto storage logic
  120. if (ed.controlManager.get(RESTORE_DRAFT)) {
  121. // Setup storage engine
  122. self.setupStorage(ed);
  123. // Auto save contents each interval time
  124. setInterval(function() {
  125. self.storeDraft();
  126. ed.nodeChanged();
  127. }, settings.autosave_interval);
  128. }
  129. });
  130. /**
  131. * This event gets fired when a draft is stored to local storage.
  132. *
  133. * @event onStoreDraft
  134. * @param {tinymce.plugins.AutoSave} sender Plugin instance sending the event.
  135. * @param {Object} draft Draft object containing the HTML contents of the editor.
  136. */
  137. self.onStoreDraft = new Dispatcher(self);
  138. /**
  139. * This event gets fired when a draft is restored from local storage.
  140. *
  141. * @event onStoreDraft
  142. * @param {tinymce.plugins.AutoSave} sender Plugin instance sending the event.
  143. * @param {Object} draft Draft object containing the HTML contents of the editor.
  144. */
  145. self.onRestoreDraft = new Dispatcher(self);
  146. /**
  147. * This event gets fired when a draft removed/expired.
  148. *
  149. * @event onRemoveDraft
  150. * @param {tinymce.plugins.AutoSave} sender Plugin instance sending the event.
  151. * @param {Object} draft Draft object containing the HTML contents of the editor.
  152. */
  153. self.onRemoveDraft = new Dispatcher(self);
  154. // Add ask before unload dialog only add one unload handler
  155. if (!unloadHandlerAdded) {
  156. window.onbeforeunload = tinymce.plugins.AutoSave._beforeUnloadHandler;
  157. unloadHandlerAdded = TRUE;
  158. }
  159. },
  160. /**
  161. * Returns information about the plugin as a name/value array.
  162. * The current keys are longname, author, authorurl, infourl and version.
  163. *
  164. * @method getInfo
  165. * @return {Object} Name/value array containing information about the plugin.
  166. */
  167. getInfo : function() {
  168. return {
  169. longname : 'Auto save',
  170. author : 'Moxiecode Systems AB',
  171. authorurl : 'http://tinymce.moxiecode.com',
  172. infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autosave',
  173. version : tinymce.majorVersion + "." + tinymce.minorVersion
  174. };
  175. },
  176. /**
  177. * Returns an expiration date UTC string.
  178. *
  179. * @method getExpDate
  180. * @return {String} Expiration date UTC string.
  181. */
  182. getExpDate : function() {
  183. return new Date(
  184. new Date().getTime() + this.editor.settings.autosave_retention
  185. ).toUTCString();
  186. },
  187. /**
  188. * This method will setup the storage engine. If the browser has support for it.
  189. *
  190. * @method setupStorage
  191. */
  192. setupStorage : function(ed) {
  193. var self = this, testKey = PLUGIN_NAME + '_test', testVal = "OK";
  194. self.key = PLUGIN_NAME + ed.id;
  195. // Loop though each storage engine type until we find one that works
  196. tinymce.each([
  197. function() {
  198. // Try HTML5 Local Storage
  199. if (localStorage) {
  200. localStorage.setItem(testKey, testVal);
  201. if (localStorage.getItem(testKey) === testVal) {
  202. localStorage.removeItem(testKey);
  203. return localStorage;
  204. }
  205. }
  206. },
  207. function() {
  208. // Try HTML5 Session Storage
  209. if (sessionStorage) {
  210. sessionStorage.setItem(testKey, testVal);
  211. if (sessionStorage.getItem(testKey) === testVal) {
  212. sessionStorage.removeItem(testKey);
  213. return sessionStorage;
  214. }
  215. }
  216. },
  217. function() {
  218. // Try IE userData
  219. if (tinymce.isIE) {
  220. ed.getElement().style.behavior = "url('#default#userData')";
  221. // Fake localStorage on old IE
  222. return {
  223. autoExpires : TRUE,
  224. setItem : function(key, value) {
  225. var userDataElement = ed.getElement();
  226. userDataElement.setAttribute(key, value);
  227. userDataElement.expires = self.getExpDate();
  228. try {
  229. userDataElement.save("TinyMCE");
  230. } catch (e) {
  231. // Ignore, saving might fail if "Userdata Persistence" is disabled in IE
  232. }
  233. },
  234. getItem : function(key) {
  235. var userDataElement = ed.getElement();
  236. try {
  237. userDataElement.load("TinyMCE");
  238. return userDataElement.getAttribute(key);
  239. } catch (e) {
  240. // Ignore, loading might fail if "Userdata Persistence" is disabled in IE
  241. return null;
  242. }
  243. },
  244. removeItem : function(key) {
  245. ed.getElement().removeAttribute(key);
  246. }
  247. };
  248. }
  249. },
  250. ], function(setup) {
  251. // Try executing each function to find a suitable storage engine
  252. try {
  253. self.storage = setup();
  254. if (self.storage)
  255. return false;
  256. } catch (e) {
  257. // Ignore
  258. }
  259. });
  260. },
  261. /**
  262. * This method will store the current contents in the the storage engine.
  263. *
  264. * @method storeDraft
  265. */
  266. storeDraft : function() {
  267. var self = this, storage = self.storage, editor = self.editor, expires, content;
  268. // Is the contents dirty
  269. if (storage) {
  270. // If there is no existing key and the contents hasn't been changed since
  271. // it's original value then there is no point in saving a draft
  272. if (!storage.getItem(self.key) && !editor.isDirty())
  273. return;
  274. // Store contents if the contents if longer than the minlength of characters
  275. content = editor.getContent({draft: true});
  276. if (content.length > editor.settings.autosave_minlength) {
  277. expires = self.getExpDate();
  278. // Store expiration date if needed IE userData has auto expire built in
  279. if (!self.storage.autoExpires)
  280. self.storage.setItem(self.key + "_expires", expires);
  281. self.storage.setItem(self.key, content);
  282. self.onStoreDraft.dispatch(self, {
  283. expires : expires,
  284. content : content
  285. });
  286. }
  287. }
  288. },
  289. /**
  290. * This method will restore the contents from the storage engine back to the editor.
  291. *
  292. * @method restoreDraft
  293. */
  294. restoreDraft : function() {
  295. var self = this, storage = self.storage;
  296. if (storage) {
  297. content = storage.getItem(self.key);
  298. if (content) {
  299. self.editor.setContent(content);
  300. self.onRestoreDraft.dispatch(self, {
  301. content : content
  302. });
  303. }
  304. }
  305. },
  306. /**
  307. * This method will return true/false if there is a local storage draft available.
  308. *
  309. * @method hasDraft
  310. * @return {boolean} true/false state if there is a local draft.
  311. */
  312. hasDraft : function() {
  313. var self = this, storage = self.storage, expDate, exists;
  314. if (storage) {
  315. // Does the item exist at all
  316. exists = !!storage.getItem(self.key);
  317. if (exists) {
  318. // Storage needs autoexpire
  319. if (!self.storage.autoExpires) {
  320. expDate = new Date(storage.getItem(self.key + "_expires"));
  321. // Contents hasn't expired
  322. if (new Date().getTime() < expDate.getTime())
  323. return TRUE;
  324. // Remove it if it has
  325. self.removeDraft();
  326. } else
  327. return TRUE;
  328. }
  329. }
  330. return false;
  331. },
  332. /**
  333. * Removes the currently stored draft.
  334. *
  335. * @method removeDraft
  336. */
  337. removeDraft : function() {
  338. var self = this, storage = self.storage, key = self.key, content;
  339. if (storage) {
  340. // Get current contents and remove the existing draft
  341. content = storage.getItem(key);
  342. storage.removeItem(key);
  343. storage.removeItem(key + "_expires");
  344. // Dispatch remove event if we had any contents
  345. if (content) {
  346. self.onRemoveDraft.dispatch(self, {
  347. content : content
  348. });
  349. }
  350. }
  351. },
  352. "static" : {
  353. // Internal unload handler will be called before the page is unloaded
  354. _beforeUnloadHandler : function(e) {
  355. var msg;
  356. tinymce.each(tinyMCE.editors, function(ed) {
  357. // Store a draft for each editor instance
  358. if (ed.plugins.autosave)
  359. ed.plugins.autosave.storeDraft();
  360. // Never ask in fullscreen mode
  361. if (ed.getParam("fullscreen_is_enabled"))
  362. return;
  363. // Setup a return message if the editor is dirty
  364. if (!msg && ed.isDirty() && ed.getParam("autosave_ask_before_unload"))
  365. msg = ed.getLang("autosave.unload_msg");
  366. });
  367. return msg;
  368. }
  369. }
  370. });
  371. tinymce.PluginManager.add('autosave', tinymce.plugins.AutoSave);
  372. })(tinymce);