PageRenderTime 51ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/sites/all/libraries/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin_src.js

https://gitlab.com/endomorphosis/toolshed
JavaScript | 422 lines | 211 code | 59 blank | 152 comment | 30 complexity | 7540f06776c6f3e5ca7d6f88b2f0ab35 MD5 | raw file
  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().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. userDataElement.save("TinyMCE");
  229. },
  230. getItem : function(key) {
  231. var userDataElement = ed.getElement();
  232. userDataElement.load("TinyMCE");
  233. return userDataElement.getAttribute(key);
  234. },
  235. removeItem : function(key) {
  236. ed.getElement().removeAttribute(key);
  237. }
  238. };
  239. }
  240. },
  241. ], function(setup) {
  242. // Try executing each function to find a suitable storage engine
  243. try {
  244. self.storage = setup();
  245. if (self.storage)
  246. return false;
  247. } catch (e) {
  248. // Ignore
  249. }
  250. });
  251. },
  252. /**
  253. * This method will store the current contents in the the storage engine.
  254. *
  255. * @method storeDraft
  256. */
  257. storeDraft : function() {
  258. var self = this, storage = self.storage, editor = self.editor, expires, content;
  259. // Is the contents dirty
  260. if (storage) {
  261. // If there is no existing key and the contents hasn't been changed since
  262. // it's original value then there is no point in saving a draft
  263. if (!storage.getItem(self.key) && !editor.isDirty())
  264. return;
  265. // Store contents if the contents if longer than the minlength of characters
  266. content = editor.getContent();
  267. if (content.length > editor.settings.autosave_minlength) {
  268. expires = self.getExpDate();
  269. // Store expiration date if needed IE userData has auto expire built in
  270. if (!self.storage.autoExpires)
  271. self.storage.setItem(self.key + "_expires", expires);
  272. self.storage.setItem(self.key, content);
  273. self.onStoreDraft.dispatch(self, {
  274. expires : expires,
  275. content : content
  276. });
  277. }
  278. }
  279. },
  280. /**
  281. * This method will restore the contents from the storage engine back to the editor.
  282. *
  283. * @method restoreDraft
  284. */
  285. restoreDraft : function() {
  286. var self = this, storage = self.storage;
  287. if (storage) {
  288. content = storage.getItem(self.key);
  289. if (content) {
  290. self.editor.setContent(content);
  291. self.onRestoreDraft.dispatch(self, {
  292. content : content
  293. });
  294. }
  295. }
  296. },
  297. /**
  298. * This method will return true/false if there is a local storage draft available.
  299. *
  300. * @method hasDraft
  301. * @return {boolean} true/false state if there is a local draft.
  302. */
  303. hasDraft : function() {
  304. var self = this, storage = self.storage, expDate, exists;
  305. if (storage) {
  306. // Does the item exist at all
  307. exists = !!storage.getItem(self.key);
  308. if (exists) {
  309. // Storage needs autoexpire
  310. if (!self.storage.autoExpires) {
  311. expDate = new Date(storage.getItem(self.key + "_expires"));
  312. // Contents hasn't expired
  313. if (new Date().getTime() < expDate.getTime())
  314. return TRUE;
  315. // Remove it if it has
  316. self.removeDraft();
  317. } else
  318. return TRUE;
  319. }
  320. }
  321. return false;
  322. },
  323. /**
  324. * Removes the currently stored draft.
  325. *
  326. * @method removeDraft
  327. */
  328. removeDraft : function() {
  329. var self = this, storage = self.storage, key = self.key, content;
  330. if (storage) {
  331. // Get current contents and remove the existing draft
  332. content = storage.getItem(key);
  333. storage.removeItem(key);
  334. storage.removeItem(key + "_expires");
  335. // Dispatch remove event if we had any contents
  336. if (content) {
  337. self.onRemoveDraft.dispatch(self, {
  338. content : content
  339. });
  340. }
  341. }
  342. },
  343. "static" : {
  344. // Internal unload handler will be called before the page is unloaded
  345. _beforeUnloadHandler : function(e) {
  346. var msg;
  347. tinymce.each(tinyMCE.editors, function(ed) {
  348. // Store a draft for each editor instance
  349. if (ed.plugins.autosave)
  350. ed.plugins.autosave.storeDraft();
  351. // Never ask in fullscreen mode
  352. if (ed.getParam("fullscreen_is_enabled"))
  353. return;
  354. // Setup a return message if the editor is dirty
  355. if (!msg && ed.isDirty() && ed.getParam("autosave_ask_before_unload"))
  356. msg = ed.getLang("autosave.unload_msg");
  357. });
  358. return msg;
  359. }
  360. }
  361. });
  362. tinymce.PluginManager.add('autosave', tinymce.plugins.AutoSave);
  363. })(tinymce);