├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── intro.rst ├── resources ├── clipboardimage.gif ├── dropimage.gif ├── emoji.png ├── markdown.gif └── without_admin.gif ├── setup.py ├── simditor ├── __init__.py ├── fields.py ├── image │ ├── __init__.py │ ├── dummy_backend.py │ └── pillow_backend.py ├── image_processing.py ├── static │ └── simditor │ │ ├── fonts │ │ ├── icomoon.eot │ │ ├── icomoon.svg │ │ ├── icomoon.ttf │ │ ├── icomoon.woff │ │ └── selection.json │ │ ├── images │ │ └── image.png │ │ ├── scripts │ │ ├── hotkeys.js │ │ ├── hotkeys.min.js │ │ ├── jquery.min.js │ │ ├── markdown.all.js │ │ ├── marked.min.js │ │ ├── module.js │ │ ├── module.min.js │ │ ├── simditor-checklist.js │ │ ├── simditor-checklist.min.js │ │ ├── simditor-dropzone.js │ │ ├── simditor-dropzone.min.js │ │ ├── simditor-fullscreen.js │ │ ├── simditor-fullscreen.min.js │ │ ├── simditor-markdown.js │ │ ├── simditor-markdown.min.js │ │ ├── simditor.ext.min.js │ │ ├── simditor.js │ │ ├── simditor.main.min.js │ │ ├── simditor.min.js │ │ ├── to-markdown.js │ │ ├── to-markdown.min.js │ │ ├── uploader.js │ │ └── uploader.min.js │ │ ├── simditor-init.js │ │ └── styles │ │ ├── editor.scss │ │ ├── fonticon.scss │ │ ├── simditor-checklist.css │ │ ├── simditor-checklist.min.css │ │ ├── simditor-fullscreen.css │ │ ├── simditor-fullscreen.min.css │ │ ├── simditor-markdown.css │ │ ├── simditor-markdown.min.css │ │ ├── simditor.css │ │ ├── simditor.main.min.css │ │ ├── simditor.min.css │ │ └── simditor.scss ├── templates │ └── simditor │ │ └── widget.html ├── urls.py ├── utils.py ├── views.py └── widgets.py ├── simditor_demo ├── app │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── manage.py ├── simditor_demo │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── templates │ └── index.html └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | .DS_Store 104 | db.sqlite3 105 | 106 | simditor/static/ 107 | static/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 版本变更记录 2 | 3 | **0.0.15** 4 | - 添加图片最大上传限制 5 | 6 | **0.0.13** 7 | 8 | - 修改图片上传代码 9 | - 添加django admin之外的示例 10 | 11 | 12 | **0.0.12** 13 | 14 | - 修复Django2.1 之前版本兼容问题 15 | 16 | **0.0.11** 17 | 18 | - 修复Django2.1 renderer 报错 19 | 20 | **0.0.10** 21 | 22 | - 修复样式问题 23 | 24 | **0.0.8** 25 | 26 | - emoji 功能 27 | 28 | 29 | **0.0.5** 30 | 31 | - Markdown 功能 32 | - 图片上传 33 | 34 | 35 | **0.0.1** 36 | 37 | - 初始化目录 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 codingcat 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. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include intro.rst 3 | recursive-include simditor/static * 4 | recursive-include simditor/templates * 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # django-simditor 2 | django simditor 3 | 4 | Installation 5 | ------------ 6 | 7 | ```bash 8 | pip install django-simditor 9 | ``` 10 | 11 | **Add `simditor` to your `INSTALLED_APPS` setting.** 12 | 13 | ```python 14 | from django.db import models 15 | from simditor.fields import RichTextField 16 | 17 | 18 | class Post(models.Model): 19 | content = RichTextField() 20 | ``` 21 | 22 | **emoji** 23 | 24 | ![](resources/emoji.png) 25 | 26 | **Markdown** 27 | 28 | ![](resources/markdown.gif) 29 | 30 | **Image upload config** 31 | 32 | *drop image* 33 | 34 | ![](resources/dropimage.gif) 35 | 36 | *clipboard image* 37 | 38 | ![](resources/clipboardimage.gif) 39 | 40 | 41 | `urls.py` 42 | 43 | 44 | ```python 45 | urlpatterns = [ 46 | url(r'^admin/', admin.site.urls), 47 | url(r'^simditor/', include('simditor.urls')) # add this line 48 | ] 49 | ``` 50 | 51 | `settings.py` 52 | 53 | ```python 54 | INSTALLED_APPS = [ 55 | 'django.contrib.admin', 56 | 'django.contrib.auth', 57 | 'django.contrib.contenttypes', 58 | 'django.contrib.sessions', 59 | 'django.contrib.messages', 60 | 'django.contrib.staticfiles', 61 | 62 | 'simditor' 63 | ] 64 | 65 | SIMDITOR_UPLOAD_PATH = 'uploads/' 66 | SIMDITOR_IMAGE_BACKEND = 'pillow' 67 | 68 | SIMDITOR_TOOLBAR = [ 69 | 'title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale', 70 | 'color', '|', 'ol', 'ul', 'blockquote', 'code', 'table', '|', 'link', 71 | 'image', 'hr', '|', 'indent', 'outdent', 'alignment', 'fullscreen', 72 | 'markdown', 'emoji' 73 | ] 74 | 75 | SIMDITOR_CONFIGS = { 76 | 'toolbar': SIMDITOR_TOOLBAR, 77 | 'upload': { 78 | 'url': '/simditor/upload/', 79 | 'fileKey': 'upload', 80 | 'image_size': 1024 * 1024 * 4 # max image size 4MB 81 | }, 82 | 'emoji': { 83 | 'imagePath': '/static/simditor/images/emoji/' 84 | } 85 | } 86 | ``` 87 | 88 | ## without django admin demo 89 | 90 | ![](./resources/without_admin.gif) 91 | 92 | ```python 93 | from django.views import generic 94 | from django.views.decorators.csrf import csrf_exempt 95 | 96 | from simditor.views import upload_handler 97 | 98 | 99 | class ImageUploadView(generic.View): 100 | """ImageUploadView.""" 101 | 102 | http_method_names = ['post'] 103 | 104 | def post(self, request, **kwargs): 105 | """Post.""" 106 | return upload_handler(request) 107 | 108 | 109 | urlpatterns = [ 110 | url(r'^$', IndexView.as_view(), name='simditor-form'), 111 | url(r'^simditor/upload', csrf_exempt(ImageUploadView.as_view())), 112 | ] 113 | ``` 114 | 115 | ```python 116 | # IndexView 117 | from django import forms 118 | 119 | from django.views import generic 120 | 121 | from simditor.fields import RichTextFormField 122 | try: 123 | from django.urls import reverse 124 | except ImportError: # Django < 2.0 125 | from django.core.urlresolvers import reverse 126 | 127 | 128 | class SimditorForm(forms.Form): 129 | content = RichTextFormField() 130 | 131 | 132 | class IndexView(generic.FormView): 133 | form_class = SimditorForm 134 | 135 | template_name = 'index.html' 136 | 137 | def get_success_url(self): 138 | return reverse('simditor-form') 139 | ``` 140 | 141 | `index.html` 142 | 143 | ```html 144 | 145 | 146 | 147 | 148 | Document 149 | 150 | 151 |
152 | {% csrf_token %} 153 | {{ form.media }} 154 | {{ form.as_p }} 155 |

