├── .bowerrc ├── .gitignore ├── Gruntfile.coffee ├── LICENSE ├── README.md ├── bower.json ├── demo.html ├── lib └── simditor-autosave.js ├── package.json ├── src └── simditor-autosave.coffee └── tea.yaml /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "vendor/bower" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | 17 | .DS_Store 18 | .sass-cache 19 | .grunt 20 | vendor 21 | .bundle -------------------------------------------------------------------------------- /Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (grunt) -> 2 | 3 | grunt.initConfig 4 | 5 | pkg: grunt.file.readJSON 'package.json' 6 | 7 | coffee: 8 | src: 9 | options: 10 | bare: true 11 | files: 12 | 'lib/simditor-autosave.js': 'src/simditor-autosave.coffee' 13 | watch: 14 | src: 15 | files: ['src/*.coffee','demo.html'] 16 | tasks: ['coffee:src', 'umd'] 17 | 18 | umd: 19 | all: 20 | src: 'lib/simditor-autosave.js' 21 | template: 'umd' 22 | amdModuleId: 'simditor-autosave' 23 | objectToExport: 'SimditorAutosave' 24 | globalAlias: 'SimditorAutosave' 25 | deps: 26 | 'default': ['$', 'SimpleModule','Simditor'] 27 | amd: ['jquery', 'simple-module', 'simditor'] 28 | cjs: ['jquery', 'simple-module', 'simditor'] 29 | global: 30 | items: ['jQuery', 'SimpleModule','Simditor'] 31 | prefix: '' 32 | 33 | grunt.loadNpmTasks 'grunt-contrib-coffee' 34 | grunt.loadNpmTasks 'grunt-contrib-watch' 35 | grunt.loadNpmTasks 'grunt-umd' 36 | 37 | grunt.registerTask 'default', ['coffee', 'umd', 'watch'] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 彩程设计 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | simditor-autosave 2 | ================= 3 | 4 | [Simditor](http://simditor.tower.im/) 的官方扩展,运用 HTML5 的 localStorage 技术来自动保存用户输入内容。 5 | 6 | ### 如何使用 7 | 8 | 在 Simditor 的基础上额外引用 simditor-autosave 的脚本。 9 | 10 | ```html 11 | 12 | ``` 13 | 14 | **配置** 15 | 16 | 方法一: 17 | 18 | 直接在使用了 Simditor 对应的 textarea 中设置 `data-autosave` 属性,其值将作为内容保存时的路径。 19 | 例如下面的设置数据将保存在 `url_path/editor-content/autosave/` 中 20 | ```html 21 | 22 | ``` 23 | 24 | 方法二: 25 | 26 | 在 Simditor 初始化时,直接写入配置信息中,例如下面的设置,效果跟上面一样, 27 | 数据也将保存在 `url_path/editor-content/autosave/` 中 28 | ```javascript 29 | new Simditor({ 30 | textarea: textareaElement, 31 | ..., 32 | autosave: 'editor-content' 33 | }) 34 | ``` 35 | 36 | 方法三: 37 | 38 | 在 Simditor 初始化时,可直接指定数据保存路径,如: 39 | ```javascript 40 | new Simditor({ 41 | textarea: textareaElement, 42 | ..., 43 | autosavePath: '/url_path/editor-content/autosave' 44 | }) 45 | ``` 46 | 47 | _注意_ 当方法一和方法二同时定义时,则优先选择方法一中的参数作为保存时的路径 48 | 49 | 另外,可通过 `autosave: false` 关闭自动保存。 50 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simditor-autosave", 3 | "version": "1.1.1", 4 | "authors": [ 5 | "farthinker" 6 | ], 7 | "description": "a Autosave plugin for Simditor", 8 | "main": "lib/simditor-autosave.js", 9 | "keywords": [ 10 | "simditor", 11 | "autosave" 12 | ], 13 | "license": "MIT", 14 | "homepage": "https://github.com/mycolorway/simditor-autosave", 15 | "ignore": [ 16 | "**/.*", 17 | "node_modules", 18 | "vendor", 19 | "Gruntfile.coffee", 20 | "package.json" 21 | ], 22 | "dependencies": { 23 | "jquery": "~2.1.4", 24 | "simditor": "2.3.x" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Simditor 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 37 | 38 |
39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /lib/simditor-autosave.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | // AMD. Register as an anonymous module unless amdModuleId is set 4 | define('simditor-autosave', ["jquery","simple-module","simditor"], function (a0,b1,c2) { 5 | return (root['SimditorAutosave'] = factory(a0,b1,c2)); 6 | }); 7 | } else if (typeof exports === 'object') { 8 | // Node. Does not work with strict CommonJS, but 9 | // only CommonJS-like environments that support module.exports, 10 | // like Node. 11 | module.exports = factory(require("jquery"),require("simple-module"),require("simditor")); 12 | } else { 13 | root['SimditorAutosave'] = factory(jQuery,SimpleModule,Simditor); 14 | } 15 | }(this, function ($, SimpleModule, Simditor) { 16 | 17 | var SimditorAutosave, 18 | extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, 19 | hasProp = {}.hasOwnProperty; 20 | 21 | SimditorAutosave = (function(superClass) { 22 | extend(SimditorAutosave, superClass); 23 | 24 | function SimditorAutosave() { 25 | return SimditorAutosave.__super__.constructor.apply(this, arguments); 26 | } 27 | 28 | SimditorAutosave.pluginName = 'Autosave'; 29 | 30 | SimditorAutosave.prototype.opts = { 31 | autosave: true, 32 | autosavePath: null 33 | }; 34 | 35 | SimditorAutosave.prototype._init = function() { 36 | var currentVal, link, name, val; 37 | this.editor = this._module; 38 | if (!this.opts.autosave) { 39 | return; 40 | } 41 | this.name = typeof this.opts.autosave === 'string' ? this.opts.autosave : 'simditor'; 42 | if (this.opts.autosavePath) { 43 | this.path = this.opts.autosavePath; 44 | } else { 45 | link = $("", { 46 | href: location.href 47 | }); 48 | name = this.editor.textarea.data('autosave') || this.name; 49 | this.path = "/" + (link[0].pathname.replace(/\/$/g, "").replace(/^\//g, "")) + "/autosave/" + name + "/"; 50 | } 51 | if (!this.path) { 52 | return; 53 | } 54 | this.editor.on("valuechanged", (function(_this) { 55 | return function() { 56 | return _this.storage.set(_this.path, _this.editor.getValue()); 57 | }; 58 | })(this)); 59 | this.editor.el.closest('form').on('ajax:success.simditor-' + this.editor.id, (function(_this) { 60 | return function(e) { 61 | return _this.storage.remove(_this.path); 62 | }; 63 | })(this)); 64 | val = this.storage.get(this.path); 65 | if (!val) { 66 | return; 67 | } 68 | currentVal = this.editor.textarea.val(); 69 | if (val === currentVal) { 70 | return; 71 | } 72 | if (this.editor.textarea.is('[data-autosave-confirm]')) { 73 | if (confirm(this.editor.textarea.data('autosave-confirm') || 'Are you sure to restore unsaved changes?')) { 74 | return this.editor.setValue(val); 75 | } else { 76 | return this.storage.remove(this.path); 77 | } 78 | } else { 79 | return this.editor.setValue(val); 80 | } 81 | }; 82 | 83 | SimditorAutosave.prototype.storage = { 84 | supported: function() { 85 | var error; 86 | try { 87 | localStorage.setItem('_storageSupported', 'yes'); 88 | localStorage.removeItem('_storageSupported'); 89 | return true; 90 | } catch (_error) { 91 | error = _error; 92 | return false; 93 | } 94 | }, 95 | set: function(key, val, session) { 96 | var storage; 97 | if (session == null) { 98 | session = false; 99 | } 100 | if (!this.supported()) { 101 | return; 102 | } 103 | storage = session ? sessionStorage : localStorage; 104 | return storage.setItem(key, val); 105 | }, 106 | get: function(key, session) { 107 | var storage; 108 | if (session == null) { 109 | session = false; 110 | } 111 | if (!this.supported()) { 112 | return; 113 | } 114 | storage = session ? sessionStorage : localStorage; 115 | return storage[key]; 116 | }, 117 | remove: function(key, session) { 118 | var storage; 119 | if (session == null) { 120 | session = false; 121 | } 122 | if (!this.supported()) { 123 | return; 124 | } 125 | storage = session ? sessionStorage : localStorage; 126 | return storage.removeItem(key); 127 | } 128 | }; 129 | 130 | return SimditorAutosave; 131 | 132 | })(SimpleModule); 133 | 134 | Simditor.connect(SimditorAutosave); 135 | 136 | return SimditorAutosave; 137 | 138 | })); 139 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simditor-autosave", 3 | "description": "A autosave plugin for Simditor", 4 | "version": "1.1.2", 5 | "homepage": "https://github.com/mycolorway/simditor-autosave", 6 | "main": "lib/simditor-autosave.js", 7 | "author": { 8 | "name": "farthinker", 9 | "email": "farthinker@gmail.com" 10 | }, 11 | "license": "MIT", 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/mycolorway/simditor-autosave.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/mycolorway/simditor-autosave/issues" 18 | }, 19 | "devDependencies": { 20 | "grunt": "0.x", 21 | "grunt-contrib-watch": "0.x", 22 | "grunt-contrib-coffee": "0.x", 23 | "grunt-umd": "2.x" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/simditor-autosave.coffee: -------------------------------------------------------------------------------- 1 | 2 | class SimditorAutosave extends SimpleModule 3 | 4 | @pluginName: 'Autosave' 5 | 6 | opts: 7 | autosave: true 8 | autosavePath: null 9 | 10 | _init: () -> 11 | @editor = @_module 12 | return unless @opts.autosave 13 | 14 | @name = if typeof @opts.autosave == 'string' then @opts.autosave else 'simditor' 15 | 16 | if @opts.autosavePath 17 | @path = @opts.autosavePath 18 | else 19 | link = $( "", {href: location.href}) 20 | name = @editor.textarea.data('autosave') || @name 21 | @path = "/#{link[0].pathname.replace( /\/$/g, "" ).replace( /^\//g, "" )}/autosave/#{name}/" 22 | 23 | return unless @path 24 | 25 | @editor.on "valuechanged", => 26 | @storage.set @path, @editor.getValue() 27 | 28 | @editor.el.closest('form').on 'ajax:success.simditor-' + @editor.id, (e) => 29 | @storage.remove @path 30 | 31 | val = @storage.get @path 32 | return unless val 33 | 34 | currentVal = @editor.textarea.val() 35 | return if val is currentVal 36 | 37 | if @editor.textarea.is('[data-autosave-confirm]') 38 | if confirm(@editor.textarea.data('autosave-confirm') || 'Are you sure to restore unsaved changes?') 39 | @editor.setValue val 40 | else 41 | @storage.remove @path 42 | else 43 | @editor.setValue val 44 | 45 | storage: 46 | supported: () -> 47 | try 48 | localStorage.setItem('_storageSupported', 'yes') 49 | localStorage.removeItem('_storageSupported') 50 | true 51 | catch error 52 | false 53 | set: (key, val, session = false) -> 54 | return unless @.supported() 55 | storage = if session then sessionStorage else localStorage 56 | storage.setItem(key, val) 57 | get: (key, session = false) -> 58 | return unless @.supported() 59 | storage = if session then sessionStorage else localStorage 60 | storage[key] 61 | remove: (key, session = false) -> 62 | return unless @.supported() 63 | storage = if session then sessionStorage else localStorage 64 | storage.removeItem(key); 65 | 66 | Simditor.connect SimditorAutosave 67 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | # 3 | # DO NOT REMOVE OR EDIT THIS WARNING: 4 | # 5 | # This file is auto-generated by the TEA app. It is intended to validate ownership of your repository. 6 | # DO NOT commit this file or accept any PR if you don't know what this is. 7 | # We are aware that spammers will try to use this file to try to profit off others' work. 8 | # We take this very seriously and will take action against any malicious actors. 9 | # 10 | # If you are not the owner of this repository, and someone maliciously opens a commit with this file 11 | # please report it to us at support@tea.xyz. 12 | # 13 | # A constitution without this header is invalid. 14 | --- 15 | version: 2.0.0 16 | codeOwners: 17 | - '0xe189752c4d4C1D404B8aA8ACe008c8Ce14eE520a' 18 | quorum: 1 19 | 20 | --------------------------------------------------------------------------------