156 |
157 | 158 | 159 | ``` 160 | 161 | > more detail you can check simditor_demo 162 | -------------------------------------------------------------------------------- /intro.rst: -------------------------------------------------------------------------------- 1 | Django Simditor 2 | =============== 3 | 4 | 5 | Installation 6 | ------------ 7 | 8 | pip install django-Simditor 9 | 10 | 11 | #. Add ``simditor`` to your ``INSTALLED_APPS`` setting. 12 | -------------------------------------------------------------------------------- /resources/clipboardimage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/resources/clipboardimage.gif -------------------------------------------------------------------------------- /resources/dropimage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/resources/dropimage.gif -------------------------------------------------------------------------------- /resources/emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/resources/emoji.png -------------------------------------------------------------------------------- /resources/markdown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/resources/markdown.gif -------------------------------------------------------------------------------- /resources/without_admin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/resources/without_admin.gif -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """setup.py""" 2 | # -*- coding: utf-8 -*- 3 | #/usr/bin/env python 4 | 5 | from setuptools import setup, find_packages 6 | 7 | VERSION = '0.0.15' 8 | LONG_DESCRIPTION = open('intro.rst', 'r').read() 9 | 10 | INSTALL_REQUIRES = [ 11 | 'Django', 12 | 'pillow' 13 | ] 14 | 15 | setup( 16 | name='django-simditor', 17 | version=VERSION, 18 | description='Django admin Simditor integration.', 19 | long_description=LONG_DESCRIPTION, 20 | author='Silence', 21 | author_email='istommao@gmail.com', 22 | url='https://github.com/istommao/django-simditor', 23 | zip_safe=False, 24 | install_requires=INSTALL_REQUIRES, 25 | packages=find_packages(exclude=[".DS_Store"]), 26 | keywords='Django admin Simditor integration!', 27 | include_package_data=True, 28 | classifiers=[ 29 | "Programming Language :: Python", 30 | "Programming Language :: Python :: 2.7", 31 | "Programming Language :: Python :: 3.3", 32 | "Programming Language :: Python :: 3.4", 33 | "Programming Language :: Python :: 3.5", 34 | "License :: OSI Approved :: MIT License", 35 | "Operating System :: OS Independent", 36 | "Framework :: Django", 37 | "Intended Audience :: Developers", 38 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content", 39 | ] 40 | ) 41 | -------------------------------------------------------------------------------- /simditor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/simditor/__init__.py -------------------------------------------------------------------------------- /simditor/fields.py: -------------------------------------------------------------------------------- 1 | """simditor fields.""" 2 | from django import forms 3 | from django.db import models 4 | 5 | from .widgets import SimditorWidget 6 | 7 | 8 | class RichTextFormField(forms.fields.CharField): 9 | """RichTextFormField.""" 10 | 11 | def __init__(self, *args, **kwargs): 12 | kwargs.update( 13 | { 14 | 'widget': SimditorWidget() 15 | } 16 | ) 17 | super(RichTextFormField, self).__init__(*args, **kwargs) 18 | 19 | 20 | class RichTextField(models.TextField): 21 | """RichTextField.""" 22 | 23 | def __init__(self, *args, **kwargs): 24 | super(RichTextField, self).__init__(*args, **kwargs) 25 | 26 | def formfield(self, **kwargs): 27 | defaults = { 28 | 'form_class': self._get_form_class() 29 | } 30 | defaults.update(kwargs) 31 | return super(RichTextField, self).formfield(**defaults) 32 | 33 | @staticmethod 34 | def _get_form_class(): 35 | return RichTextFormField 36 | -------------------------------------------------------------------------------- /simditor/image/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/simditor/image/__init__.py -------------------------------------------------------------------------------- /simditor/image/dummy_backend.py: -------------------------------------------------------------------------------- 1 | """simditor image pillow_backend.""" 2 | from __future__ import absolute_import 3 | 4 | from simditor import utils 5 | 6 | 7 | def image_verify(file_object): 8 | """image_verify.""" 9 | if not utils.is_valid_image_extension(file_object.name): 10 | raise utils.NotAnImageException 11 | -------------------------------------------------------------------------------- /simditor/image/pillow_backend.py: -------------------------------------------------------------------------------- 1 | """simditor image pillow_backend.""" 2 | from __future__ import absolute_import 3 | 4 | import os 5 | from io import BytesIO 6 | 7 | from django.core.files.storage import default_storage 8 | from django.core.files.uploadedfile import InMemoryUploadedFile 9 | 10 | from simditor import utils 11 | 12 | try: 13 | from PIL import Image, ImageOps 14 | except ImportError: 15 | import Image 16 | import ImageOps 17 | 18 | 19 | THUMBNAIL_SIZE = (75, 75) 20 | 21 | 22 | def image_verify(f): 23 | try: 24 | Image.open(f).verify() 25 | except IOError: 26 | raise utils.NotAnImageException 27 | -------------------------------------------------------------------------------- /simditor/image_processing.py: -------------------------------------------------------------------------------- 1 | """simditor image_processing.""" 2 | from __future__ import absolute_import 3 | 4 | from django.conf import settings 5 | 6 | 7 | def get_backend(): 8 | """Get backend.""" 9 | backend = getattr(settings, 'SIMDITOR_IMAGE_BACKEND', None) 10 | 11 | if backend == 'pillow': 12 | from simditor.image import pillow_backend as backend 13 | else: 14 | from simditor.image import dummy_backend as backend 15 | return backend 16 | -------------------------------------------------------------------------------- /simditor/static/simditor/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/simditor/static/simditor/fonts/icomoon.eot -------------------------------------------------------------------------------- /simditor/static/simditor/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /simditor/static/simditor/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/simditor/static/simditor/fonts/icomoon.ttf -------------------------------------------------------------------------------- /simditor/static/simditor/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/simditor/static/simditor/fonts/icomoon.woff -------------------------------------------------------------------------------- /simditor/static/simditor/fonts/selection.json: -------------------------------------------------------------------------------- 1 | { 2 | "IcoMoonType": "selection", 3 | "icons": [ 4 | { 5 | "icon": { 6 | "paths": [ 7 | "M877.248 786.752l-146.752-146.752-90.496 90.496 146.752 146.752-146.752 146.752h384v-384zM384 0h-384v384l146.752-146.752 145.376 145.248 90.496-90.496-145.376-145.248zM384 730.496l-90.496-90.496-146.752 146.752-146.752-146.752v384h384l-146.752-146.752zM1024 0h-384l146.752 146.752-145.376 145.248 90.496 90.496 145.376-145.248 146.752 146.752z" 8 | ], 9 | "tags": [ 10 | "fullscreen", 11 | "expand" 12 | ], 13 | "grid": 16, 14 | "attrs": [] 15 | }, 16 | "attrs": [], 17 | "properties": { 18 | "id": 99, 19 | "order": 2, 20 | "prevSize": 16, 21 | "code": 58880, 22 | "name": "fullscreen" 23 | }, 24 | "setIdx": 1, 25 | "setId": 6, 26 | "iconIdx": 99 27 | } 28 | ], 29 | "height": 1024, 30 | "metadata": { 31 | "name": "icomoon" 32 | }, 33 | "preferences": { 34 | "fontPref": { 35 | "prefix": "icon-", 36 | "metadata": { 37 | "fontFamily": "icomoon", 38 | "majorVersion": 1, 39 | "minorVersion": 0 40 | }, 41 | "showGlyphs": true, 42 | "metrics": { 43 | "emSize": 512, 44 | "baseline": 6.25, 45 | "whitespace": 50 46 | }, 47 | "resetPoint": 58880, 48 | "showQuickUse": true, 49 | "quickUsageToken": false, 50 | "showMetrics": false, 51 | "showMetadata": false, 52 | "autoHost": true, 53 | "embed": false, 54 | "ie7": false, 55 | "showSelector": false, 56 | "showVersion": true 57 | }, 58 | "imagePref": { 59 | "color": 0, 60 | "height": 32, 61 | "columns": 16, 62 | "margin": 16, 63 | "png": false, 64 | "sprites": true, 65 | "prefix": "icon-" 66 | }, 67 | "historySize": 100, 68 | "showCodes": true, 69 | "gridSize": 16, 70 | "showLiga": false, 71 | "showGrid": true, 72 | "showGlyphs": true, 73 | "showQuickUse": true, 74 | "search": "", 75 | "quickUsageToken": { 76 | "UntitledProject1": "ZWEwOTk2NTRmNjMyOGQ1MzAwZWFiYmJlODViMWMzZDcjMiMxNDA3NzM0MTA2IyMj" 77 | }, 78 | "showQuickUse2": true, 79 | "showSVGs": true, 80 | "fontHostingName": false 81 | } 82 | } -------------------------------------------------------------------------------- /simditor/static/simditor/images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/simditor/static/simditor/images/image.png -------------------------------------------------------------------------------- /simditor/static/simditor/scripts/hotkeys.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('simple-hotkeys', ["jquery","simple-module"], function ($, SimpleModule) { 5 | return (root['hotkeys'] = factory($, SimpleModule)); 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")); 12 | } else { 13 | root.simple = root.simple || {}; 14 | root.simple['hotkeys'] = factory(jQuery,SimpleModule); 15 | } 16 | }(this, function ($, SimpleModule) { 17 | 18 | var Hotkeys, hotkeys, 19 | 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; }, 20 | hasProp = {}.hasOwnProperty; 21 | 22 | Hotkeys = (function(superClass) { 23 | extend(Hotkeys, superClass); 24 | 25 | function Hotkeys() { 26 | return Hotkeys.__super__.constructor.apply(this, arguments); 27 | } 28 | 29 | Hotkeys.count = 0; 30 | 31 | Hotkeys.keyNameMap = { 32 | 8: "Backspace", 33 | 9: "Tab", 34 | 13: "Enter", 35 | 16: "Shift", 36 | 17: "Control", 37 | 18: "Alt", 38 | 19: "Pause", 39 | 20: "CapsLock", 40 | 27: "Esc", 41 | 32: "Spacebar", 42 | 33: "PageUp", 43 | 34: "PageDown", 44 | 35: "End", 45 | 36: "Home", 46 | 37: "Left", 47 | 38: "Up", 48 | 39: "Right", 49 | 40: "Down", 50 | 45: "Insert", 51 | 46: "Del", 52 | 91: "Meta", 53 | 93: "Meta", 54 | 48: "0", 55 | 49: "1", 56 | 50: "2", 57 | 51: "3", 58 | 52: "4", 59 | 53: "5", 60 | 54: "6", 61 | 55: "7", 62 | 56: "8", 63 | 57: "9", 64 | 65: "A", 65 | 66: "B", 66 | 67: "C", 67 | 68: "D", 68 | 69: "E", 69 | 70: "F", 70 | 71: "G", 71 | 72: "H", 72 | 73: "I", 73 | 74: "J", 74 | 75: "K", 75 | 76: "L", 76 | 77: "M", 77 | 78: "N", 78 | 79: "O", 79 | 80: "P", 80 | 81: "Q", 81 | 82: "R", 82 | 83: "S", 83 | 84: "T", 84 | 85: "U", 85 | 86: "V", 86 | 87: "W", 87 | 88: "X", 88 | 89: "Y", 89 | 90: "Z", 90 | 96: "0", 91 | 97: "1", 92 | 98: "2", 93 | 99: "3", 94 | 100: "4", 95 | 101: "5", 96 | 102: "6", 97 | 103: "7", 98 | 104: "8", 99 | 105: "9", 100 | 106: "Multiply", 101 | 107: "Add", 102 | 109: "Subtract", 103 | 110: "Decimal", 104 | 111: "Divide", 105 | 112: "F1", 106 | 113: "F2", 107 | 114: "F3", 108 | 115: "F4", 109 | 116: "F5", 110 | 117: "F6", 111 | 118: "F7", 112 | 119: "F8", 113 | 120: "F9", 114 | 121: "F10", 115 | 122: "F11", 116 | 123: "F12", 117 | 124: "F13", 118 | 125: "F14", 119 | 126: "F15", 120 | 127: "F16", 121 | 128: "F17", 122 | 129: "F18", 123 | 130: "F19", 124 | 131: "F20", 125 | 132: "F21", 126 | 133: "F22", 127 | 134: "F23", 128 | 135: "F24", 129 | 59: ";", 130 | 61: "=", 131 | 186: ";", 132 | 187: "=", 133 | 188: ",", 134 | 190: ".", 135 | 191: "/", 136 | 192: "`", 137 | 219: "[", 138 | 220: "\\", 139 | 221: "]", 140 | 222: "'" 141 | }; 142 | 143 | Hotkeys.aliases = { 144 | "escape": "esc", 145 | "delete": "del", 146 | "return": "enter", 147 | "ctrl": "control", 148 | "space": "spacebar", 149 | "ins": "insert", 150 | "cmd": "meta", 151 | "command": "meta", 152 | "wins": "meta", 153 | "windows": "meta" 154 | }; 155 | 156 | Hotkeys.normalize = function(shortcut) { 157 | var i, j, key, keyname, keys, len; 158 | keys = shortcut.toLowerCase().replace(/\s+/gi, "").split("+"); 159 | for (i = j = 0, len = keys.length; j < len; i = ++j) { 160 | key = keys[i]; 161 | keys[i] = this.aliases[key] || key; 162 | } 163 | keyname = keys.pop(); 164 | keys.sort().push(keyname); 165 | return keys.join("_"); 166 | }; 167 | 168 | Hotkeys.prototype.opts = { 169 | el: document 170 | }; 171 | 172 | Hotkeys.prototype._init = function() { 173 | this.id = ++this.constructor.count; 174 | this._map = {}; 175 | this._delegate = typeof this.opts.el === "string" ? document : this.opts.el; 176 | return $(this._delegate).on("keydown.simple-hotkeys-" + this.id, this.opts.el, (function(_this) { 177 | return function(e) { 178 | var ref; 179 | return (ref = _this._getHander(e)) != null ? ref.call(_this, e) : void 0; 180 | }; 181 | })(this)); 182 | }; 183 | 184 | Hotkeys.prototype._getHander = function(e) { 185 | var keyname, shortcut; 186 | if (!(keyname = this.constructor.keyNameMap[e.which])) { 187 | return; 188 | } 189 | shortcut = ""; 190 | if (e.altKey) { 191 | shortcut += "alt_"; 192 | } 193 | if (e.ctrlKey) { 194 | shortcut += "control_"; 195 | } 196 | if (e.metaKey) { 197 | shortcut += "meta_"; 198 | } 199 | if (e.shiftKey) { 200 | shortcut += "shift_"; 201 | } 202 | shortcut += keyname.toLowerCase(); 203 | return this._map[shortcut]; 204 | }; 205 | 206 | Hotkeys.prototype.respondTo = function(subject) { 207 | if (typeof subject === 'string') { 208 | return this._map[this.constructor.normalize(subject)] != null; 209 | } else { 210 | return this._getHander(subject) != null; 211 | } 212 | }; 213 | 214 | Hotkeys.prototype.add = function(shortcut, handler) { 215 | this._map[this.constructor.normalize(shortcut)] = handler; 216 | return this; 217 | }; 218 | 219 | Hotkeys.prototype.remove = function(shortcut) { 220 | delete this._map[this.constructor.normalize(shortcut)]; 221 | return this; 222 | }; 223 | 224 | Hotkeys.prototype.destroy = function() { 225 | $(this._delegate).off(".simple-hotkeys-" + this.id); 226 | this._map = {}; 227 | return this; 228 | }; 229 | 230 | return Hotkeys; 231 | 232 | })(SimpleModule); 233 | 234 | hotkeys = function(opts) { 235 | return new Hotkeys(opts); 236 | }; 237 | 238 | return hotkeys; 239 | 240 | })); 241 | 242 | -------------------------------------------------------------------------------- /simditor/static/simditor/scripts/hotkeys.min.js: -------------------------------------------------------------------------------- 1 | !function(a,b){"function"==typeof define&&define.amd? 2 | // AMD. Register as an anonymous module unless amdModuleId is set 3 | define("simple-hotkeys",["jquery","simple-module"],function(c,d){return a.hotkeys=b(c,d)}):"object"==typeof exports? 4 | // Node. Does not work with strict CommonJS, but 5 | // only CommonJS-like environments that support module.exports, 6 | // like Node. 7 | module.exports=b(require("jquery"),require("simple-module")):(a.simple=a.simple||{},a.simple.hotkeys=b(jQuery,SimpleModule))}(this,function(a,b){var c,d,e=function(a,b){function c(){this.constructor=a}for(var d in b)f.call(b,d)&&(a[d]=b[d]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},f={}.hasOwnProperty;return c=function(b){function c(){return c.__super__.constructor.apply(this,arguments)}return e(c,b),c.count=0,c.keyNameMap={8:"Backspace",9:"Tab",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Esc",32:"Spacebar",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",45:"Insert",46:"Del",91:"Meta",93:"Meta",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",65:"A",66:"B",67:"C",68:"D",69:"E",70:"F",71:"G",72:"H",73:"I",74:"J",75:"K",76:"L",77:"M",78:"N",79:"O",80:"P",81:"Q",82:"R",83:"S",84:"T",85:"U",86:"V",87:"W",88:"X",89:"Y",90:"Z",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"Multiply",107:"Add",109:"Subtract",110:"Decimal",111:"Divide",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",124:"F13",125:"F14",126:"F15",127:"F16",128:"F17",129:"F18",130:"F19",131:"F20",132:"F21",133:"F22",134:"F23",135:"F24",59:";",61:"=",186:";",187:"=",188:",",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},c.aliases={escape:"esc","delete":"del","return":"enter",ctrl:"control",space:"spacebar",ins:"insert",cmd:"meta",command:"meta",wins:"meta",windows:"meta"},c.normalize=function(a){var b,c,d,e,f,g;for(f=a.toLowerCase().replace(/\s+/gi,"").split("+"),b=c=0,g=f.length;g>c;b=++c)d=f[b],f[b]=this.aliases[d]||d;return e=f.pop(),f.sort().push(e),f.join("_")},c.prototype.opts={el:document},c.prototype._init=function(){return this.id=++this.constructor.count,this._map={},this._delegate="string"==typeof this.opts.el?document:this.opts.el,a(this._delegate).on("keydown.simple-hotkeys-"+this.id,this.opts.el,function(a){return function(b){var c;return null!=(c=a._getHander(b))?c.call(a,b):void 0}}(this))},c.prototype._getHander=function(a){var b,c;if(b=this.constructor.keyNameMap[a.which])return c="",a.altKey&&(c+="alt_"),a.ctrlKey&&(c+="control_"),a.metaKey&&(c+="meta_"),a.shiftKey&&(c+="shift_"),c+=b.toLowerCase(),this._map[c]},c.prototype.respondTo=function(a){return"string"==typeof a?null!=this._map[this.constructor.normalize(a)]:null!=this._getHander(a)},c.prototype.add=function(a,b){return this._map[this.constructor.normalize(a)]=b,this},c.prototype.remove=function(a){return delete this._map[this.constructor.normalize(a)],this},c.prototype.destroy=function(){return a(this._delegate).off(".simple-hotkeys-"+this.id),this._map={},this},c}(b),d=function(a){return new c(a)}}); -------------------------------------------------------------------------------- /simditor/static/simditor/scripts/marked.min.js: -------------------------------------------------------------------------------- 1 | (function(){function e(e){this.tokens=[],this.tokens.links={},this.options=e||a.defaults,this.rules=p.normal,this.options.gfm&&(this.options.tables?this.rules=p.tables:this.rules=p.gfm)}function t(e,t){if(this.options=t||a.defaults,this.links=e,this.rules=u.normal,this.renderer=this.options.renderer||new n,this.renderer.options=this.options,!this.links)throw new Error("Tokens array requires a `links` property.");this.options.gfm?this.options.breaks?this.rules=u.breaks:this.rules=u.gfm:this.options.pedantic&&(this.rules=u.pedantic)}function n(e){this.options=e||{}}function r(e){this.tokens=[],this.token=null,this.options=e||a.defaults,this.options.renderer=this.options.renderer||new n,this.renderer=this.options.renderer,this.renderer.options=this.options}function s(e,t){return e.replace(t?/&/g:/&(?!#?\w+;)/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function i(e){return e.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g,function(e,t){return t=t.toLowerCase(),"colon"===t?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""})}function l(e,t){return e=e.source,t=t||"",function n(r,s){return r?(s=s.source||s,s=s.replace(/(^|[^\[])\^/g,"$1"),e=e.replace(r,s),n):new RegExp(e,t)}}function o(){}function h(e){for(var t,n,r=1;rAn error occured:

"+s(c.message+"",!0)+"
";throw c}}var p={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:o,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:o,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:o,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};p.bullet=/(?:[*+-]|\d+\.)/,p.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/,p.item=l(p.item,"gm")(/bull/g,p.bullet)(),p.list=l(p.list)(/bull/g,p.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+p.def.source+")")(),p.blockquote=l(p.blockquote)("def",p.def)(),p._tag="(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b",p.html=l(p.html)("comment",//)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/])*?>/)(/tag/g,p._tag)(),p.paragraph=l(p.paragraph)("hr",p.hr)("heading",p.heading)("lheading",p.lheading)("blockquote",p.blockquote)("tag","<"+p._tag)("def",p.def)(),p.normal=h({},p),p.gfm=h({},p.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/}),p.gfm.paragraph=l(p.paragraph)("(?!","(?!"+p.gfm.fences.source.replace("\\1","\\2")+"|"+p.list.source.replace("\\1","\\3")+"|")(),p.tables=h({},p.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/}),e.rules=p,e.lex=function(t,n){var r=new e(n);return r.lex(t)},e.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},e.prototype.token=function(e,t,n){for(var r,s,i,l,o,h,a,u,c,e=e.replace(/^ +$/gm,"");e;)if((i=this.rules.newline.exec(e))&&(e=e.substring(i[0].length),i[0].length>1&&this.tokens.push({type:"space"})),i=this.rules.code.exec(e))e=e.substring(i[0].length),i=i[0].replace(/^ {4}/gm,""),this.tokens.push({type:"code",text:this.options.pedantic?i:i.replace(/\n+$/,"")});else if(i=this.rules.fences.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"code",lang:i[2],text:i[3]||""});else if(i=this.rules.heading.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"heading",depth:i[1].length,text:i[2]});else if(t&&(i=this.rules.nptable.exec(e))){for(e=e.substring(i[0].length),h={type:"table",header:i[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:i[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:i[3].replace(/\n$/,"").split("\n")},u=0;u ?/gm,""),this.token(i,t,!0),this.tokens.push({type:"blockquote_end"});else if(i=this.rules.list.exec(e)){for(e=e.substring(i[0].length),l=i[2],this.tokens.push({type:"list_start",ordered:l.length>1}),i=i[0].match(this.rules.item),r=!1,c=i.length,u=0;c>u;u++)h=i[u],a=h.length,h=h.replace(/^ *([*+-]|\d+\.) +/,""),~h.indexOf("\n ")&&(a-=h.length,h=this.options.pedantic?h.replace(/^ {1,4}/gm,""):h.replace(new RegExp("^ {1,"+a+"}","gm"),"")),this.options.smartLists&&u!==c-1&&(o=p.bullet.exec(i[u+1])[0],l===o||l.length>1&&o.length>1||(e=i.slice(u+1).join("\n")+e,u=c-1)),s=r||/\n\n(?!\s*$)/.test(h),u!==c-1&&(r="\n"===h.charAt(h.length-1),s||(s=r)),this.tokens.push({type:s?"loose_item_start":"list_item_start"}),this.token(h,!1,n),this.tokens.push({type:"list_item_end"});this.tokens.push({type:"list_end"})}else if(i=this.rules.html.exec(e))e=e.substring(i[0].length),this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&("pre"===i[1]||"script"===i[1]||"style"===i[1]),text:i[0]});else if(!n&&t&&(i=this.rules.def.exec(e)))e=e.substring(i[0].length),this.tokens.links[i[1].toLowerCase()]={href:i[2],title:i[3]};else if(t&&(i=this.rules.table.exec(e))){for(e=e.substring(i[0].length),h={type:"table",header:i[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:i[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:i[3].replace(/(?: *\| *)?\n$/,"").split("\n")},u=0;u])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:o,tag:/^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:o,text:/^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/,u.link=l(u.link)("inside",u._inside)("href",u._href)(),u.reflink=l(u.reflink)("inside",u._inside)(),u.normal=h({},u),u.pedantic=h({},u.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/}),u.gfm=h({},u.normal,{escape:l(u.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:l(u.text)("]|","~]|")("|","|https?://|")()}),u.breaks=h({},u.gfm,{br:l(u.br)("{2,}","*")(),text:l(u.gfm.text)("{2,}","*")()}),t.rules=u,t.output=function(e,n,r){var s=new t(n,r);return s.output(e)},t.prototype.output=function(e){for(var t,n,r,i,l="";e;)if(i=this.rules.escape.exec(e))e=e.substring(i[0].length),l+=i[1];else if(i=this.rules.autolink.exec(e))e=e.substring(i[0].length),"@"===i[2]?(n=":"===i[1].charAt(6)?this.mangle(i[1].substring(7)):this.mangle(i[1]),r=this.mangle("mailto:")+n):(n=s(i[1]),r=n),l+=this.renderer.link(r,null,n);else if(this.inLink||!(i=this.rules.url.exec(e))){if(i=this.rules.tag.exec(e))!this.inLink&&/^/i.test(i[0])&&(this.inLink=!1),e=e.substring(i[0].length),l+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(i[0]):s(i[0]):i[0];else if(i=this.rules.link.exec(e))e=e.substring(i[0].length),this.inLink=!0,l+=this.outputLink(i,{href:i[2],title:i[3]}),this.inLink=!1;else if((i=this.rules.reflink.exec(e))||(i=this.rules.nolink.exec(e))){if(e=e.substring(i[0].length),t=(i[2]||i[1]).replace(/\s+/g," "),t=this.links[t.toLowerCase()],!t||!t.href){l+=i[0].charAt(0),e=i[0].substring(1)+e;continue}this.inLink=!0,l+=this.outputLink(i,t),this.inLink=!1}else if(i=this.rules.strong.exec(e))e=e.substring(i[0].length),l+=this.renderer.strong(this.output(i[2]||i[1]));else if(i=this.rules.em.exec(e))e=e.substring(i[0].length),l+=this.renderer.em(this.output(i[2]||i[1]));else if(i=this.rules.code.exec(e))e=e.substring(i[0].length),l+=this.renderer.codespan(s(i[2],!0));else if(i=this.rules.br.exec(e))e=e.substring(i[0].length),l+=this.renderer.br();else if(i=this.rules.del.exec(e))e=e.substring(i[0].length),l+=this.renderer.del(this.output(i[1]));else if(i=this.rules.text.exec(e))e=e.substring(i[0].length),l+=this.renderer.text(s(this.smartypants(i[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else e=e.substring(i[0].length),n=s(i[1]),r=n,l+=this.renderer.link(r,null,n);return l},t.prototype.outputLink=function(e,t){var n=s(t.href),r=t.title?s(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,s(e[1]))},t.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014\/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014\/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},t.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,s=0;r>s;s++)t=e.charCodeAt(s),Math.random()>.5&&(t="x"+t.toString(16)),n+="&#"+t+";";return n},n.prototype.code=function(e,t,n){if(this.options.highlight){var r=this.options.highlight(e,t);null!=r&&r!==e&&(n=!0,e=r)}return t?'
'+(n?e:s(e,!0))+"\n
\n":"
"+(n?e:s(e,!0))+"\n
"},n.prototype.blockquote=function(e){return"
\n"+e+"
\n"},n.prototype.html=function(e){return e},n.prototype.heading=function(e,t,n){return"'+e+"\n"},n.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"},n.prototype.list=function(e,t){var n=t?"ol":"ul";return"<"+n+">\n"+e+"\n"},n.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},n.prototype.paragraph=function(e){return"

    "+e+"

    \n"},n.prototype.table=function(e,t){return"\n\n"+e+"\n\n"+t+"\n
    \n"},n.prototype.tablerow=function(e){return"\n"+e+"\n"},n.prototype.tablecell=function(e,t){var n=t.header?"th":"td",r=t.align?"<"+n+' style="text-align:'+t.align+'">':"<"+n+">";return r+e+"\n"},n.prototype.strong=function(e){return""+e+""},n.prototype.em=function(e){return""+e+""},n.prototype.codespan=function(e){return""+e+""},n.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},n.prototype.del=function(e){return""+e+""},n.prototype.link=function(e,t,n){if(this.options.sanitize){try{var r=decodeURIComponent(i(e)).replace(/[^\w:]/g,"").toLowerCase()}catch(s){return""}if(0===r.indexOf("javascript:")||0===r.indexOf("vbscript:"))return""}var l='
    "},n.prototype.image=function(e,t,n){var r=''+n+'":">"},n.prototype.text=function(e){return e},r.parse=function(e,t,n){var s=new r(t,n);return s.parse(e)},r.prototype.parse=function(e){this.inline=new t(e.links,this.options,this.renderer),this.tokens=e.reverse();for(var n="";this.next();)n+=this.tok();return n},r.prototype.next=function(){return this.token=this.tokens.pop()},r.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},r.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},r.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,s,i="",l="";for(n="",e=0;e 0)) { 148 | return result; 149 | } 150 | result = result.replace(/([^%]|^)%(?:(\d+)\$)?s/g, function(p0, p, position) { 151 | if (position) { 152 | return p + args[parseInt(position) - 1]; 153 | } else { 154 | return p + args.shift(); 155 | } 156 | }); 157 | return result.replace(/%%s/g, '%s'); 158 | }; 159 | 160 | Module.i18n = { 161 | 'zh-CN': {} 162 | }; 163 | 164 | Module.locale = 'zh-CN'; 165 | 166 | return Module; 167 | 168 | })(); 169 | 170 | return Module; 171 | 172 | })); 173 | -------------------------------------------------------------------------------- /simditor/static/simditor/scripts/module.min.js: -------------------------------------------------------------------------------- 1 | !function(a,b){"function"==typeof define&&define.amd? 2 | // AMD. Register as an anonymous module unless amdModuleId is set 3 | define("simple-module",["jquery"],function(c){return a.Module=b(c)}):"object"==typeof exports?module.exports=b(require("jquery")):a.SimpleModule=b(jQuery)}(this,function(a){var b,c=[].slice;return b=function(){function b(b){var c,d,e,f,g,h,i;if(this.opts=a.extend({},this.opts,b),(c=this.constructor)._connectedClasses||(c._connectedClasses=[]),g=function(){var a,b,c,e;for(c=this.constructor._connectedClasses,e=[],a=0,b=c.length;b>a;a++)d=c[a],i=d.pluginName.charAt(0).toLowerCase()+d.pluginName.slice(1),d.prototype._connected&&(d.prototype._module=this),e.push(this[i]=new d);return e}.call(this),this._connected)this.opts=a.extend({},this.opts,this._module.opts);else for(this._init(),e=0,h=g.length;h>e;e++)f=g[e],"function"==typeof f._init&&f._init();this.trigger("initialized")}return b.extend=function(a){var b,c,d;if(null!=a&&"object"==typeof a){for(b in a)d=a[b],"included"!==b&&"extended"!==b&&(this[b]=d);return null!=(c=a.extended)?c.call(this):void 0}},b.include=function(a){var b,c,d;if(null!=a&&"object"==typeof a){for(b in a)d=a[b],"included"!==b&&"extended"!==b&&(this.prototype[b]=d);return null!=(c=a.included)?c.call(this):void 0}},b.connect=function(a){if("function"==typeof a){if(!a.pluginName)throw new Error("Module.connect: cannot connect plugin without pluginName");return a.prototype._connected=!0,this._connectedClasses||(this._connectedClasses=[]),this._connectedClasses.push(a),a.pluginName?this[a.pluginName]=a:void 0}},b.prototype.opts={},b.prototype._init=function(){},b.prototype.on=function(){var b,d;return b=1<=arguments.length?c.call(arguments,0):[],(d=a(this)).on.apply(d,b),this},b.prototype.one=function(){var b,d;return b=1<=arguments.length?c.call(arguments,0):[],(d=a(this)).one.apply(d,b),this},b.prototype.off=function(){var b,d;return b=1<=arguments.length?c.call(arguments,0):[],(d=a(this)).off.apply(d,b),this},b.prototype.trigger=function(){var b,d;return b=1<=arguments.length?c.call(arguments,0):[],(d=a(this)).trigger.apply(d,b),this},b.prototype.triggerHandler=function(){var b,d;return b=1<=arguments.length?c.call(arguments,0):[],(d=a(this)).triggerHandler.apply(d,b)},b.prototype._t=function(){var a,b;return a=1<=arguments.length?c.call(arguments,0):[],(b=this.constructor)._t.apply(b,a)},b._t=function(){var a,b,d,e;return b=arguments[0],a=2<=arguments.length?c.call(arguments,1):[],e=(null!=(d=this.i18n[this.locale])?d[b]:void 0)||"",a.length>0?(e=e.replace(/([^%]|^)%(?:(\d+)\$)?s/g,function(b,c,d){return d?c+a[parseInt(d)-1]:c+a.shift()}),e.replace(/%%s/g,"%s")):e},b.i18n={"zh-CN":{}},b.locale="zh-CN",b}()}); -------------------------------------------------------------------------------- /simditor/static/simditor/scripts/simditor-checklist.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-checklist', ["jquery","simditor"], function (a0,b1) { 5 | return (root['ChecklistButton'] = factory(a0,b1)); 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("simditor")); 12 | } else { 13 | root['ChecklistButton'] = factory(jQuery,Simditor); 14 | } 15 | }(this, function ($, Simditor) { 16 | 17 | var ChecklistButton, 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 | slice = [].slice; 21 | 22 | ChecklistButton = (function(superClass) { 23 | extend(ChecklistButton, superClass); 24 | 25 | ChecklistButton.prototype.type = 'ul.simditor-checklist'; 26 | 27 | ChecklistButton.prototype.name = 'checklist'; 28 | 29 | ChecklistButton.prototype.icon = 'checklist'; 30 | 31 | ChecklistButton.prototype.htmlTag = 'li'; 32 | 33 | ChecklistButton.prototype.disableTag = 'pre, table'; 34 | 35 | function ChecklistButton() { 36 | var args; 37 | args = 1 <= arguments.length ? slice.call(arguments, 0) : []; 38 | ChecklistButton.__super__.constructor.apply(this, args); 39 | if ('input' && $.inArray('input', this.editor.formatter._allowedTags) < 0) { 40 | this.editor.formatter._allowedTags.push('input'); 41 | } 42 | $.extend(this.editor.formatter._allowedAttributes, { 43 | input: ['type', 'checked'] 44 | }); 45 | } 46 | 47 | ChecklistButton.prototype._init = function() { 48 | ChecklistButton.__super__._init.call(this); 49 | this.editor.on('decorate', (function(_this) { 50 | return function(e, $el) { 51 | return $el.find('ul > li input[type=checkbox]').each(function(i, checkbox) { 52 | return _this._decorate($(checkbox)); 53 | }); 54 | }; 55 | })(this)); 56 | this.editor.on('undecorate', (function(_this) { 57 | return function(e, $el) { 58 | return $el.find('.simditor-checklist > li').each(function(i, node) { 59 | return _this._undecorate($(node)); 60 | }); 61 | }; 62 | })(this)); 63 | this.editor.body.on('click', '.simditor-checklist > li', (function(_this) { 64 | return function(e) { 65 | var $node, range; 66 | e.preventDefault(); 67 | e.stopPropagation(); 68 | $node = $(e.currentTarget); 69 | range = document.createRange(); 70 | _this.editor.selection.save(); 71 | range.setStart($node[0], 0); 72 | range.setEnd($node[0], _this.editor.util.getNodeLength($node[0])); 73 | _this.editor.selection.range(range); 74 | document.execCommand('strikethrough'); 75 | $node.attr('checked', !$node.attr('checked')); 76 | _this.editor.selection.restore(); 77 | return _this.editor.trigger('valuechanged'); 78 | }; 79 | })(this)); 80 | return this.editor.keystroke.add('13', 'li', (function(_this) { 81 | return function(e, $node) { 82 | return setTimeout(function() { 83 | var $li; 84 | $li = _this.editor.selection.blockNodes().last().next(); 85 | if ($li.length) { 86 | $li[0].removeAttribute('checked'); 87 | if (document.queryCommandState('strikethrough')) { 88 | return document.execCommand('strikethrough'); 89 | } 90 | } 91 | }, 0); 92 | }; 93 | })(this)); 94 | }; 95 | 96 | ChecklistButton.prototype._status = function() { 97 | var $node; 98 | ChecklistButton.__super__._status.call(this); 99 | $node = this.editor.selection.rootNodes(); 100 | if ($node.is('.simditor-checklist')) { 101 | this.editor.toolbar.findButton('ul').setActive(false); 102 | this.editor.toolbar.findButton('ol').setActive(false); 103 | this.editor.toolbar.findButton('ul').setDisabled(true); 104 | return this.editor.toolbar.findButton('ol').setDisabled(true); 105 | } else { 106 | return this.editor.toolbar.findButton('checklist').setActive(false); 107 | } 108 | }; 109 | 110 | ChecklistButton.prototype.command = function(param) { 111 | var $list, $rootNodes; 112 | $rootNodes = this.editor.selection.blockNodes(); 113 | this.editor.selection.save(); 114 | $list = null; 115 | $rootNodes.each((function(_this) { 116 | return function(i, node) { 117 | var $node; 118 | $node = $(node); 119 | if ($node.is('blockquote, li') || $node.is(_this.disableTag) || !$.contains(document, node)) { 120 | return; 121 | } 122 | if ($node.is('.simditor-checklist')) { 123 | $node.children('li').each(function(i, li) { 124 | var $childList, $li; 125 | $li = $(li); 126 | $childList = $li.children('ul, ol').insertAfter($node); 127 | return $('

    ').append($(li).html() || _this.editor.util.phBr).insertBefore($node); 128 | }); 129 | return $node.remove(); 130 | } else if ($node.is('ul, ol')) { 131 | return $('

      ').append($node.contents()).replaceAll($node); 132 | } else if ($list && $node.prev().is($list)) { 133 | $('
    • ').append($node.html() || _this.editor.util.phBr).appendTo($list); 134 | return $node.remove(); 135 | } else { 136 | $list = $('
      '); 137 | $list.find('li').append($node.html() || _this.editor.util.phBr); 138 | return $list.replaceAll($node); 139 | } 140 | }; 141 | })(this)); 142 | this.editor.selection.restore(); 143 | return this.editor.trigger('valuechanged'); 144 | }; 145 | 146 | ChecklistButton.prototype._decorate = function($checkbox) { 147 | var $node, checked; 148 | checked = !!$checkbox.attr('checked'); 149 | $node = $checkbox.closest('li'); 150 | $checkbox.remove(); 151 | $node.attr('checked', checked); 152 | return $node.closest('ul').addClass('simditor-checklist'); 153 | }; 154 | 155 | ChecklistButton.prototype._undecorate = function($node) { 156 | var $checkbox, checked; 157 | checked = !!$node.attr('checked'); 158 | $checkbox = $('').attr('checked', checked); 159 | return $node.attr('checked', '').prepend($checkbox); 160 | }; 161 | 162 | return ChecklistButton; 163 | 164 | })(Simditor.Button); 165 | 166 | Simditor.Toolbar.addButton(ChecklistButton); 167 | 168 | return ChecklistButton; 169 | 170 | })); -------------------------------------------------------------------------------- /simditor/static/simditor/scripts/simditor-checklist.min.js: -------------------------------------------------------------------------------- 1 | (function(root,factory){if(typeof define==="function"&&define.amd){define("simditor-checklist",["jquery","simditor"],function(a0,b1){return(root["ChecklistButton"]=factory(a0,b1))})}else{if(typeof exports==="object"){module.exports=factory(require("jquery"),require("simditor"))}else{root["ChecklistButton"]=factory(jQuery,Simditor)}}}(this,function($,Simditor){var ChecklistButton,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},hasProp={}.hasOwnProperty,slice=[].slice;ChecklistButton=(function(superClass){extend(ChecklistButton,superClass);ChecklistButton.prototype.type="ul.simditor-checklist";ChecklistButton.prototype.name="checklist";ChecklistButton.prototype.icon="checklist";ChecklistButton.prototype.htmlTag="li";ChecklistButton.prototype.disableTag="pre, table";function ChecklistButton(){var args;args=1<=arguments.length?slice.call(arguments,0):[];ChecklistButton.__super__.constructor.apply(this,args);if("input"&&$.inArray("input",this.editor.formatter._allowedTags)<0){this.editor.formatter._allowedTags.push("input")}$.extend(this.editor.formatter._allowedAttributes,{input:["type","checked"]})}ChecklistButton.prototype._init=function(){ChecklistButton.__super__._init.call(this);this.editor.on("decorate",(function(_this){return function(e,$el){return $el.find("ul > li input[type=checkbox]").each(function(i,checkbox){return _this._decorate($(checkbox))})}})(this));this.editor.on("undecorate",(function(_this){return function(e,$el){return $el.find(".simditor-checklist > li").each(function(i,node){return _this._undecorate($(node))})}})(this));this.editor.body.on("click",".simditor-checklist > li",(function(_this){return function(e){var $node,range;e.preventDefault();e.stopPropagation();$node=$(e.currentTarget);range=document.createRange();_this.editor.selection.save();range.setStart($node[0],0);range.setEnd($node[0],_this.editor.util.getNodeLength($node[0]));_this.editor.selection.range(range);document.execCommand("strikethrough");$node.attr("checked",!$node.attr("checked"));_this.editor.selection.restore();return _this.editor.trigger("valuechanged")}})(this));return this.editor.keystroke.add("13","li",(function(_this){return function(e,$node){return setTimeout(function(){var $li;$li=_this.editor.selection.blockNodes().last().next();if($li.length){$li[0].removeAttribute("checked");if(document.queryCommandState("strikethrough")){return document.execCommand("strikethrough")}}},0)}})(this))};ChecklistButton.prototype._status=function(){var $node;ChecklistButton.__super__._status.call(this);$node=this.editor.selection.rootNodes();if($node.is(".simditor-checklist")){this.editor.toolbar.findButton("ul").setActive(false);this.editor.toolbar.findButton("ol").setActive(false);this.editor.toolbar.findButton("ul").setDisabled(true);return this.editor.toolbar.findButton("ol").setDisabled(true)}else{return this.editor.toolbar.findButton("checklist").setActive(false)}};ChecklistButton.prototype.command=function(param){var $list,$rootNodes;$rootNodes=this.editor.selection.blockNodes();this.editor.selection.save();$list=null;$rootNodes.each((function(_this){return function(i,node){var $node;$node=$(node);if($node.is("blockquote, li")||$node.is(_this.disableTag)||!$.contains(document,node)){return}if($node.is(".simditor-checklist")){$node.children("li").each(function(i,li){var $childList,$li;$li=$(li);$childList=$li.children("ul, ol").insertAfter($node);return $("

      ").append($(li).html()||_this.editor.util.phBr).insertBefore($node)});return $node.remove()}else{if($node.is("ul, ol")){return $('

        ').append($node.contents()).replaceAll($node)}else{if($list&&$node.prev().is($list)){$("
      • ").append($node.html()||_this.editor.util.phBr).appendTo($list);return $node.remove()}else{$list=$('
        ');$list.find("li").append($node.html()||_this.editor.util.phBr);return $list.replaceAll($node)}}}}})(this));this.editor.selection.restore();return this.editor.trigger("valuechanged")};ChecklistButton.prototype._decorate=function($checkbox){var $node,checked;checked=!!$checkbox.attr("checked");$node=$checkbox.closest("li");$checkbox.remove();$node.attr("checked",checked);return $node.closest("ul").addClass("simditor-checklist")};ChecklistButton.prototype._undecorate=function($node){var $checkbox,checked;checked=!!$node.attr("checked");$checkbox=$('').attr("checked",checked);return $node.attr("checked","").prepend($checkbox)};return ChecklistButton})(Simditor.Button);Simditor.Toolbar.addButton(ChecklistButton);return ChecklistButton})); -------------------------------------------------------------------------------- /simditor/static/simditor/scripts/simditor-dropzone.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | // AMD. Register as an anonymous module. 4 | define(["jquery", 5 | "simple-module", 6 | "simditor"], function ($, SimpleModule, Simditor) { 7 | return (root.returnExportsGlobal = factory($, SimpleModule, Simditor)); 8 | }); 9 | } else if (typeof exports === 'object') { 10 | // Node. Does not work with strict CommonJS, but 11 | // only CommonJS-like enviroments that support module.exports, 12 | // like Node. 13 | module.exports = factory(require("jquery"), 14 | require("simple-module"), 15 | require("simditor")); 16 | } else { 17 | root['Simditor'] = factory(jQuery, 18 | SimpleModule, 19 | Simditor); 20 | } 21 | }(this, function ($, SimpleModule, Simditor) { 22 | 23 | var Dropzone, 24 | __hasProp = {}.hasOwnProperty, 25 | __extends = 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; }; 26 | 27 | Dropzone = (function(_super) { 28 | __extends(Dropzone, _super); 29 | 30 | function Dropzone() { 31 | return Dropzone.__super__.constructor.apply(this, arguments); 32 | } 33 | 34 | Dropzone.pluginName = "Dropzone"; 35 | 36 | Dropzone.prototype._entered = 0; 37 | 38 | Dropzone.prototype._init = function() { 39 | this.editor = this._module; 40 | if (this.editor.uploader == null) { 41 | throw new Error("Can't work without 'simple-uploader' module"); 42 | return; 43 | } 44 | $(document.body).on("dragover", function(e) { 45 | e.originalEvent.dataTransfer.dropEffect = "none"; 46 | return e.preventDefault(); 47 | }); 48 | $(document.body).on('drop', function(e) { 49 | return e.preventDefault(); 50 | }); 51 | this.imageBtn = this.editor.toolbar.findButton("image"); 52 | return this.editor.body.on("dragover", function(e) { 53 | e.originalEvent.dataTransfer.dropEffect = "copy"; 54 | e.stopPropagation(); 55 | return e.preventDefault(); 56 | }).on("dragenter", (function(_this) { 57 | return function(e) { 58 | if ((_this._entered += 1) === 1) { 59 | _this.show(); 60 | } 61 | e.preventDefault(); 62 | return e.stopPropagation(); 63 | }; 64 | })(this)).on("dragleave", (function(_this) { 65 | return function(e) { 66 | if ((_this._entered -= 1) <= 0) { 67 | _this.hide(); 68 | } 69 | e.preventDefault(); 70 | return e.stopPropagation(); 71 | }; 72 | })(this)).on("drop", (function(_this) { 73 | return function(e) { 74 | var file, imageFiles, _i, _j, _len, _len1, _ref; 75 | imageFiles = []; 76 | _ref = e.originalEvent.dataTransfer.files; 77 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 78 | file = _ref[_i]; 79 | if (!_this.validFile(file)) { 80 | alert("「" + file.name + "]」文件不是图片。"); 81 | _this.hide(); 82 | return false; 83 | } 84 | imageFiles.push(file); 85 | } 86 | for (_j = 0, _len1 = imageFiles.length; _j < _len1; _j++) { 87 | file = imageFiles[_j]; 88 | _this.editor.uploader.upload(file, { 89 | inline: true 90 | }); 91 | } 92 | _this.hide(); 93 | e.stopPropagation(); 94 | return e.preventDefault(); 95 | }; 96 | })(this)); 97 | }; 98 | 99 | Dropzone.prototype.show = function() { 100 | return this.imageBtn.setActive(true); 101 | }; 102 | 103 | Dropzone.prototype.hide = function() { 104 | this.imageBtn.setActive(false); 105 | return this._entered = 0; 106 | }; 107 | 108 | Dropzone.prototype.validFile = function(file) { 109 | return file.type.indexOf("image/") > -1; 110 | }; 111 | 112 | return Dropzone; 113 | 114 | })(SimpleModule); 115 | 116 | Simditor.connect(Dropzone); 117 | 118 | 119 | return Simditor; 120 | 121 | 122 | })); -------------------------------------------------------------------------------- /simditor/static/simditor/scripts/simditor-dropzone.min.js: -------------------------------------------------------------------------------- 1 | (function(root,factory){if(typeof define==="function"&&define.amd){define(["jquery","simple-module","simditor"],function($,SimpleModule,Simditor){return(root.returnExportsGlobal=factory($,SimpleModule,Simditor))})}else{if(typeof exports==="object"){module.exports=factory(require("jquery"),require("simple-module"),require("simditor"))}else{root["Simditor"]=factory(jQuery,SimpleModule,Simditor)}}}(this,function($,SimpleModule,Simditor){var Dropzone,__hasProp={}.hasOwnProperty,__extends=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};Dropzone=(function(_super){__extends(Dropzone,_super);function Dropzone(){return Dropzone.__super__.constructor.apply(this,arguments)}Dropzone.pluginName="Dropzone";Dropzone.prototype._entered=0;Dropzone.prototype._init=function(){this.editor=this._module;if(this.editor.uploader==null){throw new Error("Can't work without 'simple-uploader' module");return}$(document.body).on("dragover",function(e){e.originalEvent.dataTransfer.dropEffect="none";return e.preventDefault()});$(document.body).on("drop",function(e){return e.preventDefault()});this.imageBtn=this.editor.toolbar.findButton("image");return this.editor.body.on("dragover",function(e){e.originalEvent.dataTransfer.dropEffect="copy";e.stopPropagation();return e.preventDefault()}).on("dragenter",(function(_this){return function(e){if((_this._entered+=1)===1){_this.show()}e.preventDefault();return e.stopPropagation()}})(this)).on("dragleave",(function(_this){return function(e){if((_this._entered-=1)<=0){_this.hide()}e.preventDefault();return e.stopPropagation()}})(this)).on("drop",(function(_this){return function(e){var file,imageFiles,_i,_j,_len,_len1,_ref;imageFiles=[];_ref=e.originalEvent.dataTransfer.files;for(_i=0,_len=_ref.length;_i<_len;_i++){file=_ref[_i];if(!_this.validFile(file)){alert("「"+file.name+"]」文件不是图片。");_this.hide();return false}imageFiles.push(file)}for(_j=0,_len1=imageFiles.length;_j<_len1;_j++){file=imageFiles[_j];_this.editor.uploader.upload(file,{inline:true})}_this.hide();e.stopPropagation();return e.preventDefault()}})(this))};Dropzone.prototype.show=function(){return this.imageBtn.setActive(true)};Dropzone.prototype.hide=function(){this.imageBtn.setActive(false);return this._entered=0};Dropzone.prototype.validFile=function(file){return file.type.indexOf("image/")>-1};return Dropzone})(SimpleModule);Simditor.connect(Dropzone);return Simditor})); -------------------------------------------------------------------------------- /simditor/static/simditor/scripts/simditor-fullscreen.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-fullscreen', ["jquery","simditor"], function (a0,b1) { 5 | return (root['SimditorFullscreen'] = factory(a0,b1)); 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("simditor")); 12 | } else { 13 | root['SimditorFullscreen'] = factory(jQuery,Simditor); 14 | } 15 | }(this, function ($, Simditor) { 16 | 17 | var SimditorFullscreen, 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 | SimditorFullscreen = (function(superClass) { 22 | extend(SimditorFullscreen, superClass); 23 | 24 | function SimditorFullscreen() { 25 | return SimditorFullscreen.__super__.constructor.apply(this, arguments); 26 | } 27 | 28 | SimditorFullscreen.cls = 'simditor-fullscreen'; 29 | 30 | SimditorFullscreen.i18n = { 31 | 'zh-CN': { 32 | fullscreen: '全屏' 33 | } 34 | }; 35 | 36 | SimditorFullscreen.prototype.name = 'fullscreen'; 37 | 38 | SimditorFullscreen.prototype.needFocus = false; 39 | 40 | SimditorFullscreen.prototype.iconClassOf = function() { 41 | return 'icon-fullscreen'; 42 | }; 43 | 44 | SimditorFullscreen.prototype._init = function() { 45 | SimditorFullscreen.__super__._init.call(this); 46 | this.window = $(window); 47 | this.body = $('body'); 48 | this.editable = this.editor.body; 49 | return this.toolbar = this.editor.toolbar.wrapper; 50 | }; 51 | 52 | SimditorFullscreen.prototype.status = function() { 53 | return this.setActive(this.body.hasClass(this.constructor.cls)); 54 | }; 55 | 56 | SimditorFullscreen.prototype.command = function() { 57 | var editablePadding, isFullscreen; 58 | this.body.toggleClass(this.constructor.cls); 59 | isFullscreen = this.body.hasClass(this.constructor.cls); 60 | if (isFullscreen) { 61 | editablePadding = this.editable.outerHeight() - this.editable.height(); 62 | this.window.on("resize.simditor-fullscreen-" + this.editor.id, (function(_this) { 63 | return function() { 64 | return _this._resize({ 65 | height: _this.window.height() - _this.toolbar.outerHeight() - editablePadding 66 | }); 67 | }; 68 | })(this)).resize(); 69 | } else { 70 | this.window.off("resize.simditor-fullscreen-" + this.editor.id).resize(); 71 | this._resize({ 72 | height: 'auto' 73 | }); 74 | } 75 | return this.setActive(isFullscreen); 76 | }; 77 | 78 | SimditorFullscreen.prototype._resize = function(size) { 79 | return this.editable.height(size.height); 80 | }; 81 | 82 | return SimditorFullscreen; 83 | 84 | })(Simditor.Button); 85 | 86 | Simditor.Toolbar.addButton(SimditorFullscreen); 87 | 88 | return SimditorFullscreen; 89 | 90 | })); -------------------------------------------------------------------------------- /simditor/static/simditor/scripts/simditor-fullscreen.min.js: -------------------------------------------------------------------------------- 1 | (function(root,factory){if(typeof define==="function"&&define.amd){define("simditor-fullscreen",["jquery","simditor"],function(a0,b1){return(root["SimditorFullscreen"]=factory(a0,b1))})}else{if(typeof exports==="object"){module.exports=factory(require("jquery"),require("simditor"))}else{root["SimditorFullscreen"]=factory(jQuery,Simditor)}}}(this,function($,Simditor){var SimditorFullscreen,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},hasProp={}.hasOwnProperty;SimditorFullscreen=(function(superClass){extend(SimditorFullscreen,superClass);function SimditorFullscreen(){return SimditorFullscreen.__super__.constructor.apply(this,arguments)}SimditorFullscreen.cls="simditor-fullscreen";SimditorFullscreen.i18n={"zh-CN":{fullscreen:"全屏"}};SimditorFullscreen.prototype.name="fullscreen";SimditorFullscreen.prototype.needFocus=false;SimditorFullscreen.prototype.iconClassOf=function(){return"icon-fullscreen"};SimditorFullscreen.prototype._init=function(){SimditorFullscreen.__super__._init.call(this);this.window=$(window);this.body=$("body");this.editable=this.editor.body;return this.toolbar=this.editor.toolbar.wrapper};SimditorFullscreen.prototype.status=function(){return this.setActive(this.body.hasClass(this.constructor.cls))};SimditorFullscreen.prototype.command=function(){var editablePadding,isFullscreen;this.body.toggleClass(this.constructor.cls);isFullscreen=this.body.hasClass(this.constructor.cls);if(isFullscreen){editablePadding=this.editable.outerHeight()-this.editable.height();this.window.on("resize.simditor-fullscreen-"+this.editor.id,(function(_this){return function(){return _this._resize({height:_this.window.height()-_this.toolbar.outerHeight()-editablePadding})}})(this)).resize()}else{this.window.off("resize.simditor-fullscreen-"+this.editor.id).resize();this._resize({height:"auto"})}return this.setActive(isFullscreen)};SimditorFullscreen.prototype._resize=function(size){return this.editable.height(size.height)};return SimditorFullscreen})(Simditor.Button);Simditor.Toolbar.addButton(SimditorFullscreen);return SimditorFullscreen})); -------------------------------------------------------------------------------- /simditor/static/simditor/scripts/simditor-markdown.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-markdown', ["jquery","simditor","to-markdown","marked"], function (a0,b1,c2,d3) { 5 | return (root['SimditorMarkdown'] = factory(a0,b1,c2,d3)); 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("simditor"),require("to-markdown"),require("marked")); 12 | } else { 13 | root['SimditorMarkdown'] = factory(jQuery,Simditor,toMarkdown,marked); 14 | } 15 | }(this, function ($, Simditor, toMarkdown, marked) { 16 | 17 | var SimditorMarkdown, 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 | SimditorMarkdown = (function(superClass) { 22 | extend(SimditorMarkdown, superClass); 23 | 24 | function SimditorMarkdown() { 25 | return SimditorMarkdown.__super__.constructor.apply(this, arguments); 26 | } 27 | 28 | SimditorMarkdown.prototype.name = 'markdown'; 29 | 30 | SimditorMarkdown.prototype.icon = 'markdown'; 31 | 32 | SimditorMarkdown.prototype.needFocus = false; 33 | 34 | SimditorMarkdown.prototype._init = function() { 35 | SimditorMarkdown.__super__._init.call(this); 36 | this.markdownEl = $('
        ').insertBefore(this.editor.body); 37 | this.textarea = $(' 3 |
        4 | -------------------------------------------------------------------------------- /simditor/urls.py: -------------------------------------------------------------------------------- 1 | """simditor urls.""" 2 | from __future__ import absolute_import 3 | 4 | import django 5 | 6 | from django.conf import settings 7 | from django.conf.urls import static 8 | from django.contrib.admin.views.decorators import staff_member_required 9 | 10 | from . import views 11 | 12 | if django.VERSION >= (2, 0): 13 | # pylint disable=C0103 14 | from django.urls import path 15 | urlpatterns = [ 16 | path('upload/', staff_member_required(views.UPLOAD), 17 | name='simditor_upload'), 18 | ] 19 | elif django.VERSION >= (1, 8): 20 | from django.conf.urls import url 21 | # pylint disable=C0103 22 | urlpatterns = [ 23 | url(r'^upload/', staff_member_required(views.UPLOAD), 24 | name='simditor_upload'), 25 | ] 26 | else: 27 | from django.conf.urls import patterns, url # pylint disable=C0411 28 | # pylint disable=C0103 29 | urlpatterns = patterns( 30 | '', 31 | url(r'^upload/', staff_member_required(views.UPLOAD), 32 | name='simditor_upload'), 33 | ) 34 | 35 | if settings.DEBUG: 36 | urlpatterns += static.static(settings.MEDIA_URL, 37 | document_root=settings.MEDIA_ROOT) 38 | urlpatterns += static.static(settings.STATIC_URL, 39 | document_root=settings.STATIC_ROOT) 40 | -------------------------------------------------------------------------------- /simditor/utils.py: -------------------------------------------------------------------------------- 1 | """simditor utils.""" 2 | from __future__ import absolute_import 3 | 4 | import os.path 5 | import random 6 | 7 | import string 8 | 9 | from django.core.files.storage import default_storage 10 | from django.template.defaultfilters import slugify 11 | 12 | 13 | class NotAnImageException(Exception): 14 | pass 15 | 16 | 17 | def get_random_string(): 18 | """Get random string.""" 19 | return ''.join(random.sample(string.ascii_lowercase * 6, 6)) 20 | 21 | 22 | def get_slugified_name(filename): 23 | """get_slugified_name.""" 24 | slugified = slugify(filename) 25 | return slugified or get_random_string() 26 | 27 | 28 | def slugify_filename(filename): 29 | """ Slugify filename """ 30 | name, ext = os.path.splitext(filename) 31 | slugified = get_slugified_name(name) 32 | return slugified + ext 33 | 34 | 35 | def get_media_url(path): 36 | """ 37 | Determine system file's media URL. 38 | """ 39 | return default_storage.url(path) 40 | 41 | 42 | def is_valid_image_extension(file_path): 43 | """is_valid_image_extension.""" 44 | valid_extensions = ['.jpeg', '.jpg', '.gif', '.png'] 45 | _, extension = os.path.splitext(file_path) 46 | return extension.lower() in valid_extensions 47 | -------------------------------------------------------------------------------- /simditor/views.py: -------------------------------------------------------------------------------- 1 | """simditor views.""" 2 | from __future__ import absolute_import 3 | 4 | import os 5 | from datetime import datetime 6 | 7 | from django.conf import settings 8 | from django.core.files.storage import default_storage 9 | 10 | from django.http import JsonResponse 11 | 12 | from django.views import generic 13 | from django.views.decorators.csrf import csrf_exempt 14 | 15 | from . import utils, image_processing 16 | 17 | 18 | def get_upload_filename(upload_name): 19 | # Generate date based path to put uploaded file. 20 | date_path = datetime.now().strftime('%Y/%m/%d') 21 | 22 | # Complete upload path (upload_path + date_path). 23 | upload_path = os.path.join(settings.SIMDITOR_UPLOAD_PATH, date_path) 24 | 25 | if getattr(settings, 'SIMDITOR_UPLOAD_SLUGIFY_FILENAME', True): 26 | upload_name = utils.slugify_filename(upload_name) 27 | 28 | return default_storage.get_available_name(os.path.join(upload_path, upload_name)) 29 | 30 | 31 | def upload_handler(request): 32 | files = request.FILES 33 | 34 | upload_config = settings.SIMDITOR_CONFIGS.get( 35 | 'upload', {'fileKey': 'upload'}) 36 | filekey = upload_config.get('fileKey', 'upload') 37 | 38 | uploaded_file = files.get(filekey) 39 | 40 | if not uploaded_file: 41 | retdata = {'file_path': '', 'success': False, 42 | 'msg': '图片上传失败,无法获取到图片对象!'} 43 | return JsonResponse(retdata) 44 | 45 | image_size = upload_config.get('image_size') 46 | if image_size and uploaded_file.size > image_size: 47 | retdata = {'file_path': '', 'success': False, 48 | 'msg': '上传失败,已超出图片最大限制!'} 49 | return JsonResponse(retdata) 50 | 51 | backend = image_processing.get_backend() 52 | 53 | if not getattr(settings, 'SIMDITOR_ALLOW_NONIMAGE_FILES', True): 54 | try: 55 | backend.image_verify(uploaded_file) 56 | except utils.NotAnImageException: 57 | retdata = {'file_path': '', 'success': False, 58 | 'msg': '图片格式错误!'} 59 | return JsonResponse(retdata) 60 | 61 | filename = get_upload_filename(uploaded_file.name) 62 | saved_path = default_storage.save(filename, uploaded_file) 63 | 64 | url = utils.get_media_url(saved_path) 65 | 66 | is_api = settings.SIMDITOR_CONFIGS.get('is_api', False) 67 | url = request.META.get('HTTP_ORIGIN') + url if is_api else url 68 | 69 | retdata = {'file_path': url, 'success': True, 'msg': '上传成功!'} 70 | 71 | return JsonResponse(retdata) 72 | 73 | 74 | class ImageUploadView(generic.View): 75 | """ImageUploadView.""" 76 | 77 | http_method_names = ['post'] 78 | 79 | def post(self, request, **kwargs): 80 | """Post.""" 81 | return upload_handler(request) 82 | 83 | 84 | UPLOAD = csrf_exempt(ImageUploadView.as_view()) 85 | -------------------------------------------------------------------------------- /simditor/widgets.py: -------------------------------------------------------------------------------- 1 | """simditor widgets.""" 2 | from __future__ import absolute_import 3 | 4 | import django 5 | from django import forms 6 | from django.conf import settings 7 | from django.core.exceptions import ImproperlyConfigured 8 | from django.core.serializers.json import DjangoJSONEncoder 9 | 10 | from django.template.loader import render_to_string 11 | from django.utils.safestring import mark_safe 12 | from django.utils.html import conditional_escape 13 | from django.utils.functional import Promise 14 | 15 | try: 16 | # Django >=2.1 17 | from django.forms.widgets import get_default_renderer 18 | IS_NEW_WIDGET = True 19 | except ImportError: 20 | IS_NEW_WIDGET = False 21 | 22 | try: 23 | # Django >=1.7 24 | from django.forms.utils import flatatt 25 | except ImportError: 26 | # Django <1.7 27 | from django.forms.util import flatatt # pylint disable=E0611, E0401 28 | 29 | if django.VERSION >= (4, 0): 30 | from django.utils.encoding import force_str 31 | else: 32 | from django.utils.encoding import force_text as force_str 33 | 34 | 35 | class LazyEncoder(DjangoJSONEncoder): 36 | """LazyEncoder.""" 37 | 38 | # pylint disable=E0202 39 | def default(self, obj): 40 | if isinstance(obj, Promise): 41 | return force_str(obj) 42 | return super(LazyEncoder, self).default(obj) 43 | 44 | 45 | JSON_ENCODE = LazyEncoder().encode 46 | 47 | 48 | FULL_TOOLBAR = [ 49 | 'title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale', 50 | 'color', '|', 'ol', 'ul', 'blockquote', 'code', 'table', '|', 'link', 51 | 'image', 'hr', '|', 'indent', 'outdent', 'alignment', 'checklist', 52 | 'markdown', 'fullscreen' 53 | ] 54 | 55 | DEFAULT_TOOLBAR = [ 56 | 'title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale', 57 | 'color', '|', 'ol', 'ul', 'blockquote', 'table', '|', 'link', 58 | 'image', 'hr', '|', 'indent', 'outdent', 'alignment' 59 | ] 60 | 61 | DEFAULT_CONFIG = { 62 | 'toolbar': DEFAULT_TOOLBAR, 63 | 'cleanPaste': True, 64 | 'tabIndent': True, 65 | 'pasteImage': True, 66 | 'upload': { 67 | 'url': '/', 68 | 'fileKey': 'file' 69 | } 70 | } 71 | 72 | 73 | class SimditorWidget(forms.Textarea): 74 | """ 75 | Widget providing Simditor for Rich Text Editing.abs 76 | Supports direct image uploads and embed. 77 | """ 78 | class Media: 79 | """Media.""" 80 | 81 | css_list = [ 82 | 'simditor/styles/simditor.min.css' 83 | ] 84 | 85 | if 'emoji' in settings.SIMDITOR_TOOLBAR: 86 | css_list.append('simditor/styles/simditor-emoji.css') 87 | 88 | if 'fullscreen' in settings.SIMDITOR_TOOLBAR: 89 | css_list.append('simditor/styles/simditor-fullscreen.min.css') 90 | 91 | if 'checklist' in settings.SIMDITOR_TOOLBAR: 92 | css_list.append('simditor/styles/simditor-checklist.min.css') 93 | 94 | if 'markdown' in settings.SIMDITOR_TOOLBAR: 95 | css_list.append('simditor/styles/simditor-markdown.min.css') 96 | 97 | css = {'all': tuple(settings.STATIC_URL + url for url in css_list)} 98 | 99 | jquery_list = ['simditor/scripts/jquery.min.js', 100 | 'simditor/scripts/module.min.js', 101 | 'simditor/scripts/hotkeys.min.js', 102 | 'simditor/scripts/uploader.min.js', 103 | 'simditor/scripts/simditor.min.js'] 104 | 105 | if 'fullscreen' in settings.SIMDITOR_TOOLBAR: 106 | jquery_list.append('simditor/scripts/simditor-fullscreen.min.js') 107 | 108 | if 'checklist' in settings.SIMDITOR_TOOLBAR: 109 | jquery_list.append('simditor/scripts/simditor-checklist.min.js') 110 | 111 | if 'markdown' in settings.SIMDITOR_TOOLBAR: 112 | jquery_list.append('simditor/scripts/marked.min.js') 113 | jquery_list.append('simditor/scripts/to-markdown.min.js') 114 | jquery_list.append('simditor/scripts/simditor-markdown.min.js') 115 | 116 | if 'image' in settings.SIMDITOR_TOOLBAR: 117 | jquery_list.append('simditor/scripts/simditor-dropzone.min.js') 118 | 119 | if 'emoji' in settings.SIMDITOR_TOOLBAR: 120 | jquery_list.append('simditor/scripts/simditor-emoji.js') 121 | 122 | js = tuple(settings.STATIC_URL + url for url in jquery_list) 123 | 124 | try: 125 | 126 | js += (settings.STATIC_URL + 'simditor/simditor-init.js',) 127 | except AttributeError: 128 | raise ImproperlyConfigured("django-simditor requires \ 129 | SIMDITOR_MEDIA_PREFIX setting. This setting specifies a \ 130 | URL prefix to the ckeditor JS and CSS media (not \ 131 | uploaded media). Make sure to use a trailing slash: \ 132 | SIMDITOR_MEDIA_PREFIX = '/media/simditor/'") 133 | 134 | def __init__(self, *args, **kwargs): 135 | super(SimditorWidget, self).__init__(*args, **kwargs) 136 | # Setup config from defaults. 137 | self.config = DEFAULT_CONFIG.copy() 138 | 139 | # Try to get valid config from settings. 140 | configs = getattr(settings, 'SIMDITOR_CONFIGS', None) 141 | if configs: 142 | if isinstance(configs, dict): 143 | self.config.update(configs) 144 | else: 145 | raise ImproperlyConfigured( 146 | 'SIMDITOR_CONFIGS setting must be a dictionary type.') 147 | 148 | def build_attrs(self, base_attrs, extra_attrs=None, **kwargs): 149 | """ 150 | Helper function for building an attribute dictionary. 151 | This is combination of the same method from Django<=1.10 and Django1.11 152 | """ 153 | attrs = dict(base_attrs, **kwargs) 154 | if extra_attrs: 155 | attrs.update(extra_attrs) 156 | return attrs 157 | 158 | def render(self, name, value, attrs=None, renderer=None): 159 | if value is None: 160 | value = '' 161 | final_attrs = self.build_attrs(self.attrs, attrs, name=name) 162 | 163 | params = ('simditor/widget.html', { 164 | 'final_attrs': flatatt(final_attrs), 165 | 'value': conditional_escape(force_str(value)), 166 | 'id': final_attrs['id'], 167 | 'config': JSON_ENCODE(self.config) 168 | }) 169 | 170 | if renderer is None and IS_NEW_WIDGET: 171 | renderer = get_default_renderer() 172 | 173 | data = renderer.render(*params) if IS_NEW_WIDGET else render_to_string(*params) 174 | 175 | return mark_safe(data) 176 | -------------------------------------------------------------------------------- /simditor_demo/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/simditor_demo/app/__init__.py -------------------------------------------------------------------------------- /simditor_demo/app/admin.py: -------------------------------------------------------------------------------- 1 | """demo admin.""" 2 | from django.contrib import admin 3 | 4 | from app.models import News 5 | 6 | 7 | @admin.register(News) 8 | class NewsAdmin(admin.ModelAdmin): 9 | """NewsAdmin.""" 10 | -------------------------------------------------------------------------------- /simditor_demo/app/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AppConfig(AppConfig): 5 | name = 'app' 6 | -------------------------------------------------------------------------------- /simditor_demo/app/forms.py: -------------------------------------------------------------------------------- 1 | """app forms.""" 2 | from __future__ import absolute_import 3 | 4 | from django import forms 5 | 6 | from simditor.fields import RichTextFormField 7 | 8 | 9 | class SimditorForm(forms.ModelForm): 10 | """SimditorForm.""" 11 | content = RichTextFormField() 12 | -------------------------------------------------------------------------------- /simditor_demo/app/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.2 on 2017-06-08 07:44 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import simditor.fields 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='News', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('content', simditor.fields.RichTextField()), 22 | ], 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /simditor_demo/app/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/simditor_demo/app/migrations/__init__.py -------------------------------------------------------------------------------- /simditor_demo/app/models.py: -------------------------------------------------------------------------------- 1 | """demo.""" 2 | from django.db import models 3 | 4 | from simditor.fields import RichTextField 5 | 6 | 7 | class News(models.Model): 8 | """News.""" 9 | content = RichTextField() 10 | -------------------------------------------------------------------------------- /simditor_demo/app/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /simditor_demo/app/views.py: -------------------------------------------------------------------------------- 1 | """app views.""" 2 | 3 | from django import forms 4 | 5 | from django.views import generic 6 | 7 | from simditor.fields import RichTextFormField 8 | try: 9 | from django.urls import reverse 10 | except ImportError: # Django < 2.0 11 | from django.core.urlresolvers import reverse 12 | 13 | 14 | class SimditorForm(forms.Form): 15 | content = RichTextFormField() 16 | 17 | 18 | class IndexView(generic.FormView): 19 | form_class = SimditorForm 20 | 21 | template_name = 'index.html' 22 | 23 | def get_success_url(self): 24 | return reverse('simditor-form') 25 | -------------------------------------------------------------------------------- /simditor_demo/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "simditor_demo.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /simditor_demo/simditor_demo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istommao/django-simditor/026e06571382671a97f382a1ff4e1af8ad5048b6/simditor_demo/simditor_demo/__init__.py -------------------------------------------------------------------------------- /simditor_demo/simditor_demo/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for simditor_demo project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.11.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.11/ref/settings/ 11 | """ 12 | 13 | import os 14 | import tempfile 15 | 16 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 17 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = '8wpi^@j!glnnb#_(0&mrpn6@=2les0@loxnt*j0b*8u1k!&5$m' 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = [] 30 | 31 | 32 | # Application definition 33 | 34 | INSTALLED_APPS = [ 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | 42 | 'simditor', 43 | 'app' 44 | ] 45 | 46 | MIDDLEWARE = [ 47 | 'django.middleware.security.SecurityMiddleware', 48 | 'django.contrib.sessions.middleware.SessionMiddleware', 49 | 'django.middleware.common.CommonMiddleware', 50 | 'django.middleware.csrf.CsrfViewMiddleware', 51 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 52 | 'django.contrib.messages.middleware.MessageMiddleware', 53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 54 | ] 55 | 56 | ROOT_URLCONF = 'simditor_demo.urls' 57 | 58 | TEMPLATES = [ 59 | { 60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 61 | 'DIRS': ['templates'], 62 | 'APP_DIRS': True, 63 | 'OPTIONS': { 64 | 'context_processors': [ 65 | 'django.template.context_processors.debug', 66 | 'django.template.context_processors.request', 67 | 'django.contrib.auth.context_processors.auth', 68 | 'django.contrib.messages.context_processors.messages', 69 | ], 70 | }, 71 | }, 72 | ] 73 | 74 | WSGI_APPLICATION = 'simditor_demo.wsgi.application' 75 | 76 | 77 | # Database 78 | # https://docs.djangoproject.com/en/1.11/ref/settings/#databases 79 | 80 | DATABASES = { 81 | 'default': { 82 | 'ENGINE': 'django.db.backends.sqlite3', 83 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 84 | } 85 | } 86 | 87 | 88 | # Password validation 89 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 90 | 91 | AUTH_PASSWORD_VALIDATORS = [ 92 | { 93 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 94 | }, 95 | { 96 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 97 | }, 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 100 | }, 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 103 | }, 104 | ] 105 | 106 | 107 | # Internationalization 108 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 109 | 110 | LANGUAGE_CODE = 'en-us' 111 | 112 | TIME_ZONE = 'UTC' 113 | 114 | USE_I18N = True 115 | 116 | USE_L10N = True 117 | 118 | USE_TZ = True 119 | 120 | 121 | # Static files (CSS, JavaScript, Images) 122 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 123 | 124 | STATIC_URL = '/static/' 125 | MEDIA_URL = '/media/' 126 | # STATIC_ROOT = os.path.join(tempfile.gettempdir(), 'sm_static') 127 | STATIC_ROOT = os.path.join(BASE_DIR, 'static') 128 | 129 | MEDIA_ROOT = os.path.join(tempfile.gettempdir(), 'sm_media') 130 | 131 | SIMDITOR_UPLOAD_PATH = 'uploads/' 132 | SIMDITOR_IMAGE_BACKEND = 'pillow' 133 | # SIMDITOR_JQUERY_URL = '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js' 134 | 135 | SIMDITOR_TOOLBAR = [ 136 | 'title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale', 137 | 'color', '|', 'ol', 'ul', 'blockquote', 'code', 'table', '|', 'link', 138 | 'image', 'hr', '|', 'indent', 'outdent', 'alignment', 139 | 'markdown', 'emoji', 'checklist', 'fullscreen' 140 | ] 141 | 142 | SIMDITOR_CONFIGS = { 143 | 'toolbar': SIMDITOR_TOOLBAR, 144 | 'upload': { 145 | 'url': '/simditor/upload/', 146 | 'fileKey': 'upload', 147 | 'image_size': 1024 * 1024 * 4 # max image size 4MB 148 | }, 149 | 'emoji': { 150 | 'imagePath': '/static/simditor/images/emoji/' 151 | }, 152 | 'is_api': False 153 | } 154 | -------------------------------------------------------------------------------- /simditor_demo/simditor_demo/urls.py: -------------------------------------------------------------------------------- 1 | """simditor_demo URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.11/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf import settings 17 | from django.conf.urls import include, url 18 | from django.conf.urls.static import static 19 | from django.contrib import admin 20 | from django.views import generic 21 | from django.views.decorators.csrf import csrf_exempt 22 | 23 | from app.views import IndexView 24 | 25 | from simditor.views import upload_handler 26 | 27 | 28 | class ImageUploadView(generic.View): 29 | """ImageUploadView.""" 30 | 31 | http_method_names = ['post'] 32 | 33 | def post(self, request, **kwargs): 34 | """Post.""" 35 | return upload_handler(request) 36 | 37 | 38 | # pylint disable=C0103 39 | urlpatterns = [ 40 | url(r'^$', IndexView.as_view(), name='simditor-form'), 41 | url(r'^admin/', admin.site.urls), 42 | url(r'^simditor/upload', csrf_exempt(ImageUploadView.as_view())), 43 | ] 44 | 45 | urlpatterns += static( 46 | settings.STATIC_URL, 47 | document_root=settings.STATIC_ROOT 48 | ) 49 | 50 | urlpatterns += static( 51 | settings.MEDIA_URL, 52 | document_root=settings.MEDIA_ROOT 53 | ) 54 | -------------------------------------------------------------------------------- /simditor_demo/simditor_demo/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for simditor_demo project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "simditor_demo.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /simditor_demo/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 |
        9 | {% csrf_token %} 10 | {{ form.media }} 11 | {{ form.as_p }} 12 |

        13 |
        14 | 15 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | skip_missing_interpreters=True 3 | envlist= 4 | py27-coverage-init 5 | {py27,py34}-django{18,19,110} 6 | py27-{lint,isort,coverage-report} 7 | 8 | [testenv] 9 | usedevelop=True 10 | setenv= 11 | DJANGO_SETTINGS_MODULE=ckeditor_demo.settings 12 | # DISPLAY = :0 13 | COVERAGE_FILE=.coverage.{envname} 14 | passenv = LC_ALL LANG TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH DISPLAY 15 | changedir = {toxinidir} 16 | commands= {envbindir}/coverage run manage.py test ckeditor_demo 17 | deps= 18 | coverage==4.2 19 | Pillow==3.2.0 20 | selenium==2.53.6 21 | django19: Django>=1.9,<1.10 22 | django18: Django>=1.8,<1.9 23 | django110: Django>=1.10,<1.11 24 | 25 | [testenv:py27-coverage-init] 26 | setenv = 27 | COVERAGE_FILE = .coverage 28 | commands = 29 | coverage erase 30 | deps = 31 | coverage==4.2 32 | 33 | [testenv:py27-coverage-report] 34 | setenv = 35 | COVERAGE_FILE = .coverage 36 | commands= 37 | coverage combine 38 | coverage report -m 39 | deps = 40 | coverage==4.2 41 | 42 | 43 | [testenv:py27-lint] 44 | commands = {envbindir}/flake8 45 | deps = 46 | flake8 47 | 48 | [testenv:py27-isort] 49 | commands = {envbindir}/isort -vb --recursive --check-only ckeditor ckeditor_demo 50 | deps = 51 | isort 52 | --------------------------------------------------------------------------------