forked from mirror/MrDoc
v0.4.1 添加文档历史版本功能,优化样式,提升交互体验
This commit is contained in:
parent
dc2578177b
commit
2b599398e2
@ -1,5 +1,12 @@
|
||||
## 版本更新记录
|
||||
|
||||
### v0.4.1
|
||||
|
||||
- 添加文档历史版本功能,可在修改文档时对比查看和选择恢复文档的历史版本;
|
||||
- 优化文档阅读页面样式,调整悬浮的目录样式;
|
||||
- 优化编辑器交互,增加所有网络请求的加载框显示;
|
||||
- 优化文集阅读页面样式,新增文集创建的管理链接;
|
||||
|
||||
### v0.4 2020-04-06
|
||||
|
||||
- 新增附件功能,可在个人中心管理附件,在文档编辑器中上传和添加附件;
|
||||
|
@ -25,7 +25,7 @@ SECRET_KEY = '5&71mt9@^58zdg*_!t(x6g14q*@84d%ptr%%s6e0l50zs0we3d'
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = False
|
||||
|
||||
VERSIONS = '0.4'
|
||||
VERSIONS = '0.4.1'
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
|
43
README.md
43
README.md
@ -6,28 +6,30 @@
|
||||
|
||||
`MrDoc`是基于`Python`开发的在线文档系统,适合作为个人和小型团队的文档、知识和笔记管理工具。
|
||||
|
||||
### 特性:
|
||||
## 开源地址
|
||||
|
||||
**Gitee:** [https://gitee.com/zmister/MrDoc](https://gitee.com/zmister/MrDoc)
|
||||
|
||||
**GitHub:** [https://github.com/zmister2016/MrDoc](https://github.com/zmister2016/MrDoc)
|
||||
|
||||
## 特性:
|
||||
|
||||
- 简洁的站点与用户系统
|
||||
- 支持用户注册、用户登录、用户管理、管理员等控制等功能;
|
||||
- 支持全站关闭注册;
|
||||
- 支持注册邀请码配置;
|
||||
- 支持广告位自定义配置;
|
||||
- 支持统计代码自定义配置;
|
||||
- 支持注册邀请码配置,支持全站关闭注册;
|
||||
- 支持广告代码、统计代码自定义配置;
|
||||
|
||||
- 结构清晰地文档系统
|
||||
- 基于文集的文档撰写和阅读;
|
||||
- 基于文集进行文档撰写和阅读,拥有文集、文档、文档模板、图片和附件5大模块;
|
||||
- 使用基于`Editormd`的`Markdown`编辑器并扩展,以`Markdown`语法进行文档写作;
|
||||
- 提供文档模板功能,支持文档模板的创建、修改;
|
||||
- 两栏式文档阅读页面,支持文档阅读页面的字体缩放,字体类型修改,页面社交分享,良好的移动端阅读体验;
|
||||
- 支持三级目录层级显示;
|
||||
- 支持文集后台导出为`markdown`文本格式`.md`文件、前台导出为`EPUB`等格式文件;
|
||||
- 基于文集进行权限控制,提供公开、私密、指定用户可见、访问码可见4种权限模式;
|
||||
- 两栏式文档阅读页面、三级目录层级显示,文档阅读字体缩放,字体类型切换,页面社交分享,移动端阅读优化;
|
||||
- 支持文集后台导出打包`markdown`文本格式`.md`文件、前台导出为`EPUB`等格式文件;
|
||||
- 基于文集进行文档权限控制,提供公开、私密、指定用户可见、访问码可见4种权限模式;
|
||||
- 支持基于账户的`API`接口,可以借助账户`token`通过`API`获取文集、上传图片和创建文档;
|
||||
- 支持文集协作功能,一个文集可以拥有一个创建者和多个协作者,可灵活选择协作权限;
|
||||
- 支持附件上传和管理、图片上传和管理;
|
||||
- 支持文档历史版本功能;
|
||||
|
||||
当前版本为:**v0.4**,版本发布时间为**2020-04-06**
|
||||
当前版本为:**v0.4.1**,版本发布时间为**2020-04-11**
|
||||
|
||||
完整更新记录详见:[CHANGES.md](./CHANGES.md)
|
||||
|
||||
@ -37,9 +39,9 @@
|
||||
|
||||
MrDoc基于Python语言的Django Web框架配合前端的LayUI、JQuery等库进行开发。
|
||||
|
||||
后端环境推荐使用Python3.4+、Django2.1+的版本。
|
||||
在Django2.1、2.2和Python3.5、3.6、3.7上测试运行良好。
|
||||
|
||||
## 安装简明教程
|
||||
## 简明安装教程
|
||||
|
||||
### 1、安装依赖库
|
||||
```
|
||||
@ -114,6 +116,16 @@ python manage.py runserver
|
||||
|
||||
加入MrDoc交流QQ群,群号为**735507293**,入群密码:**mrdoc**
|
||||
|
||||
### 3、联系作者
|
||||
|
||||
微信(WeChat):**taoist_ling**
|
||||
|
||||
## 赞赏项目
|
||||
|
||||
如果MrDoc对你有所帮助,欢迎给予开发者赞赏,助力项目更好发展。
|
||||
|
||||

|
||||
|
||||
|
||||
## 更多应用截图
|
||||
|
||||
@ -137,3 +149,4 @@ python manage.py runserver
|
||||
|
||||
### 注册页面
|
||||

|
||||
|
||||
|
27
app_doc/migrations/0018_dochistory.py
Normal file
27
app_doc/migrations/0018_dochistory.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Generated by Django 2.2.11 on 2020-04-09 20:59
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app_doc', '0017_auto_20200404_0934'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DocHistory',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('pre_content', models.TextField(blank=True, null=True, verbose_name='文档历史编辑内容')),
|
||||
('create_time', models.DateTimeField(auto_now=True)),
|
||||
('doc', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app_doc.Doc')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '文档历史',
|
||||
'verbose_name_plural': '文档历史',
|
||||
},
|
||||
),
|
||||
]
|
21
app_doc/migrations/0019_dochistory_create_user.py
Normal file
21
app_doc/migrations/0019_dochistory_create_user.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Generated by Django 2.2.11 on 2020-04-09 21:05
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('app_doc', '0018_dochistory'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dochistory',
|
||||
name='create_user',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
@ -72,6 +72,20 @@ class Doc(models.Model):
|
||||
"doc_id":self.pk}
|
||||
)
|
||||
|
||||
# 文档历史模型
|
||||
class DocHistory(models.Model):
|
||||
doc = models.ForeignKey(Doc,on_delete=models.CASCADE)
|
||||
pre_content = models.TextField(verbose_name='文档历史编辑内容',null=True,blank=True)
|
||||
create_user = models.ForeignKey(User,on_delete=models.SET_NULL,null=True)
|
||||
create_time = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.doc
|
||||
|
||||
class Meta:
|
||||
verbose_name = '文档历史'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
# 文档模板模型
|
||||
class DocTemp(models.Model):
|
||||
name = models.CharField(verbose_name="模板名称",max_length=50)
|
||||
|
@ -24,6 +24,8 @@ urlpatterns = [
|
||||
path('modify_doc/<int:doc_id>/', views.modify_doc, name="modify_doc"), # 修改文档
|
||||
path('del_doc/',views.del_doc,name="del_doc"), # 删除文档
|
||||
path('manage_doc/',views.manage_doc,name="manage_doc"), # 管理文档
|
||||
path('diff_doc/<int:doc_id>-<int:his_id>/',views.diff_doc,name='diff_doc'), # 对比文档历史版本
|
||||
path('manage_doc_history/<int:doc_id>/',views.manage_doc_history,name='manage_doc_history'), # 管理文档历史版本
|
||||
#################文档模板相关
|
||||
path('manage_doctemp/',views.manage_doctemp,name='manage_doctemp'), # 文档模板列表
|
||||
path('create_doctemp/',views.create_doctemp,name="create_doctemp"), # 创建文档模板
|
||||
|
@ -279,8 +279,8 @@ def manage_project(request):
|
||||
try:
|
||||
search_kw = request.GET.get('kw', None)
|
||||
if search_kw:
|
||||
pro_list = Project.objects.filter(create_user=request.user,intro__icontains=search_kw)
|
||||
paginator = Paginator(pro_list, 10)
|
||||
pro_list = Project.objects.filter(create_user=request.user,intro__icontains=search_kw).order_by('-create_time')
|
||||
paginator = Paginator(pro_list, 15)
|
||||
page = request.GET.get('page', 1)
|
||||
try:
|
||||
pros = paginator.page(page)
|
||||
@ -290,8 +290,8 @@ def manage_project(request):
|
||||
pros = paginator.page(paginator.num_pages)
|
||||
pros.kw = search_kw
|
||||
else:
|
||||
pro_list = Project.objects.filter(create_user=request.user)
|
||||
paginator = Paginator(pro_list, 10)
|
||||
pro_list = Project.objects.filter(create_user=request.user).order_by('-create_time')
|
||||
paginator = Paginator(pro_list, 15)
|
||||
page = request.GET.get('page', 1)
|
||||
try:
|
||||
pros = paginator.page(page)
|
||||
@ -532,6 +532,7 @@ def modify_doc(request,doc_id):
|
||||
if (request.user == doc.create_user) or (pro_colla[0].role == 1):
|
||||
doc_list = Doc.objects.filter(top_doc=project.id)
|
||||
doctemp_list = DocTemp.objects.filter(create_user=request.user)
|
||||
history_list = DocHistory.objects.filter(doc=doc).order_by('-create_time')
|
||||
return render(request,'app_doc/modify_doc.html',locals())
|
||||
else:
|
||||
return render(request,'403.html')
|
||||
@ -555,6 +556,12 @@ def modify_doc(request,doc_id):
|
||||
pro_colla = ProjectCollaborator.objects.filter(project=project, user=request.user)
|
||||
# 验证用户有权限修改文档 - 文档的创建者或文集的高级协作者
|
||||
if (request.user == doc.create_user) or (pro_colla[0].role == 1):
|
||||
# 将现有文档内容写入到文档历史中
|
||||
DocHistory.objects.create(
|
||||
doc = doc,
|
||||
pre_content = doc.pre_content,
|
||||
create_user = request.user
|
||||
)
|
||||
# 更新文档内容
|
||||
Doc.objects.filter(id=int(doc_id)).update(
|
||||
name=doc_name,
|
||||
@ -698,6 +705,76 @@ def manage_doc(request):
|
||||
else:
|
||||
return HttpResponse('方法不允许')
|
||||
|
||||
# 查看对比文档历史版本
|
||||
@login_required()
|
||||
def diff_doc(request,doc_id,his_id):
|
||||
if request.method == 'GET':
|
||||
try:
|
||||
doc = Doc.objects.get(id=doc_id) # 查询文档信息
|
||||
project = Project.objects.get(id=doc.top_doc) # 查询文档所属的文集信息
|
||||
pro_colla = ProjectCollaborator.objects.filter(project=project, user=request.user) # 查询用户的协作文集信息
|
||||
if (request.user == doc.create_user) or (pro_colla[0].role == 1):
|
||||
history = DocHistory.objects.get(id=his_id)
|
||||
history_list = DocHistory.objects.filter(doc=doc).order_by('-create_time')
|
||||
if history.doc == doc:
|
||||
return render(request, 'app_doc/diff_doc.html', locals())
|
||||
else:
|
||||
return render(request, '403.html')
|
||||
else:
|
||||
return render(request, '403.html')
|
||||
except Exception as e:
|
||||
if settings.DEBUG:
|
||||
print(traceback.print_exc())
|
||||
return render(request, '404.html')
|
||||
|
||||
elif request.method == 'POST':
|
||||
try:
|
||||
doc = Doc.objects.get(id=doc_id) # 查询文档信息
|
||||
project = Project.objects.get(id=doc.top_doc) # 查询文档所属的文集信息
|
||||
pro_colla = ProjectCollaborator.objects.filter(project=project, user=request.user) # 查询用户的协作文集信息
|
||||
if (request.user == doc.create_user) or (pro_colla[0].role == 1):
|
||||
history = DocHistory.objects.get(id=his_id)
|
||||
if history.doc == doc:
|
||||
return JsonResponse({'status':True,'data':history.pre_content})
|
||||
else:
|
||||
return JsonResponse({'status': False, 'data': '非法请求'})
|
||||
else:
|
||||
return JsonResponse({'status':False,'data':'非法请求'})
|
||||
except Exception as e:
|
||||
if settings.DEBUG:
|
||||
print(traceback.print_exc())
|
||||
return JsonResponse({'status':False,'data':'获取异常'})
|
||||
|
||||
# 管理文档历史版本
|
||||
@login_required()
|
||||
def manage_doc_history(request,doc_id):
|
||||
if request.method == 'GET':
|
||||
try:
|
||||
doc = Doc.objects.get(id=doc_id,create_user=request.user)
|
||||
history_list = DocHistory.objects.filter(create_user=request.user,doc=doc_id).order_by('-create_time')
|
||||
paginator = Paginator(history_list, 15)
|
||||
page = request.GET.get('page', 1)
|
||||
try:
|
||||
historys = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
historys = paginator.page(1)
|
||||
except EmptyPage:
|
||||
historys = paginator.page(paginator.num_pages)
|
||||
return render(request, 'app_doc/manage_doc_history.html', locals())
|
||||
except Exception as e:
|
||||
if settings.DEBUG:
|
||||
print(traceback.print_exc())
|
||||
return render(request, '404.html')
|
||||
elif request.method == 'POST':
|
||||
try:
|
||||
history_id = request.POST.get('history_id','')
|
||||
DocHistory.objects.filter(id=history_id,doc=doc_id,create_user=request.user).delete()
|
||||
return JsonResponse({'status':True,'data':'删除成功'})
|
||||
except:
|
||||
if settings.DEBUG:
|
||||
print(traceback.print_exc())
|
||||
return JsonResponse({'status':False,'data':'出现异常'})
|
||||
|
||||
|
||||
# 创建文档模板
|
||||
@login_required()
|
||||
|
BIN
captrue/mrdoc-zan.png
Normal file
BIN
captrue/mrdoc-zan.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
349
static/docdiff/codemirror.css
Normal file
349
static/docdiff/codemirror.css
Normal file
@ -0,0 +1,349 @@
|
||||
/* BASICS */
|
||||
|
||||
.CodeMirror {
|
||||
/* Set height, width, borders, and global font properties here */
|
||||
font-family: monospace;
|
||||
height: 300px;
|
||||
color: black;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
/* PADDING */
|
||||
|
||||
.CodeMirror-lines {
|
||||
padding: 4px 0; /* Vertical padding around content */
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-line,
|
||||
.CodeMirror pre.CodeMirror-line-like {
|
||||
padding: 0 4px; /* Horizontal padding of content */
|
||||
}
|
||||
|
||||
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||
background-color: white; /* The little square between H and V scrollbars */
|
||||
}
|
||||
|
||||
/* GUTTER */
|
||||
|
||||
.CodeMirror-gutters {
|
||||
border-right: 1px solid #ddd;
|
||||
background-color: #f7f7f7;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.CodeMirror-linenumbers {}
|
||||
.CodeMirror-linenumber {
|
||||
padding: 0 3px 0 5px;
|
||||
min-width: 20px;
|
||||
text-align: right;
|
||||
color: #999;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.CodeMirror-guttermarker { color: black; }
|
||||
.CodeMirror-guttermarker-subtle { color: #999; }
|
||||
|
||||
/* CURSOR */
|
||||
|
||||
.CodeMirror-cursor {
|
||||
border-left: 1px solid black;
|
||||
border-right: none;
|
||||
width: 0;
|
||||
}
|
||||
/* Shown when moving in bi-directional text */
|
||||
.CodeMirror div.CodeMirror-secondarycursor {
|
||||
border-left: 1px solid silver;
|
||||
}
|
||||
.cm-fat-cursor .CodeMirror-cursor {
|
||||
width: auto;
|
||||
border: 0 !important;
|
||||
background: #7e7;
|
||||
}
|
||||
.cm-fat-cursor div.CodeMirror-cursors {
|
||||
z-index: 1;
|
||||
}
|
||||
.cm-fat-cursor-mark {
|
||||
background-color: rgba(20, 255, 20, 0.5);
|
||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||
-moz-animation: blink 1.06s steps(1) infinite;
|
||||
animation: blink 1.06s steps(1) infinite;
|
||||
}
|
||||
.cm-animate-fat-cursor {
|
||||
width: auto;
|
||||
border: 0;
|
||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||
-moz-animation: blink 1.06s steps(1) infinite;
|
||||
animation: blink 1.06s steps(1) infinite;
|
||||
background-color: #7e7;
|
||||
}
|
||||
@-moz-keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
@-webkit-keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
@keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
|
||||
/* Can style cursor different in overwrite (non-insert) mode */
|
||||
.CodeMirror-overwrite .CodeMirror-cursor {}
|
||||
|
||||
.cm-tab { display: inline-block; text-decoration: inherit; }
|
||||
|
||||
.CodeMirror-rulers {
|
||||
position: absolute;
|
||||
left: 0; right: 0; top: -50px; bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.CodeMirror-ruler {
|
||||
border-left: 1px solid #ccc;
|
||||
top: 0; bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* DEFAULT THEME */
|
||||
|
||||
.cm-s-default .cm-header {color: blue;}
|
||||
.cm-s-default .cm-quote {color: #090;}
|
||||
.cm-negative {color: #d44;}
|
||||
.cm-positive {color: #292;}
|
||||
.cm-header, .cm-strong {font-weight: bold;}
|
||||
.cm-em {font-style: italic;}
|
||||
.cm-link {text-decoration: underline;}
|
||||
.cm-strikethrough {text-decoration: line-through;}
|
||||
|
||||
.cm-s-default .cm-keyword {color: #708;}
|
||||
.cm-s-default .cm-atom {color: #219;}
|
||||
.cm-s-default .cm-number {color: #164;}
|
||||
.cm-s-default .cm-def {color: #00f;}
|
||||
.cm-s-default .cm-variable,
|
||||
.cm-s-default .cm-punctuation,
|
||||
.cm-s-default .cm-property,
|
||||
.cm-s-default .cm-operator {}
|
||||
.cm-s-default .cm-variable-2 {color: #05a;}
|
||||
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
|
||||
.cm-s-default .cm-comment {color: #a50;}
|
||||
.cm-s-default .cm-string {color: #a11;}
|
||||
.cm-s-default .cm-string-2 {color: #f50;}
|
||||
.cm-s-default .cm-meta {color: #555;}
|
||||
.cm-s-default .cm-qualifier {color: #555;}
|
||||
.cm-s-default .cm-builtin {color: #30a;}
|
||||
.cm-s-default .cm-bracket {color: #997;}
|
||||
.cm-s-default .cm-tag {color: #170;}
|
||||
.cm-s-default .cm-attribute {color: #00c;}
|
||||
.cm-s-default .cm-hr {color: #999;}
|
||||
.cm-s-default .cm-link {color: #00c;}
|
||||
|
||||
.cm-s-default .cm-error {color: #f00;}
|
||||
.cm-invalidchar {color: #f00;}
|
||||
|
||||
.CodeMirror-composing { border-bottom: 2px solid; }
|
||||
|
||||
/* Default styles for common addons */
|
||||
|
||||
div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
|
||||
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
|
||||
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
|
||||
.CodeMirror-activeline-background {background: #e8f2ff;}
|
||||
|
||||
/* STOP */
|
||||
|
||||
/* The rest of this file contains styles related to the mechanics of
|
||||
the editor. You probably shouldn't touch them. */
|
||||
|
||||
.CodeMirror {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
overflow: scroll !important; /* Things will break if this is overridden */
|
||||
/* 30px is the magic margin used to hide the element's real scrollbars */
|
||||
/* See overflow: hidden in .CodeMirror */
|
||||
margin-bottom: -30px; margin-right: -30px;
|
||||
padding-bottom: 30px;
|
||||
height: 100%;
|
||||
outline: none; /* Prevent dragging from highlighting the element */
|
||||
position: relative;
|
||||
}
|
||||
.CodeMirror-sizer {
|
||||
position: relative;
|
||||
border-right: 30px solid transparent;
|
||||
}
|
||||
|
||||
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
||||
before actual scrolling happens, thus preventing shaking and
|
||||
flickering artifacts. */
|
||||
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
display: none;
|
||||
}
|
||||
.CodeMirror-vscrollbar {
|
||||
right: 0; top: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.CodeMirror-hscrollbar {
|
||||
bottom: 0; left: 0;
|
||||
overflow-y: hidden;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.CodeMirror-scrollbar-filler {
|
||||
right: 0; bottom: 0;
|
||||
}
|
||||
.CodeMirror-gutter-filler {
|
||||
left: 0; bottom: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-gutters {
|
||||
position: absolute; left: 0; top: 0;
|
||||
min-height: 100%;
|
||||
z-index: 3;
|
||||
}
|
||||
.CodeMirror-gutter {
|
||||
white-space: normal;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-bottom: -30px;
|
||||
}
|
||||
.CodeMirror-gutter-wrapper {
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
.CodeMirror-gutter-background {
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
z-index: 4;
|
||||
}
|
||||
.CodeMirror-gutter-elt {
|
||||
position: absolute;
|
||||
cursor: default;
|
||||
z-index: 4;
|
||||
}
|
||||
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
|
||||
.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
|
||||
|
||||
.CodeMirror-lines {
|
||||
cursor: text;
|
||||
min-height: 1px; /* prevents collapsing before first draw */
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-line,
|
||||
.CodeMirror pre.CodeMirror-line-like {
|
||||
/* Reset some styles that the rest of the page might have set */
|
||||
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
|
||||
border-width: 0;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
margin: 0;
|
||||
white-space: pre;
|
||||
word-wrap: normal;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-font-variant-ligatures: contextual;
|
||||
font-variant-ligatures: contextual;
|
||||
}
|
||||
.CodeMirror-wrap pre.CodeMirror-line,
|
||||
.CodeMirror-wrap pre.CodeMirror-line-like {
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.CodeMirror-linebackground {
|
||||
position: absolute;
|
||||
left: 0; right: 0; top: 0; bottom: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-linewidget {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 0.1px; /* Force widget margins to stay inside of the container */
|
||||
}
|
||||
|
||||
.CodeMirror-widget {}
|
||||
|
||||
.CodeMirror-rtl pre { direction: rtl; }
|
||||
|
||||
.CodeMirror-code {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Force content-box sizing for the elements where we expect it */
|
||||
.CodeMirror-scroll,
|
||||
.CodeMirror-sizer,
|
||||
.CodeMirror-gutter,
|
||||
.CodeMirror-gutters,
|
||||
.CodeMirror-linenumber {
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.CodeMirror-measure {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.CodeMirror-cursor {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.CodeMirror-measure pre { position: static; }
|
||||
|
||||
div.CodeMirror-cursors {
|
||||
visibility: hidden;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
div.CodeMirror-dragcursors {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.CodeMirror-focused div.CodeMirror-cursors {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.CodeMirror-selected { background: #d9d9d9; }
|
||||
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
||||
.CodeMirror-crosshair { cursor: crosshair; }
|
||||
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
|
||||
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
|
||||
|
||||
.cm-searching {
|
||||
background-color: #ffa;
|
||||
background-color: rgba(255, 255, 0, .4);
|
||||
}
|
||||
|
||||
/* Used to force a border model for a node */
|
||||
.cm-force-border { padding-right: .1px; }
|
||||
|
||||
@media print {
|
||||
/* Hide the cursor when printing */
|
||||
.CodeMirror div.CodeMirror-cursors {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/* See issue #2901 */
|
||||
.cm-tab-wrap-hack:after { content: ''; }
|
||||
|
||||
/* Help users use markselection to safely style text background */
|
||||
span.CodeMirror-selectedtext { background: none; }
|
9833
static/docdiff/codemirror.js
Normal file
9833
static/docdiff/codemirror.js
Normal file
File diff suppressed because it is too large
Load Diff
49
static/docdiff/diff_match_patch.js
Normal file
49
static/docdiff/diff_match_patch.js
Normal file
@ -0,0 +1,49 @@
|
||||
(function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32}
|
||||
diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a,
|
||||
b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a};
|
||||
diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,b,
|
||||
d):this.diff_bisect_(a,b,d)};
|
||||
diff_match_patch.prototype.diff_lineMode_=function(a,b,c){var d=this.diff_linesToChars_(a,b);a=d.chars1;b=d.chars2;d=d.lineArray;a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push([0,""]);for(var e=d=b=0,f="",g="";b<a.length;){switch(a[b][0]){case 1:e++;g+=a[b][1];break;case -1:d++;f+=a[b][1];break;case 0:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,0,d[e]);b+=d.length}d=e=0;g=f=""}b++}a.pop();return a};
|
||||
diff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=f,h=2*f,j=Array(h),i=Array(h),k=0;k<h;k++)j[k]=-1,i[k]=-1;j[g+1]=0;i[g+1]=0;for(var k=d-e,q=0!=k%2,r=0,t=0,p=0,w=0,v=0;v<f&&!((new Date).getTime()>c);v++){for(var n=-v+r;n<=v-t;n+=2){var l=g+n,m;m=n==-v||n!=v&&j[l-1]<j[l+1]?j[l+1]:j[l-1]+1;for(var s=m-n;m<d&&s<e&&a.charAt(m)==b.charAt(s);)m++,s++;j[l]=m;if(m>d)t+=2;else if(s>e)r+=2;else if(q&&(l=g+k-n,0<=l&&l<h&&-1!=i[l])){var u=d-i[l];if(m>=
|
||||
u)return this.diff_bisectSplit_(a,b,m,s,c)}}for(n=-v+p;n<=v-w;n+=2){l=g+n;u=n==-v||n!=v&&i[l-1]<i[l+1]?i[l+1]:i[l-1]+1;for(m=u-n;u<d&&m<e&&a.charAt(d-u-1)==b.charAt(e-m-1);)u++,m++;i[l]=u;if(u>d)w+=2;else if(m>e)p+=2;else if(!q&&(l=g+k-n,0<=l&&(l<h&&-1!=j[l])&&(m=j[l],s=g+m-l,u=d-u,m>=u)))return this.diff_bisectSplit_(a,b,m,s,c)}}return[[-1,a],[1,b]]};
|
||||
diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)};
|
||||
diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;f<a.length-1;){f=a.indexOf("\n",c);-1==f&&(f=a.length-1);var r=a.substring(c,f+1),c=f+1;(e.hasOwnProperty?e.hasOwnProperty(r):void 0!==e[r])?b+=String.fromCharCode(e[r]):(b+=String.fromCharCode(g),e[r]=g,d[g++]=r)}return b}var d=[],e={};d[0]="";var f=c(a),g=c(b);return{chars1:f,chars2:g,lineArray:d}};
|
||||
diff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var c=0;c<a.length;c++){for(var d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join("")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
|
||||
diff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
|
||||
diff_match_patch.prototype.diff_commonOverlap_=function(a,b){var c=a.length,d=b.length;if(0==c||0==d)return 0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return c;for(var d=0,e=1;;){var f=a.substring(c-e),f=b.indexOf(f);if(-1==f)return d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};
|
||||
diff_match_patch.prototype.diff_halfMatch_=function(a,b){function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g="",h,j,n,l;-1!=(e=b.indexOf(d,e+1));){var m=f.diff_commonPrefix(a.substring(c),b.substring(e)),s=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<s+m&&(g=b.substring(e-s,e)+b.substring(e,e+m),h=a.substring(0,c-s),j=a.substring(c+m),n=b.substring(0,e-s),l=b.substring(e+m))}return 2*g.length>=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null;
|
||||
var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return null;var f=this,g=c(d,e,Math.ceil(d.length/4)),d=c(d,e,Math.ceil(d.length/2)),h;if(!g&&!d)return null;h=d?g?g[4].length>d[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]};
|
||||
diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f<a.length;)0==a[f][0]?(c[d++]=f,g=j,h=i,i=j=0,e=a[f][1]):(1==a[f][0]?j+=a[f][1].length:i+=a[f][1].length,e&&(e.length<=Math.max(g,h)&&e.length<=Math.max(j,i))&&(a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,d--,f=0<d?c[d-1]:-1,i=j=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<a.length;){if(-1==a[f-1][0]&&1==a[f][0]){b=a[f-1][1];c=a[f][1];
|
||||
d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}};
|
||||
diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_);
|
||||
return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c<a.length-1;){if(0==a[c-1][0]&&0==a[c+1][0]){var d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g)var h=e.substring(e.length-g),d=d.substring(0,d.length-g),e=h+e.substring(0,e.length-g),f=h+f;for(var g=d,h=e,j=f,i=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){var d=d+e.charAt(0),e=e.substring(1)+f.charAt(0),f=f.substring(1),k=b(d,e)+b(e,f);k>=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]=
|
||||
h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
|
||||
diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;f<a.length;){if(0==a[f][0])a[f][1].length<this.Diff_EditCost&&(j||i)?(c[d++]=f,g=j,h=i,e=a[f][1]):(d=0,e=null),j=i=!1;else if(-1==a[f][0]?i=!0:j=!0,e&&(g&&h&&j&&i||e.length<this.Diff_EditCost/2&&3==g+h+j+i))a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,e=null,g&&h?(j=i=!0,d=0):(d--,f=0<d?c[d-1]:-1,j=i=!1),b=!0;f++}b&&this.diff_cleanupMerge(a)};
|
||||
diff_match_patch.prototype.diff_cleanupMerge=function(a){a.push([0,""]);for(var b=0,c=0,d=0,e="",f="",g;b<a.length;)switch(a[b][0]){case 1:d++;f+=a[b][1];b++;break;case -1:c++;e+=a[b][1];b++;break;case 0:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&0==a[b-c-d-1][0]?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,[0,f.substring(0,g)]),b++),f=f.substring(g),e=e.substring(g)),g=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-g)+a[b][1],f=f.substring(0,f.length-
|
||||
g),e=e.substring(0,e.length-g))),0===c?a.splice(b-d,c+d,[1,f]):0===d?a.splice(b-c,c+d,[-1,e]):a.splice(b-c-d,c+d,[-1,e],[1,f]),b=b-c-d+(c?1:0)+(d?1:0)+1):0!==b&&0==a[b-1][0]?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=""}""===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)0==a[b-1][0]&&0==a[b+1][0]&&(a[b][1].substring(a[b][1].length-a[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,
|
||||
a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};diff_match_patch.prototype.diff_xIndex=function(a,b){var c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){1!==a[g][0]&&(c+=a[g][1].length);-1!==a[g][0]&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)};
|
||||
diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=/</g,e=/>/g,f=/\n/g,g=0;g<a.length;g++){var h=a[g][0],j=a[g][1],j=j.replace(c,"&").replace(d,"<").replace(e,">").replace(f,"¶<br>");switch(h){case 1:b[g]='<ins style="background:#e6ffe6;">'+j+"</ins>";break;case -1:b[g]='<del style="background:#ffe6e6;">'+j+"</del>";break;case 0:b[g]="<span>"+j+"</span>"}}return b.join("")};
|
||||
diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;c<a.length;c++)1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_text2=function(a){for(var b=[],c=0;c<a.length;c++)-1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_levenshtein=function(a){for(var b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][0],g=a[e][1];switch(f){case 1:c+=g.length;break;case -1:d+=g.length;break;case 0:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};
|
||||
diff_match_patch.prototype.diff_toDelta=function(a){for(var b=[],c=0;c<a.length;c++)switch(a[c][0]){case 1:b[c]="+"+encodeURI(a[c][1]);break;case -1:b[c]="-"+a[c][1].length;break;case 0:b[c]="="+a[c][1].length}return b.join("\t").replace(/%20/g," ")};
|
||||
diff_match_patch.prototype.diff_fromDelta=function(a,b){for(var c=[],d=0,e=0,f=b.split(/\t/g),g=0;g<f.length;g++){var h=f[g].substring(1);switch(f[g].charAt(0)){case "+":try{c[d++]=[1,decodeURI(h)]}catch(j){throw Error("Illegal escape in diff_fromDelta: "+h);}break;case "-":case "=":var i=parseInt(h,10);if(isNaN(i)||0>i)throw Error("Invalid number in diff_fromDelta: "+h);h=a.substring(e,e+=i);"="==f[g].charAt(0)?c[d++]=[0,h]:c[d++]=[-1,h];break;default:if(f[g])throw Error("Invalid diff operation in diff_fromDelta: "+
|
||||
f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+") does not equal source text length ("+a.length+").");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error("Null input. (match_main)");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
|
||||
diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return!f.Match_Distance?g?1:e:e+g/f.Match_Distance}if(b.length>this.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<<b.length-1,h=-1,i,k,q=b.length+a.length,r,t=0;t<b.length;t++){i=0;for(k=q;i<k;)d(t,c+
|
||||
k)<=g?i=k:q=k,k=Math.floor((q-i)/2+i);q=k;i=Math.max(1,c-k+1);var p=Math.min(c+k,a.length)+b.length;k=Array(p+2);for(k[p+1]=(1<<t)-1;p>=i;p--){var w=e[a.charAt(p-1)];k[p]=0===t?(k[p+1]<<1|1)&w:(k[p+1]<<1|1)&w|((r[p+1]|r[p])<<1|1)|r[p+1];if(k[p]&j&&(w=d(t,p-1),w<=g))if(g=w,h=p-1,h>c)i=Math.max(1,2*c-h);else break}if(d(t+1,c)>g)break;r=k}return h};
|
||||
diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return b};
|
||||
diff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){for(var c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift([0,c]);(d=b.substring(a.start2+a.length1,a.start2+a.length1+d))&&a.diffs.push([0,d]);a.start1-=c.length;a.start2-=c.length;a.length1+=
|
||||
c.length+d.length;a.length2+=c.length+d.length}};
|
||||
diff_match_patch.prototype.patch_make=function(a,b,c){var d;if("string"==typeof a&&"string"==typeof b&&"undefined"==typeof c)d=a,b=this.diff_main(d,b,!0),2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b));else if(a&&"object"==typeof a&&"undefined"==typeof b&&"undefined"==typeof c)b=a,d=this.diff_text1(b);else if("string"==typeof a&&b&&"object"==typeof b&&"undefined"==typeof c)d=a;else if("string"==typeof a&&"string"==typeof b&&c&&"object"==typeof c)d=a,b=c;else throw Error("Unknown call format to patch_make.");
|
||||
if(0===b.length)return[];c=[];a=new diff_match_patch.patch_obj;for(var e=0,f=0,g=0,h=d,j=0;j<b.length;j++){var i=b[j][0],k=b[j][1];!e&&0!==i&&(a.start1=f,a.start2=g);switch(i){case 1:a.diffs[e++]=b[j];a.length2+=k.length;d=d.substring(0,g)+k+d.substring(g);break;case -1:a.length1+=k.length;a.diffs[e++]=b[j];d=d.substring(0,g)+d.substring(g+k.length);break;case 0:k.length<=2*this.Patch_Margin&&e&&b.length!=j+1?(a.diffs[e++]=b[j],a.length1+=k.length,a.length2+=k.length):k.length>=2*this.Patch_Margin&&
|
||||
e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c],e=new diff_match_patch.patch_obj;e.diffs=[];for(var f=0;f<d.diffs.length;f++)e.diffs[f]=d.diffs[f].slice();e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return b};
|
||||
diff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];a=this.patch_deepCopy(a);var c=this.patch_addPadding(a);b=c+b+c;this.patch_splitMax(a);for(var d=0,e=[],f=0;f<a.length;f++){var g=a[f].start2+d,h=this.diff_text1(a[f].diffs),j,i=-1;if(h.length>this.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g);
|
||||
if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;i<a[f].diffs.length;i++){var q=a[f].diffs[i];0!==q[0]&&(k=this.diff_xIndex(g,h));1===q[0]?b=b.substring(0,
|
||||
j+k)+q[1]+b.substring(j+k):-1===q[0]&&(b=b.substring(0,j+k)+b.substring(j+this.diff_xIndex(g,h+q[1].length)));-1!==q[0]&&(h+=q[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};
|
||||
diff_match_patch.prototype.patch_addPadding=function(a){for(var b=this.Patch_Margin,c="",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;var d=a[0],e=d.diffs;if(0==e.length||0!=e[0][0])e.unshift([0,c]),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else if(b>e[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0,
|
||||
c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};
|
||||
diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var d=a[c];a.splice(c--,1);for(var e=d.start1,f=d.start2,g="";0!==d.diffs.length;){var h=new diff_match_patch.patch_obj,j=!0;h.start1=e-g.length;h.start2=f-g.length;""!==g&&(h.length1=h.length2=g.length,h.diffs.push([0,g]));for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){var g=d.diffs[0][0],i=d.diffs[0][1];1===g?(h.length2+=i.length,f+=i.length,h.diffs.push(d.diffs.shift()),
|
||||
j=!1):-1===g&&1==h.diffs.length&&0==h.diffs[0][0]&&i.length>2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&&
|
||||
(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join("")};
|
||||
diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;a=a.split("\n");for(var c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error("Invalid patch string: "+a[c]);var f=new diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);""===e[2]?(f.start1--,f.length1=1):"0"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);""===e[4]?(f.start2--,f.length2=1):"0"==e[4]?f.length2=0:(f.start2--,f.length2=
|
||||
parseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var g=decodeURI(a[c].substring(1))}catch(h){throw Error("Illegal escape in patch_fromText: "+g);}if("-"==e)f.diffs.push([-1,g]);else if("+"==e)f.diffs.push([1,g]);else if(" "==e)f.diffs.push([0,g]);else if("@"==e)break;else if(""!==e)throw Error('Invalid patch mode "'+e+'" in: '+g);c++}}return b};diff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};
|
||||
diff_match_patch.patch_obj.prototype.toString=function(){var a,b;a=0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1;b=0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2;a=["@@ -"+a+" +"+b+" @@\n"];var c;for(b=0;b<this.diffs.length;b++){switch(this.diffs[b][0]){case 1:c="+";break;case -1:c="-";break;case 0:c=" "}a[b+1]=c+encodeURI(this.diffs[b][1])+"\n"}return a.join("").replace(/%20/g," ")};
|
||||
this.diff_match_patch=diff_match_patch;this.DIFF_DELETE=-1;this.DIFF_INSERT=1;this.DIFF_EQUAL=0;})()
|
883
static/docdiff/markdown.js
Normal file
883
static/docdiff/markdown.js
Normal file
@ -0,0 +1,883 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
|
||||
|
||||
var htmlMode = CodeMirror.getMode(cmCfg, "text/html");
|
||||
var htmlModeMissing = htmlMode.name == "null"
|
||||
|
||||
function getMode(name) {
|
||||
if (CodeMirror.findModeByName) {
|
||||
var found = CodeMirror.findModeByName(name);
|
||||
if (found) name = found.mime || found.mimes[0];
|
||||
}
|
||||
var mode = CodeMirror.getMode(cmCfg, name);
|
||||
return mode.name == "null" ? null : mode;
|
||||
}
|
||||
|
||||
// Should characters that affect highlighting be highlighted separate?
|
||||
// Does not include characters that will be output (such as `1.` and `-` for lists)
|
||||
if (modeCfg.highlightFormatting === undefined)
|
||||
modeCfg.highlightFormatting = false;
|
||||
|
||||
// Maximum number of nested blockquotes. Set to 0 for infinite nesting.
|
||||
// Excess `>` will emit `error` token.
|
||||
if (modeCfg.maxBlockquoteDepth === undefined)
|
||||
modeCfg.maxBlockquoteDepth = 0;
|
||||
|
||||
// Turn on task lists? ("- [ ] " and "- [x] ")
|
||||
if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
|
||||
|
||||
// Turn on strikethrough syntax
|
||||
if (modeCfg.strikethrough === undefined)
|
||||
modeCfg.strikethrough = false;
|
||||
|
||||
if (modeCfg.emoji === undefined)
|
||||
modeCfg.emoji = false;
|
||||
|
||||
if (modeCfg.fencedCodeBlockHighlighting === undefined)
|
||||
modeCfg.fencedCodeBlockHighlighting = true;
|
||||
|
||||
if (modeCfg.xml === undefined)
|
||||
modeCfg.xml = true;
|
||||
|
||||
// Allow token types to be overridden by user-provided token types.
|
||||
if (modeCfg.tokenTypeOverrides === undefined)
|
||||
modeCfg.tokenTypeOverrides = {};
|
||||
|
||||
var tokenTypes = {
|
||||
header: "header",
|
||||
code: "comment",
|
||||
quote: "quote",
|
||||
list1: "variable-2",
|
||||
list2: "variable-3",
|
||||
list3: "keyword",
|
||||
hr: "hr",
|
||||
image: "image",
|
||||
imageAltText: "image-alt-text",
|
||||
imageMarker: "image-marker",
|
||||
formatting: "formatting",
|
||||
linkInline: "link",
|
||||
linkEmail: "link",
|
||||
linkText: "link",
|
||||
linkHref: "string",
|
||||
em: "em",
|
||||
strong: "strong",
|
||||
strikethrough: "strikethrough",
|
||||
emoji: "builtin"
|
||||
};
|
||||
|
||||
for (var tokenType in tokenTypes) {
|
||||
if (tokenTypes.hasOwnProperty(tokenType) && modeCfg.tokenTypeOverrides[tokenType]) {
|
||||
tokenTypes[tokenType] = modeCfg.tokenTypeOverrides[tokenType];
|
||||
}
|
||||
}
|
||||
|
||||
var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
|
||||
, listRE = /^(?:[*\-+]|^[0-9]+([.)]))\s+/
|
||||
, taskListRE = /^\[(x| )\](?=\s)/i // Must follow listRE
|
||||
, atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
|
||||
, setextHeaderRE = /^ {0,3}(?:\={1,}|-{2,})\s*$/
|
||||
, textRE = /^[^#!\[\]*_\\<>` "'(~:]+/
|
||||
, fencedCodeRE = /^(~~~+|```+)[ \t]*([\w+#-]*)[^\n`]*$/
|
||||
, linkDefRE = /^\s*\[[^\]]+?\]:.*$/ // naive link-definition
|
||||
, punctuation = /[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]/
|
||||
, expandedTab = " " // CommonMark specifies tab as 4 spaces
|
||||
|
||||
function switchInline(stream, state, f) {
|
||||
state.f = state.inline = f;
|
||||
return f(stream, state);
|
||||
}
|
||||
|
||||
function switchBlock(stream, state, f) {
|
||||
state.f = state.block = f;
|
||||
return f(stream, state);
|
||||
}
|
||||
|
||||
function lineIsEmpty(line) {
|
||||
return !line || !/\S/.test(line.string)
|
||||
}
|
||||
|
||||
// Blocks
|
||||
|
||||
function blankLine(state) {
|
||||
// Reset linkTitle state
|
||||
state.linkTitle = false;
|
||||
state.linkHref = false;
|
||||
state.linkText = false;
|
||||
// Reset EM state
|
||||
state.em = false;
|
||||
// Reset STRONG state
|
||||
state.strong = false;
|
||||
// Reset strikethrough state
|
||||
state.strikethrough = false;
|
||||
// Reset state.quote
|
||||
state.quote = 0;
|
||||
// Reset state.indentedCode
|
||||
state.indentedCode = false;
|
||||
if (state.f == htmlBlock) {
|
||||
var exit = htmlModeMissing
|
||||
if (!exit) {
|
||||
var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
|
||||
exit = inner.mode.name == "xml" && inner.state.tagStart === null &&
|
||||
(!inner.state.context && inner.state.tokenize.isInText)
|
||||
}
|
||||
if (exit) {
|
||||
state.f = inlineNormal;
|
||||
state.block = blockNormal;
|
||||
state.htmlState = null;
|
||||
}
|
||||
}
|
||||
// Reset state.trailingSpace
|
||||
state.trailingSpace = 0;
|
||||
state.trailingSpaceNewLine = false;
|
||||
// Mark this line as blank
|
||||
state.prevLine = state.thisLine
|
||||
state.thisLine = {stream: null}
|
||||
return null;
|
||||
}
|
||||
|
||||
function blockNormal(stream, state) {
|
||||
var firstTokenOnLine = stream.column() === state.indentation;
|
||||
var prevLineLineIsEmpty = lineIsEmpty(state.prevLine.stream);
|
||||
var prevLineIsIndentedCode = state.indentedCode;
|
||||
var prevLineIsHr = state.prevLine.hr;
|
||||
var prevLineIsList = state.list !== false;
|
||||
var maxNonCodeIndentation = (state.listStack[state.listStack.length - 1] || 0) + 3;
|
||||
|
||||
state.indentedCode = false;
|
||||
|
||||
var lineIndentation = state.indentation;
|
||||
// compute once per line (on first token)
|
||||
if (state.indentationDiff === null) {
|
||||
state.indentationDiff = state.indentation;
|
||||
if (prevLineIsList) {
|
||||
state.list = null;
|
||||
// While this list item's marker's indentation is less than the deepest
|
||||
// list item's content's indentation,pop the deepest list item
|
||||
// indentation off the stack, and update block indentation state
|
||||
while (lineIndentation < state.listStack[state.listStack.length - 1]) {
|
||||
state.listStack.pop();
|
||||
if (state.listStack.length) {
|
||||
state.indentation = state.listStack[state.listStack.length - 1];
|
||||
// less than the first list's indent -> the line is no longer a list
|
||||
} else {
|
||||
state.list = false;
|
||||
}
|
||||
}
|
||||
if (state.list !== false) {
|
||||
state.indentationDiff = lineIndentation - state.listStack[state.listStack.length - 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not comprehensive (currently only for setext detection purposes)
|
||||
var allowsInlineContinuation = (
|
||||
!prevLineLineIsEmpty && !prevLineIsHr && !state.prevLine.header &&
|
||||
(!prevLineIsList || !prevLineIsIndentedCode) &&
|
||||
!state.prevLine.fencedCodeEnd
|
||||
);
|
||||
|
||||
var isHr = (state.list === false || prevLineIsHr || prevLineLineIsEmpty) &&
|
||||
state.indentation <= maxNonCodeIndentation && stream.match(hrRE);
|
||||
|
||||
var match = null;
|
||||
if (state.indentationDiff >= 4 && (prevLineIsIndentedCode || state.prevLine.fencedCodeEnd ||
|
||||
state.prevLine.header || prevLineLineIsEmpty)) {
|
||||
stream.skipToEnd();
|
||||
state.indentedCode = true;
|
||||
return tokenTypes.code;
|
||||
} else if (stream.eatSpace()) {
|
||||
return null;
|
||||
} else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
|
||||
state.quote = 0;
|
||||
state.header = match[1].length;
|
||||
state.thisLine.header = true;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "header";
|
||||
state.f = state.inline;
|
||||
return getType(state);
|
||||
} else if (state.indentation <= maxNonCodeIndentation && stream.eat('>')) {
|
||||
state.quote = firstTokenOnLine ? 1 : state.quote + 1;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "quote";
|
||||
stream.eatSpace();
|
||||
return getType(state);
|
||||
} else if (!isHr && !state.setext && firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(listRE))) {
|
||||
var listType = match[1] ? "ol" : "ul";
|
||||
|
||||
state.indentation = lineIndentation + stream.current().length;
|
||||
state.list = true;
|
||||
state.quote = 0;
|
||||
|
||||
// Add this list item's content's indentation to the stack
|
||||
state.listStack.push(state.indentation);
|
||||
// Reset inline styles which shouldn't propagate aross list items
|
||||
state.em = false;
|
||||
state.strong = false;
|
||||
state.code = false;
|
||||
state.strikethrough = false;
|
||||
|
||||
if (modeCfg.taskLists && stream.match(taskListRE, false)) {
|
||||
state.taskList = true;
|
||||
}
|
||||
state.f = state.inline;
|
||||
if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
|
||||
return getType(state);
|
||||
} else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(fencedCodeRE, true))) {
|
||||
state.quote = 0;
|
||||
state.fencedEndRE = new RegExp(match[1] + "+ *$");
|
||||
// try switching mode
|
||||
state.localMode = modeCfg.fencedCodeBlockHighlighting && getMode(match[2]);
|
||||
if (state.localMode) state.localState = CodeMirror.startState(state.localMode);
|
||||
state.f = state.block = local;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "code-block";
|
||||
state.code = -1
|
||||
return getType(state);
|
||||
// SETEXT has lowest block-scope precedence after HR, so check it after
|
||||
// the others (code, blockquote, list...)
|
||||
} else if (
|
||||
// if setext set, indicates line after ---/===
|
||||
state.setext || (
|
||||
// line before ---/===
|
||||
(!allowsInlineContinuation || !prevLineIsList) && !state.quote && state.list === false &&
|
||||
!state.code && !isHr && !linkDefRE.test(stream.string) &&
|
||||
(match = stream.lookAhead(1)) && (match = match.match(setextHeaderRE))
|
||||
)
|
||||
) {
|
||||
if ( !state.setext ) {
|
||||
state.header = match[0].charAt(0) == '=' ? 1 : 2;
|
||||
state.setext = state.header;
|
||||
} else {
|
||||
state.header = state.setext;
|
||||
// has no effect on type so we can reset it now
|
||||
state.setext = 0;
|
||||
stream.skipToEnd();
|
||||
if (modeCfg.highlightFormatting) state.formatting = "header";
|
||||
}
|
||||
state.thisLine.header = true;
|
||||
state.f = state.inline;
|
||||
return getType(state);
|
||||
} else if (isHr) {
|
||||
stream.skipToEnd();
|
||||
state.hr = true;
|
||||
state.thisLine.hr = true;
|
||||
return tokenTypes.hr;
|
||||
} else if (stream.peek() === '[') {
|
||||
return switchInline(stream, state, footnoteLink);
|
||||
}
|
||||
|
||||
return switchInline(stream, state, state.inline);
|
||||
}
|
||||
|
||||
function htmlBlock(stream, state) {
|
||||
var style = htmlMode.token(stream, state.htmlState);
|
||||
if (!htmlModeMissing) {
|
||||
var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
|
||||
if ((inner.mode.name == "xml" && inner.state.tagStart === null &&
|
||||
(!inner.state.context && inner.state.tokenize.isInText)) ||
|
||||
(state.md_inside && stream.current().indexOf(">") > -1)) {
|
||||
state.f = inlineNormal;
|
||||
state.block = blockNormal;
|
||||
state.htmlState = null;
|
||||
}
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
function local(stream, state) {
|
||||
var currListInd = state.listStack[state.listStack.length - 1] || 0;
|
||||
var hasExitedList = state.indentation < currListInd;
|
||||
var maxFencedEndInd = currListInd + 3;
|
||||
if (state.fencedEndRE && state.indentation <= maxFencedEndInd && (hasExitedList || stream.match(state.fencedEndRE))) {
|
||||
if (modeCfg.highlightFormatting) state.formatting = "code-block";
|
||||
var returnType;
|
||||
if (!hasExitedList) returnType = getType(state)
|
||||
state.localMode = state.localState = null;
|
||||
state.block = blockNormal;
|
||||
state.f = inlineNormal;
|
||||
state.fencedEndRE = null;
|
||||
state.code = 0
|
||||
state.thisLine.fencedCodeEnd = true;
|
||||
if (hasExitedList) return switchBlock(stream, state, state.block);
|
||||
return returnType;
|
||||
} else if (state.localMode) {
|
||||
return state.localMode.token(stream, state.localState);
|
||||
} else {
|
||||
stream.skipToEnd();
|
||||
return tokenTypes.code;
|
||||
}
|
||||
}
|
||||
|
||||
// Inline
|
||||
function getType(state) {
|
||||
var styles = [];
|
||||
|
||||
if (state.formatting) {
|
||||
styles.push(tokenTypes.formatting);
|
||||
|
||||
if (typeof state.formatting === "string") state.formatting = [state.formatting];
|
||||
|
||||
for (var i = 0; i < state.formatting.length; i++) {
|
||||
styles.push(tokenTypes.formatting + "-" + state.formatting[i]);
|
||||
|
||||
if (state.formatting[i] === "header") {
|
||||
styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.header);
|
||||
}
|
||||
|
||||
// Add `formatting-quote` and `formatting-quote-#` for blockquotes
|
||||
// Add `error` instead if the maximum blockquote nesting depth is passed
|
||||
if (state.formatting[i] === "quote") {
|
||||
if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
|
||||
styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.quote);
|
||||
} else {
|
||||
styles.push("error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.taskOpen) {
|
||||
styles.push("meta");
|
||||
return styles.length ? styles.join(' ') : null;
|
||||
}
|
||||
if (state.taskClosed) {
|
||||
styles.push("property");
|
||||
return styles.length ? styles.join(' ') : null;
|
||||
}
|
||||
|
||||
if (state.linkHref) {
|
||||
styles.push(tokenTypes.linkHref, "url");
|
||||
} else { // Only apply inline styles to non-url text
|
||||
if (state.strong) { styles.push(tokenTypes.strong); }
|
||||
if (state.em) { styles.push(tokenTypes.em); }
|
||||
if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }
|
||||
if (state.emoji) { styles.push(tokenTypes.emoji); }
|
||||
if (state.linkText) { styles.push(tokenTypes.linkText); }
|
||||
if (state.code) { styles.push(tokenTypes.code); }
|
||||
if (state.image) { styles.push(tokenTypes.image); }
|
||||
if (state.imageAltText) { styles.push(tokenTypes.imageAltText, "link"); }
|
||||
if (state.imageMarker) { styles.push(tokenTypes.imageMarker); }
|
||||
}
|
||||
|
||||
if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); }
|
||||
|
||||
if (state.quote) {
|
||||
styles.push(tokenTypes.quote);
|
||||
|
||||
// Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
|
||||
if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
|
||||
styles.push(tokenTypes.quote + "-" + state.quote);
|
||||
} else {
|
||||
styles.push(tokenTypes.quote + "-" + modeCfg.maxBlockquoteDepth);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.list !== false) {
|
||||
var listMod = (state.listStack.length - 1) % 3;
|
||||
if (!listMod) {
|
||||
styles.push(tokenTypes.list1);
|
||||
} else if (listMod === 1) {
|
||||
styles.push(tokenTypes.list2);
|
||||
} else {
|
||||
styles.push(tokenTypes.list3);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.trailingSpaceNewLine) {
|
||||
styles.push("trailing-space-new-line");
|
||||
} else if (state.trailingSpace) {
|
||||
styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
|
||||
}
|
||||
|
||||
return styles.length ? styles.join(' ') : null;
|
||||
}
|
||||
|
||||
function handleText(stream, state) {
|
||||
if (stream.match(textRE, true)) {
|
||||
return getType(state);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function inlineNormal(stream, state) {
|
||||
var style = state.text(stream, state);
|
||||
if (typeof style !== 'undefined')
|
||||
return style;
|
||||
|
||||
if (state.list) { // List marker (*, +, -, 1., etc)
|
||||
state.list = null;
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
if (state.taskList) {
|
||||
var taskOpen = stream.match(taskListRE, true)[1] === " ";
|
||||
if (taskOpen) state.taskOpen = true;
|
||||
else state.taskClosed = true;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "task";
|
||||
state.taskList = false;
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
state.taskOpen = false;
|
||||
state.taskClosed = false;
|
||||
|
||||
if (state.header && stream.match(/^#+$/, true)) {
|
||||
if (modeCfg.highlightFormatting) state.formatting = "header";
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
var ch = stream.next();
|
||||
|
||||
// Matches link titles present on next line
|
||||
if (state.linkTitle) {
|
||||
state.linkTitle = false;
|
||||
var matchCh = ch;
|
||||
if (ch === '(') {
|
||||
matchCh = ')';
|
||||
}
|
||||
matchCh = (matchCh+'').replace(/([.?*+^\[\]\\(){}|-])/g, "\\$1");
|
||||
var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
|
||||
if (stream.match(new RegExp(regex), true)) {
|
||||
return tokenTypes.linkHref;
|
||||
}
|
||||
}
|
||||
|
||||
// If this block is changed, it may need to be updated in GFM mode
|
||||
if (ch === '`') {
|
||||
var previousFormatting = state.formatting;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "code";
|
||||
stream.eatWhile('`');
|
||||
var count = stream.current().length
|
||||
if (state.code == 0 && (!state.quote || count == 1)) {
|
||||
state.code = count
|
||||
return getType(state)
|
||||
} else if (count == state.code) { // Must be exact
|
||||
var t = getType(state)
|
||||
state.code = 0
|
||||
return t
|
||||
} else {
|
||||
state.formatting = previousFormatting
|
||||
return getType(state)
|
||||
}
|
||||
} else if (state.code) {
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
if (ch === '\\') {
|
||||
stream.next();
|
||||
if (modeCfg.highlightFormatting) {
|
||||
var type = getType(state);
|
||||
var formattingEscape = tokenTypes.formatting + "-escape";
|
||||
return type ? type + " " + formattingEscape : formattingEscape;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
|
||||
state.imageMarker = true;
|
||||
state.image = true;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "image";
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
if (ch === '[' && state.imageMarker && stream.match(/[^\]]*\](\(.*?\)| ?\[.*?\])/, false)) {
|
||||
state.imageMarker = false;
|
||||
state.imageAltText = true
|
||||
if (modeCfg.highlightFormatting) state.formatting = "image";
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
if (ch === ']' && state.imageAltText) {
|
||||
if (modeCfg.highlightFormatting) state.formatting = "image";
|
||||
var type = getType(state);
|
||||
state.imageAltText = false;
|
||||
state.image = false;
|
||||
state.inline = state.f = linkHref;
|
||||
return type;
|
||||
}
|
||||
|
||||
if (ch === '[' && !state.image) {
|
||||
if (state.linkText && stream.match(/^.*?\]/)) return getType(state)
|
||||
state.linkText = true;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
if (ch === ']' && state.linkText) {
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
var type = getType(state);
|
||||
state.linkText = false;
|
||||
state.inline = state.f = stream.match(/\(.*?\)| ?\[.*?\]/, false) ? linkHref : inlineNormal
|
||||
return type;
|
||||
}
|
||||
|
||||
if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
|
||||
state.f = state.inline = linkInline;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
var type = getType(state);
|
||||
if (type){
|
||||
type += " ";
|
||||
} else {
|
||||
type = "";
|
||||
}
|
||||
return type + tokenTypes.linkInline;
|
||||
}
|
||||
|
||||
if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
|
||||
state.f = state.inline = linkInline;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
var type = getType(state);
|
||||
if (type){
|
||||
type += " ";
|
||||
} else {
|
||||
type = "";
|
||||
}
|
||||
return type + tokenTypes.linkEmail;
|
||||
}
|
||||
|
||||
if (modeCfg.xml && ch === '<' && stream.match(/^(!--|\?|!\[CDATA\[|[a-z][a-z0-9-]*(?:\s+[a-z_:.\-]+(?:\s*=\s*[^>]+)?)*\s*(?:>|$))/i, false)) {
|
||||
var end = stream.string.indexOf(">", stream.pos);
|
||||
if (end != -1) {
|
||||
var atts = stream.string.substring(stream.start, end);
|
||||
if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) state.md_inside = true;
|
||||
}
|
||||
stream.backUp(1);
|
||||
state.htmlState = CodeMirror.startState(htmlMode);
|
||||
return switchBlock(stream, state, htmlBlock);
|
||||
}
|
||||
|
||||
if (modeCfg.xml && ch === '<' && stream.match(/^\/\w*?>/)) {
|
||||
state.md_inside = false;
|
||||
return "tag";
|
||||
} else if (ch === "*" || ch === "_") {
|
||||
var len = 1, before = stream.pos == 1 ? " " : stream.string.charAt(stream.pos - 2)
|
||||
while (len < 3 && stream.eat(ch)) len++
|
||||
var after = stream.peek() || " "
|
||||
// See http://spec.commonmark.org/0.27/#emphasis-and-strong-emphasis
|
||||
var leftFlanking = !/\s/.test(after) && (!punctuation.test(after) || /\s/.test(before) || punctuation.test(before))
|
||||
var rightFlanking = !/\s/.test(before) && (!punctuation.test(before) || /\s/.test(after) || punctuation.test(after))
|
||||
var setEm = null, setStrong = null
|
||||
if (len % 2) { // Em
|
||||
if (!state.em && leftFlanking && (ch === "*" || !rightFlanking || punctuation.test(before)))
|
||||
setEm = true
|
||||
else if (state.em == ch && rightFlanking && (ch === "*" || !leftFlanking || punctuation.test(after)))
|
||||
setEm = false
|
||||
}
|
||||
if (len > 1) { // Strong
|
||||
if (!state.strong && leftFlanking && (ch === "*" || !rightFlanking || punctuation.test(before)))
|
||||
setStrong = true
|
||||
else if (state.strong == ch && rightFlanking && (ch === "*" || !leftFlanking || punctuation.test(after)))
|
||||
setStrong = false
|
||||
}
|
||||
if (setStrong != null || setEm != null) {
|
||||
if (modeCfg.highlightFormatting) state.formatting = setEm == null ? "strong" : setStrong == null ? "em" : "strong em"
|
||||
if (setEm === true) state.em = ch
|
||||
if (setStrong === true) state.strong = ch
|
||||
var t = getType(state)
|
||||
if (setEm === false) state.em = false
|
||||
if (setStrong === false) state.strong = false
|
||||
return t
|
||||
}
|
||||
} else if (ch === ' ') {
|
||||
if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
|
||||
if (stream.peek() === ' ') { // Surrounded by spaces, ignore
|
||||
return getType(state);
|
||||
} else { // Not surrounded by spaces, back up pointer
|
||||
stream.backUp(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modeCfg.strikethrough) {
|
||||
if (ch === '~' && stream.eatWhile(ch)) {
|
||||
if (state.strikethrough) {// Remove strikethrough
|
||||
if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
|
||||
var t = getType(state);
|
||||
state.strikethrough = false;
|
||||
return t;
|
||||
} else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
|
||||
state.strikethrough = true;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
|
||||
return getType(state);
|
||||
}
|
||||
} else if (ch === ' ') {
|
||||
if (stream.match(/^~~/, true)) { // Probably surrounded by space
|
||||
if (stream.peek() === ' ') { // Surrounded by spaces, ignore
|
||||
return getType(state);
|
||||
} else { // Not surrounded by spaces, back up pointer
|
||||
stream.backUp(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modeCfg.emoji && ch === ":" && stream.match(/^(?:[a-z_\d+][a-z_\d+-]*|\-[a-z_\d+][a-z_\d+-]*):/)) {
|
||||
state.emoji = true;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "emoji";
|
||||
var retType = getType(state);
|
||||
state.emoji = false;
|
||||
return retType;
|
||||
}
|
||||
|
||||
if (ch === ' ') {
|
||||
if (stream.match(/^ +$/, false)) {
|
||||
state.trailingSpace++;
|
||||
} else if (state.trailingSpace) {
|
||||
state.trailingSpaceNewLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
return getType(state);
|
||||
}
|
||||
|
||||
function linkInline(stream, state) {
|
||||
var ch = stream.next();
|
||||
|
||||
if (ch === ">") {
|
||||
state.f = state.inline = inlineNormal;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
var type = getType(state);
|
||||
if (type){
|
||||
type += " ";
|
||||
} else {
|
||||
type = "";
|
||||
}
|
||||
return type + tokenTypes.linkInline;
|
||||
}
|
||||
|
||||
stream.match(/^[^>]+/, true);
|
||||
|
||||
return tokenTypes.linkInline;
|
||||
}
|
||||
|
||||
function linkHref(stream, state) {
|
||||
// Check if space, and return NULL if so (to avoid marking the space)
|
||||
if(stream.eatSpace()){
|
||||
return null;
|
||||
}
|
||||
var ch = stream.next();
|
||||
if (ch === '(' || ch === '[') {
|
||||
state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link-string";
|
||||
state.linkHref = true;
|
||||
return getType(state);
|
||||
}
|
||||
return 'error';
|
||||
}
|
||||
|
||||
var linkRE = {
|
||||
")": /^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/,
|
||||
"]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\]]|\\.)*\])*?(?=\])/
|
||||
}
|
||||
|
||||
function getLinkHrefInside(endChar) {
|
||||
return function(stream, state) {
|
||||
var ch = stream.next();
|
||||
|
||||
if (ch === endChar) {
|
||||
state.f = state.inline = inlineNormal;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link-string";
|
||||
var returnState = getType(state);
|
||||
state.linkHref = false;
|
||||
return returnState;
|
||||
}
|
||||
|
||||
stream.match(linkRE[endChar])
|
||||
state.linkHref = true;
|
||||
return getType(state);
|
||||
};
|
||||
}
|
||||
|
||||
function footnoteLink(stream, state) {
|
||||
if (stream.match(/^([^\]\\]|\\.)*\]:/, false)) {
|
||||
state.f = footnoteLinkInside;
|
||||
stream.next(); // Consume [
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
state.linkText = true;
|
||||
return getType(state);
|
||||
}
|
||||
return switchInline(stream, state, inlineNormal);
|
||||
}
|
||||
|
||||
function footnoteLinkInside(stream, state) {
|
||||
if (stream.match(/^\]:/, true)) {
|
||||
state.f = state.inline = footnoteUrl;
|
||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
||||
var returnType = getType(state);
|
||||
state.linkText = false;
|
||||
return returnType;
|
||||
}
|
||||
|
||||
stream.match(/^([^\]\\]|\\.)+/, true);
|
||||
|
||||
return tokenTypes.linkText;
|
||||
}
|
||||
|
||||
function footnoteUrl(stream, state) {
|
||||
// Check if space, and return NULL if so (to avoid marking the space)
|
||||
if(stream.eatSpace()){
|
||||
return null;
|
||||
}
|
||||
// Match URL
|
||||
stream.match(/^[^\s]+/, true);
|
||||
// Check for link title
|
||||
if (stream.peek() === undefined) { // End of line, set flag to check next line
|
||||
state.linkTitle = true;
|
||||
} else { // More content on line, check if link title
|
||||
stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
|
||||
}
|
||||
state.f = state.inline = inlineNormal;
|
||||
return tokenTypes.linkHref + " url";
|
||||
}
|
||||
|
||||
var mode = {
|
||||
startState: function() {
|
||||
return {
|
||||
f: blockNormal,
|
||||
|
||||
prevLine: {stream: null},
|
||||
thisLine: {stream: null},
|
||||
|
||||
block: blockNormal,
|
||||
htmlState: null,
|
||||
indentation: 0,
|
||||
|
||||
inline: inlineNormal,
|
||||
text: handleText,
|
||||
|
||||
formatting: false,
|
||||
linkText: false,
|
||||
linkHref: false,
|
||||
linkTitle: false,
|
||||
code: 0,
|
||||
em: false,
|
||||
strong: false,
|
||||
header: 0,
|
||||
setext: 0,
|
||||
hr: false,
|
||||
taskList: false,
|
||||
list: false,
|
||||
listStack: [],
|
||||
quote: 0,
|
||||
trailingSpace: 0,
|
||||
trailingSpaceNewLine: false,
|
||||
strikethrough: false,
|
||||
emoji: false,
|
||||
fencedEndRE: null
|
||||
};
|
||||
},
|
||||
|
||||
copyState: function(s) {
|
||||
return {
|
||||
f: s.f,
|
||||
|
||||
prevLine: s.prevLine,
|
||||
thisLine: s.thisLine,
|
||||
|
||||
block: s.block,
|
||||
htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
|
||||
indentation: s.indentation,
|
||||
|
||||
localMode: s.localMode,
|
||||
localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
|
||||
|
||||
inline: s.inline,
|
||||
text: s.text,
|
||||
formatting: false,
|
||||
linkText: s.linkText,
|
||||
linkTitle: s.linkTitle,
|
||||
linkHref: s.linkHref,
|
||||
code: s.code,
|
||||
em: s.em,
|
||||
strong: s.strong,
|
||||
strikethrough: s.strikethrough,
|
||||
emoji: s.emoji,
|
||||
header: s.header,
|
||||
setext: s.setext,
|
||||
hr: s.hr,
|
||||
taskList: s.taskList,
|
||||
list: s.list,
|
||||
listStack: s.listStack.slice(0),
|
||||
quote: s.quote,
|
||||
indentedCode: s.indentedCode,
|
||||
trailingSpace: s.trailingSpace,
|
||||
trailingSpaceNewLine: s.trailingSpaceNewLine,
|
||||
md_inside: s.md_inside,
|
||||
fencedEndRE: s.fencedEndRE
|
||||
};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
|
||||
// Reset state.formatting
|
||||
state.formatting = false;
|
||||
|
||||
if (stream != state.thisLine.stream) {
|
||||
state.header = 0;
|
||||
state.hr = false;
|
||||
|
||||
if (stream.match(/^\s*$/, true)) {
|
||||
blankLine(state);
|
||||
return null;
|
||||
}
|
||||
|
||||
state.prevLine = state.thisLine
|
||||
state.thisLine = {stream: stream}
|
||||
|
||||
// Reset state.taskList
|
||||
state.taskList = false;
|
||||
|
||||
// Reset state.trailingSpace
|
||||
state.trailingSpace = 0;
|
||||
state.trailingSpaceNewLine = false;
|
||||
|
||||
if (!state.localState) {
|
||||
state.f = state.block;
|
||||
if (state.f != htmlBlock) {
|
||||
var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, expandedTab).length;
|
||||
state.indentation = indentation;
|
||||
state.indentationDiff = null;
|
||||
if (indentation > 0) return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return state.f(stream, state);
|
||||
},
|
||||
|
||||
innerMode: function(state) {
|
||||
if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
|
||||
if (state.localState) return {state: state.localState, mode: state.localMode};
|
||||
return {state: state, mode: mode};
|
||||
},
|
||||
|
||||
indent: function(state, textAfter, line) {
|
||||
if (state.block == htmlBlock && htmlMode.indent) return htmlMode.indent(state.htmlState, textAfter, line)
|
||||
if (state.localState && state.localMode.indent) return state.localMode.indent(state.localState, textAfter, line)
|
||||
return CodeMirror.Pass
|
||||
},
|
||||
|
||||
blankLine: blankLine,
|
||||
|
||||
getType: getType,
|
||||
|
||||
blockCommentStart: "<!--",
|
||||
blockCommentEnd: "-->",
|
||||
closeBrackets: "()[]{}''\"\"``",
|
||||
fold: "markdown"
|
||||
};
|
||||
return mode;
|
||||
}, "xml");
|
||||
|
||||
CodeMirror.defineMIME("text/markdown", "markdown");
|
||||
|
||||
CodeMirror.defineMIME("text/x-markdown", "markdown");
|
||||
|
||||
});
|
119
static/docdiff/merge.css
Normal file
119
static/docdiff/merge.css
Normal file
@ -0,0 +1,119 @@
|
||||
.CodeMirror-merge {
|
||||
position: relative;
|
||||
border: 1px solid #ddd;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.CodeMirror-merge, .CodeMirror-merge .CodeMirror {
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; }
|
||||
.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; }
|
||||
.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; }
|
||||
.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; }
|
||||
|
||||
.CodeMirror-merge-pane {
|
||||
display: inline-block;
|
||||
white-space: normal;
|
||||
vertical-align: top;
|
||||
}
|
||||
.CodeMirror-merge-pane-rightmost {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.CodeMirror-merge-gap {
|
||||
z-index: 2;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
border-left: 1px solid #ddd;
|
||||
border-right: 1px solid #ddd;
|
||||
position: relative;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.CodeMirror-merge-scrolllock-wrap {
|
||||
position: absolute;
|
||||
bottom: 0; left: 50%;
|
||||
}
|
||||
.CodeMirror-merge-scrolllock {
|
||||
position: relative;
|
||||
left: -50%;
|
||||
cursor: pointer;
|
||||
color: #555;
|
||||
line-height: 1;
|
||||
}
|
||||
.CodeMirror-merge-scrolllock:after {
|
||||
content: "\21db\00a0\00a0\21da";
|
||||
}
|
||||
.CodeMirror-merge-scrolllock.CodeMirror-merge-scrolllock-enabled:after {
|
||||
content: "\21db\21da";
|
||||
}
|
||||
|
||||
.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right {
|
||||
position: absolute;
|
||||
left: 0; top: 0;
|
||||
right: 0; bottom: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.CodeMirror-merge-copy {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
color: #44c;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.CodeMirror-merge-copy-reverse {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
color: #44c;
|
||||
}
|
||||
|
||||
.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
|
||||
.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }
|
||||
|
||||
.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);
|
||||
background-position: bottom left;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);
|
||||
background-position: bottom left;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
.CodeMirror-merge-r-chunk { background: #ffffe0; }
|
||||
.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; }
|
||||
.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; }
|
||||
.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; }
|
||||
|
||||
.CodeMirror-merge-l-chunk { background: #eef; }
|
||||
.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; }
|
||||
.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; }
|
||||
.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }
|
||||
|
||||
.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }
|
||||
.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }
|
||||
.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }
|
||||
|
||||
.CodeMirror-merge-collapsed-widget:before {
|
||||
content: "(...)";
|
||||
}
|
||||
.CodeMirror-merge-collapsed-widget {
|
||||
cursor: pointer;
|
||||
color: #88b;
|
||||
background: #eef;
|
||||
border: 1px solid #ddf;
|
||||
font-size: 90%;
|
||||
padding: 0 3px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; }
|
1006
static/docdiff/merge.js
Normal file
1006
static/docdiff/merge.js
Normal file
File diff suppressed because it is too large
Load Diff
49
static/editor.md/lib/codemirror/diff_match_patch.js
Normal file
49
static/editor.md/lib/codemirror/diff_match_patch.js
Normal file
@ -0,0 +1,49 @@
|
||||
(function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32}
|
||||
diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a,
|
||||
b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a};
|
||||
diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,b,
|
||||
d):this.diff_bisect_(a,b,d)};
|
||||
diff_match_patch.prototype.diff_lineMode_=function(a,b,c){var d=this.diff_linesToChars_(a,b);a=d.chars1;b=d.chars2;d=d.lineArray;a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push([0,""]);for(var e=d=b=0,f="",g="";b<a.length;){switch(a[b][0]){case 1:e++;g+=a[b][1];break;case -1:d++;f+=a[b][1];break;case 0:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,0,d[e]);b+=d.length}d=e=0;g=f=""}b++}a.pop();return a};
|
||||
diff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=f,h=2*f,j=Array(h),i=Array(h),k=0;k<h;k++)j[k]=-1,i[k]=-1;j[g+1]=0;i[g+1]=0;for(var k=d-e,q=0!=k%2,r=0,t=0,p=0,w=0,v=0;v<f&&!((new Date).getTime()>c);v++){for(var n=-v+r;n<=v-t;n+=2){var l=g+n,m;m=n==-v||n!=v&&j[l-1]<j[l+1]?j[l+1]:j[l-1]+1;for(var s=m-n;m<d&&s<e&&a.charAt(m)==b.charAt(s);)m++,s++;j[l]=m;if(m>d)t+=2;else if(s>e)r+=2;else if(q&&(l=g+k-n,0<=l&&l<h&&-1!=i[l])){var u=d-i[l];if(m>=
|
||||
u)return this.diff_bisectSplit_(a,b,m,s,c)}}for(n=-v+p;n<=v-w;n+=2){l=g+n;u=n==-v||n!=v&&i[l-1]<i[l+1]?i[l+1]:i[l-1]+1;for(m=u-n;u<d&&m<e&&a.charAt(d-u-1)==b.charAt(e-m-1);)u++,m++;i[l]=u;if(u>d)w+=2;else if(m>e)p+=2;else if(!q&&(l=g+k-n,0<=l&&(l<h&&-1!=j[l])&&(m=j[l],s=g+m-l,u=d-u,m>=u)))return this.diff_bisectSplit_(a,b,m,s,c)}}return[[-1,a],[1,b]]};
|
||||
diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)};
|
||||
diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;f<a.length-1;){f=a.indexOf("\n",c);-1==f&&(f=a.length-1);var r=a.substring(c,f+1),c=f+1;(e.hasOwnProperty?e.hasOwnProperty(r):void 0!==e[r])?b+=String.fromCharCode(e[r]):(b+=String.fromCharCode(g),e[r]=g,d[g++]=r)}return b}var d=[],e={};d[0]="";var f=c(a),g=c(b);return{chars1:f,chars2:g,lineArray:d}};
|
||||
diff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var c=0;c<a.length;c++){for(var d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join("")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
|
||||
diff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
|
||||
diff_match_patch.prototype.diff_commonOverlap_=function(a,b){var c=a.length,d=b.length;if(0==c||0==d)return 0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return c;for(var d=0,e=1;;){var f=a.substring(c-e),f=b.indexOf(f);if(-1==f)return d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};
|
||||
diff_match_patch.prototype.diff_halfMatch_=function(a,b){function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g="",h,j,n,l;-1!=(e=b.indexOf(d,e+1));){var m=f.diff_commonPrefix(a.substring(c),b.substring(e)),s=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<s+m&&(g=b.substring(e-s,e)+b.substring(e,e+m),h=a.substring(0,c-s),j=a.substring(c+m),n=b.substring(0,e-s),l=b.substring(e+m))}return 2*g.length>=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null;
|
||||
var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return null;var f=this,g=c(d,e,Math.ceil(d.length/4)),d=c(d,e,Math.ceil(d.length/2)),h;if(!g&&!d)return null;h=d?g?g[4].length>d[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]};
|
||||
diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f<a.length;)0==a[f][0]?(c[d++]=f,g=j,h=i,i=j=0,e=a[f][1]):(1==a[f][0]?j+=a[f][1].length:i+=a[f][1].length,e&&(e.length<=Math.max(g,h)&&e.length<=Math.max(j,i))&&(a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,d--,f=0<d?c[d-1]:-1,i=j=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<a.length;){if(-1==a[f-1][0]&&1==a[f][0]){b=a[f-1][1];c=a[f][1];
|
||||
d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}};
|
||||
diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_);
|
||||
return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c<a.length-1;){if(0==a[c-1][0]&&0==a[c+1][0]){var d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g)var h=e.substring(e.length-g),d=d.substring(0,d.length-g),e=h+e.substring(0,e.length-g),f=h+f;for(var g=d,h=e,j=f,i=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){var d=d+e.charAt(0),e=e.substring(1)+f.charAt(0),f=f.substring(1),k=b(d,e)+b(e,f);k>=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]=
|
||||
h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
|
||||
diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;f<a.length;){if(0==a[f][0])a[f][1].length<this.Diff_EditCost&&(j||i)?(c[d++]=f,g=j,h=i,e=a[f][1]):(d=0,e=null),j=i=!1;else if(-1==a[f][0]?i=!0:j=!0,e&&(g&&h&&j&&i||e.length<this.Diff_EditCost/2&&3==g+h+j+i))a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,e=null,g&&h?(j=i=!0,d=0):(d--,f=0<d?c[d-1]:-1,j=i=!1),b=!0;f++}b&&this.diff_cleanupMerge(a)};
|
||||
diff_match_patch.prototype.diff_cleanupMerge=function(a){a.push([0,""]);for(var b=0,c=0,d=0,e="",f="",g;b<a.length;)switch(a[b][0]){case 1:d++;f+=a[b][1];b++;break;case -1:c++;e+=a[b][1];b++;break;case 0:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&0==a[b-c-d-1][0]?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,[0,f.substring(0,g)]),b++),f=f.substring(g),e=e.substring(g)),g=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-g)+a[b][1],f=f.substring(0,f.length-
|
||||
g),e=e.substring(0,e.length-g))),0===c?a.splice(b-d,c+d,[1,f]):0===d?a.splice(b-c,c+d,[-1,e]):a.splice(b-c-d,c+d,[-1,e],[1,f]),b=b-c-d+(c?1:0)+(d?1:0)+1):0!==b&&0==a[b-1][0]?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=""}""===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)0==a[b-1][0]&&0==a[b+1][0]&&(a[b][1].substring(a[b][1].length-a[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,
|
||||
a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};diff_match_patch.prototype.diff_xIndex=function(a,b){var c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){1!==a[g][0]&&(c+=a[g][1].length);-1!==a[g][0]&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)};
|
||||
diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=/</g,e=/>/g,f=/\n/g,g=0;g<a.length;g++){var h=a[g][0],j=a[g][1],j=j.replace(c,"&").replace(d,"<").replace(e,">").replace(f,"¶<br>");switch(h){case 1:b[g]='<ins style="background:#e6ffe6;">'+j+"</ins>";break;case -1:b[g]='<del style="background:#ffe6e6;">'+j+"</del>";break;case 0:b[g]="<span>"+j+"</span>"}}return b.join("")};
|
||||
diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;c<a.length;c++)1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_text2=function(a){for(var b=[],c=0;c<a.length;c++)-1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_levenshtein=function(a){for(var b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][0],g=a[e][1];switch(f){case 1:c+=g.length;break;case -1:d+=g.length;break;case 0:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};
|
||||
diff_match_patch.prototype.diff_toDelta=function(a){for(var b=[],c=0;c<a.length;c++)switch(a[c][0]){case 1:b[c]="+"+encodeURI(a[c][1]);break;case -1:b[c]="-"+a[c][1].length;break;case 0:b[c]="="+a[c][1].length}return b.join("\t").replace(/%20/g," ")};
|
||||
diff_match_patch.prototype.diff_fromDelta=function(a,b){for(var c=[],d=0,e=0,f=b.split(/\t/g),g=0;g<f.length;g++){var h=f[g].substring(1);switch(f[g].charAt(0)){case "+":try{c[d++]=[1,decodeURI(h)]}catch(j){throw Error("Illegal escape in diff_fromDelta: "+h);}break;case "-":case "=":var i=parseInt(h,10);if(isNaN(i)||0>i)throw Error("Invalid number in diff_fromDelta: "+h);h=a.substring(e,e+=i);"="==f[g].charAt(0)?c[d++]=[0,h]:c[d++]=[-1,h];break;default:if(f[g])throw Error("Invalid diff operation in diff_fromDelta: "+
|
||||
f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+") does not equal source text length ("+a.length+").");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error("Null input. (match_main)");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
|
||||
diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return!f.Match_Distance?g?1:e:e+g/f.Match_Distance}if(b.length>this.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<<b.length-1,h=-1,i,k,q=b.length+a.length,r,t=0;t<b.length;t++){i=0;for(k=q;i<k;)d(t,c+
|
||||
k)<=g?i=k:q=k,k=Math.floor((q-i)/2+i);q=k;i=Math.max(1,c-k+1);var p=Math.min(c+k,a.length)+b.length;k=Array(p+2);for(k[p+1]=(1<<t)-1;p>=i;p--){var w=e[a.charAt(p-1)];k[p]=0===t?(k[p+1]<<1|1)&w:(k[p+1]<<1|1)&w|((r[p+1]|r[p])<<1|1)|r[p+1];if(k[p]&j&&(w=d(t,p-1),w<=g))if(g=w,h=p-1,h>c)i=Math.max(1,2*c-h);else break}if(d(t+1,c)>g)break;r=k}return h};
|
||||
diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return b};
|
||||
diff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){for(var c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift([0,c]);(d=b.substring(a.start2+a.length1,a.start2+a.length1+d))&&a.diffs.push([0,d]);a.start1-=c.length;a.start2-=c.length;a.length1+=
|
||||
c.length+d.length;a.length2+=c.length+d.length}};
|
||||
diff_match_patch.prototype.patch_make=function(a,b,c){var d;if("string"==typeof a&&"string"==typeof b&&"undefined"==typeof c)d=a,b=this.diff_main(d,b,!0),2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b));else if(a&&"object"==typeof a&&"undefined"==typeof b&&"undefined"==typeof c)b=a,d=this.diff_text1(b);else if("string"==typeof a&&b&&"object"==typeof b&&"undefined"==typeof c)d=a;else if("string"==typeof a&&"string"==typeof b&&c&&"object"==typeof c)d=a,b=c;else throw Error("Unknown call format to patch_make.");
|
||||
if(0===b.length)return[];c=[];a=new diff_match_patch.patch_obj;for(var e=0,f=0,g=0,h=d,j=0;j<b.length;j++){var i=b[j][0],k=b[j][1];!e&&0!==i&&(a.start1=f,a.start2=g);switch(i){case 1:a.diffs[e++]=b[j];a.length2+=k.length;d=d.substring(0,g)+k+d.substring(g);break;case -1:a.length1+=k.length;a.diffs[e++]=b[j];d=d.substring(0,g)+d.substring(g+k.length);break;case 0:k.length<=2*this.Patch_Margin&&e&&b.length!=j+1?(a.diffs[e++]=b[j],a.length1+=k.length,a.length2+=k.length):k.length>=2*this.Patch_Margin&&
|
||||
e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c],e=new diff_match_patch.patch_obj;e.diffs=[];for(var f=0;f<d.diffs.length;f++)e.diffs[f]=d.diffs[f].slice();e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return b};
|
||||
diff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];a=this.patch_deepCopy(a);var c=this.patch_addPadding(a);b=c+b+c;this.patch_splitMax(a);for(var d=0,e=[],f=0;f<a.length;f++){var g=a[f].start2+d,h=this.diff_text1(a[f].diffs),j,i=-1;if(h.length>this.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g);
|
||||
if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;i<a[f].diffs.length;i++){var q=a[f].diffs[i];0!==q[0]&&(k=this.diff_xIndex(g,h));1===q[0]?b=b.substring(0,
|
||||
j+k)+q[1]+b.substring(j+k):-1===q[0]&&(b=b.substring(0,j+k)+b.substring(j+this.diff_xIndex(g,h+q[1].length)));-1!==q[0]&&(h+=q[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};
|
||||
diff_match_patch.prototype.patch_addPadding=function(a){for(var b=this.Patch_Margin,c="",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;var d=a[0],e=d.diffs;if(0==e.length||0!=e[0][0])e.unshift([0,c]),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else if(b>e[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0,
|
||||
c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};
|
||||
diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var d=a[c];a.splice(c--,1);for(var e=d.start1,f=d.start2,g="";0!==d.diffs.length;){var h=new diff_match_patch.patch_obj,j=!0;h.start1=e-g.length;h.start2=f-g.length;""!==g&&(h.length1=h.length2=g.length,h.diffs.push([0,g]));for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){var g=d.diffs[0][0],i=d.diffs[0][1];1===g?(h.length2+=i.length,f+=i.length,h.diffs.push(d.diffs.shift()),
|
||||
j=!1):-1===g&&1==h.diffs.length&&0==h.diffs[0][0]&&i.length>2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&&
|
||||
(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join("")};
|
||||
diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;a=a.split("\n");for(var c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error("Invalid patch string: "+a[c]);var f=new diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);""===e[2]?(f.start1--,f.length1=1):"0"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);""===e[4]?(f.start2--,f.length2=1):"0"==e[4]?f.length2=0:(f.start2--,f.length2=
|
||||
parseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var g=decodeURI(a[c].substring(1))}catch(h){throw Error("Illegal escape in patch_fromText: "+g);}if("-"==e)f.diffs.push([-1,g]);else if("+"==e)f.diffs.push([1,g]);else if(" "==e)f.diffs.push([0,g]);else if("@"==e)break;else if(""!==e)throw Error('Invalid patch mode "'+e+'" in: '+g);c++}}return b};diff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};
|
||||
diff_match_patch.patch_obj.prototype.toString=function(){var a,b;a=0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1;b=0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2;a=["@@ -"+a+" +"+b+" @@\n"];var c;for(b=0;b<this.diffs.length;b++){switch(this.diffs[b][0]){case 1:c="+";break;case -1:c="-";break;case 0:c=" "}a[b+1]=c+encodeURI(this.diffs[b][1])+"\n"}return a.join("").replace(/%20/g," ")};
|
||||
this.diff_match_patch=diff_match_patch;this.DIFF_DELETE=-1;this.DIFF_INSERT=1;this.DIFF_EQUAL=0;})()
|
@ -1,3 +1,12 @@
|
||||
##### MrDoc使用文档
|
||||
|
||||
- [http://mrdoc.zmister.com](http://mrdoc.zmister.com)
|
||||
|
||||
##### MrDoc项目地址
|
||||
|
||||
- [https://gitee.com/zmister/MrDoc](https://gitee.com/zmister/MrDoc)
|
||||
- [https://github.com/zmister2016/MrDoc](https://github.com/zmister2016/MrDoc)
|
||||
|
||||
##### Markdown语法教程 (Markdown syntax tutorial)
|
||||
|
||||
- [Markdown Syntax](http://daringfireball.net/projects/markdown/syntax/ "Markdown Syntax")
|
||||
|
@ -307,7 +307,7 @@ li.active > a,li.active > div > a{
|
||||
position: relative;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 15px 40px 5px;
|
||||
padding: 15px 15px 15px 5px;
|
||||
}
|
||||
.doc-content{
|
||||
outline: 0;
|
||||
|
@ -22,6 +22,19 @@
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
}
|
||||
/* 编辑器预览列表样式 */
|
||||
.markdown-body ul li{
|
||||
list-style:disc;
|
||||
}
|
||||
.markdown-body ul > li > ul > li{
|
||||
list-style-type: circle;
|
||||
}
|
||||
.markdown-body ol li{
|
||||
list-style-type: decimal;
|
||||
}
|
||||
.markdown-body ol ol ul,.markdown-body ol ul ul,.markdown-body ul ol ul,.markdown-body ul ul ul li{
|
||||
list-style-type: square;
|
||||
}
|
||||
/* 选择上级文档样式 */
|
||||
.selected-parent-doc{
|
||||
text-decoration-line: underline;
|
||||
@ -259,14 +272,10 @@
|
||||
id:'uploadAttach',//配置ID,
|
||||
content:$('#upload-attach'),
|
||||
success: function(layero, index){
|
||||
layer.load();
|
||||
$.post('{% url "manage_attachment" %}',{types:2},function(r){
|
||||
$("#attach_table tbody").empty()
|
||||
if(r.status){
|
||||
// $.each(r.data,function(k,v){
|
||||
// console.log(k,v)
|
||||
// var row = "<tr><td>" + v.filename + "</td><td>"+ v.filesize +"</td><td>"+ v.filetime +"</td><td><button class='layui-btn layui-btn-normal layui-btn-sm' data-name='"+ v.filename +"' data-path='"+ v.filepath +"' onclick='insertAttach(this)'>选择</button></td></tr>"
|
||||
// $("#attach_table tbody").append(row)
|
||||
// })
|
||||
//调用分页显示
|
||||
laypage.render({
|
||||
elem: 'select-attach-page',//分页的div
|
||||
@ -283,8 +292,9 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
//layer.close(loading);//关闭加载提示
|
||||
layer.closeAll("loading");//关闭加载提示
|
||||
}else{
|
||||
layer.closeAll("loading");//关闭加载提示
|
||||
layer.msg("获取附件失败,请稍后重试!")
|
||||
}
|
||||
})
|
||||
@ -361,11 +371,16 @@
|
||||
reader.onload = function (event) {
|
||||
var base64 = event.target.result;
|
||||
//ajax上传图片
|
||||
layer.load();
|
||||
$.post("{% url 'upload_doc_img' %}",{base:base64}, function (ret) {
|
||||
layer.msg(ret.message);
|
||||
if (ret.success === 1) {
|
||||
//新一行的图片显示
|
||||
layer.closeAll("loading");
|
||||
editor.insertValue("\n");
|
||||
}else{
|
||||
layer.closeAll("loading");
|
||||
layer.msg("粘贴图片失败!")
|
||||
}
|
||||
});
|
||||
}; // data url!
|
||||
@ -388,12 +403,12 @@
|
||||
//console.log(data.index); //得到当前Tab的所在下标
|
||||
//console.log(data.elem); //得到当前的Tab大容器
|
||||
if(data.index == 1){
|
||||
layer.load();
|
||||
console.log('选择图片')
|
||||
$("#select-img-group").empty(); //删除已有分组按钮
|
||||
//请求新的分组数据
|
||||
$.post("{% url 'manage_img_group' %}",{'types':3},function(r){
|
||||
if(r.status){
|
||||
//console.log(r.data)
|
||||
group_btn_str = ''
|
||||
for(var i in r.data){
|
||||
group_btn_str += '<button class="layui-btn layui-btn-normal layui-btn-sm" onclick="switchImgGroup(' + r.data[i].group_id +')">' + r.data[i].group_name + '(' + r.data[i].group_cnt + ')</button>'
|
||||
@ -402,7 +417,6 @@
|
||||
}
|
||||
});
|
||||
//请求全部图片数据
|
||||
var loading = layer.load();
|
||||
$.post("{% url 'manage_image' %}",{'types':2,'group_id':0},function(r){
|
||||
if(r.status){
|
||||
//调用分页显示
|
||||
@ -422,9 +436,9 @@
|
||||
}();
|
||||
}
|
||||
});
|
||||
layer.close(loading);//关闭加载提示
|
||||
layer.closeAll("loading");
|
||||
}else{
|
||||
layer.close(loading);
|
||||
layer.closeAll("loading");
|
||||
layer.msg("获取图片失败")
|
||||
}
|
||||
})
|
||||
@ -437,7 +451,7 @@
|
||||
};
|
||||
//切换图片分组
|
||||
switchImgGroup = function(e){
|
||||
var switch_loading = layer.load();
|
||||
layer.load();
|
||||
$.post("{% url 'manage_image' %}", {
|
||||
'types': 2,
|
||||
'group_id': e
|
||||
@ -462,9 +476,9 @@
|
||||
} ();
|
||||
}
|
||||
});
|
||||
layer.close(switch_loading); //关闭加载提示
|
||||
layer.closeAll("loading"); //关闭加载提示
|
||||
} else {
|
||||
layer.close(switch_loading);
|
||||
layer.closeAll("loading");
|
||||
layer.msg("获取分组图片失败")
|
||||
}
|
||||
})
|
||||
@ -516,9 +530,7 @@
|
||||
<th>操作</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
<div id="select-attach-page"></div>
|
||||
</div>
|
||||
@ -548,6 +560,9 @@
|
||||
upload.render({
|
||||
elem: '#upload_img',
|
||||
url: '{% url "upload_doc_img" %}',
|
||||
before: function(obj){ //obj参数包含的信息,跟 choose回调完全一致,可参见上文。
|
||||
layer.load(); //上传loading
|
||||
},
|
||||
done: function(res, index, upload){ //上传后的回调
|
||||
//上传成功
|
||||
if(res.success == 1){
|
||||
@ -555,13 +570,14 @@
|
||||
layer.closeAll();
|
||||
layer.msg("上传成功");
|
||||
}else{
|
||||
layer.closeAll();
|
||||
layer.msg("上传出错,请重试!")
|
||||
}
|
||||
},
|
||||
accept: 'images', //允许上传的文件类型
|
||||
acceptMime:'image/*',
|
||||
field:'manage_upload',
|
||||
size: 5000, //最大允许上传的文件大小
|
||||
size: 5120, //最大允许上传的图片大小
|
||||
|
||||
});
|
||||
// 按钮选择上传附件
|
||||
@ -570,6 +586,9 @@
|
||||
elem: '#upload_attachment',
|
||||
url: '{% url "manage_attachment" %}',
|
||||
data:{types:0,csrfmiddlewaretoken: '{{ csrf_token }}'},
|
||||
before: function(obj){ //obj参数包含的信息,跟 choose回调完全一致,可参见上文。
|
||||
layer.load(); //上传loading
|
||||
},
|
||||
done: function(res, index, upload){ //上传后的回调
|
||||
//上传成功,刷新页面
|
||||
if(res.status){
|
||||
@ -583,7 +602,7 @@
|
||||
accept: 'file', //允许上传的文件类型
|
||||
exts:'zip', //允许上传zip压缩文件
|
||||
field:'attachment_upload',
|
||||
size: 50000, //最大允许上传的文件大小
|
||||
size: 51200, //最大允许上传的文件大小
|
||||
})
|
||||
//修改预览div中a标签链接新窗口打开
|
||||
$('div.editormd-preview').on('click','a',function(e){
|
||||
|
@ -215,18 +215,19 @@
|
||||
else{
|
||||
//发布按钮设为禁用
|
||||
$("#create_doc").attr({"disabled":"disabled"});
|
||||
layer.load(); // 加载提示
|
||||
$.post("{% url 'create_doc' %}",data,function(r){
|
||||
if(r.status){
|
||||
//创建成功
|
||||
layer.closeAll("loading"); //关闭加载层
|
||||
layer.msg('发布文档成功,即将跳转到文档页面',function(){
|
||||
md_changed = false;
|
||||
//跳转到首页
|
||||
//window.location.href = "{% url 'pro_list' %}";
|
||||
//跳转到发布的文档
|
||||
window.location.href = "/project-" + r.data.pro + "/doc-" + r.data.doc
|
||||
});
|
||||
}else{
|
||||
//创建失败
|
||||
layer.closeAll("loading"); //关闭加载层
|
||||
layer.msg('发布文档失败:'+r.data);
|
||||
//恢复按钮状态
|
||||
$("#create_doc").removeAttr("disabled");
|
||||
@ -253,15 +254,18 @@
|
||||
layer.msg('请选择文集!');
|
||||
}
|
||||
else{
|
||||
layer.load();
|
||||
$.post("{% url 'create_doc' %}",data,function(r){
|
||||
if(r.status){
|
||||
//保存成功
|
||||
layer.closeAll("loading");
|
||||
md_changed = false;
|
||||
layer.msg('保存草稿成功',function(){
|
||||
window.location.href = "/modify_doc/"+r.data.doc+"/";
|
||||
});
|
||||
}else{
|
||||
//创建失败
|
||||
layer.closeAll("loading");
|
||||
layer.msg('保存草稿失败:'+r.data);
|
||||
}
|
||||
});
|
||||
|
@ -59,6 +59,7 @@
|
||||
<script>
|
||||
//保存文档模板
|
||||
createDocTemp = function(){
|
||||
layer.load();
|
||||
var data = {
|
||||
'name':$("#doctemp-name").val(),
|
||||
'content':editor.getMarkdown(),
|
||||
@ -66,12 +67,14 @@
|
||||
$.post("{% url 'create_doctemp' %}",data,function(r){
|
||||
if(r.status){
|
||||
//创建成功
|
||||
layer.closeAll("loading");
|
||||
layer.msg('保存成功',function(){
|
||||
md_changed = false;
|
||||
window.location.href = "{% url 'manage_doctemp' %}";
|
||||
});
|
||||
}else{
|
||||
//创建失败
|
||||
layer.closeAll("loading");
|
||||
layer.msg('保存失败');
|
||||
}
|
||||
});
|
||||
|
229
template/app_doc/diff_doc.html
Normal file
229
template/app_doc/diff_doc.html
Normal file
@ -0,0 +1,229 @@
|
||||
{% load staticfiles %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
|
||||
<meta http-equiv="Cache-Control" content="no-transform" />
|
||||
<meta http-equiv="Cache-Control" content="no-siteapp" />
|
||||
<meta http-equiv="Cache-Control" content="max-age=7200" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<meta name="keywords" content="{% block keyword %}{% endblock %}mrdoc"/>
|
||||
<meta name="description" content="{% block description %}{% endblock %}" />
|
||||
<title>文档历史版本对比:{{ doc.name }} - MrDoc</title>
|
||||
<link href="{% static 'layui/css/layui.css' %}?version={{mrdoc_version}}" rel="stylesheet">
|
||||
<link rel="icon" href="{% static 'favicon_16.png' %}"/>
|
||||
<link href="{% static 'mrdoc.css' %}?version={{mrdoc_version}}" rel="stylesheet">
|
||||
|
||||
<link rel=stylesheet href="{% static '/docdiff/codemirror.css' %}">
|
||||
<link rel=stylesheet href="{% static '/docdiff/merge.css' %}">
|
||||
<style>
|
||||
.CodeMirror-merge, .CodeMirror-merge .CodeMirror{
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
||||
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="doc layui-fluid" style="padding-left:0px;">
|
||||
<!-- 左侧工具栏 -->
|
||||
<div class="doc-summary">
|
||||
<div class="project-title"><i class="fa fa-edit"></i> MrDoc文档编辑器<br>
|
||||
<span style="font-size: 14px;">你正在:对比文档历史版本</span>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="layui-row" style="padding: 10px;">
|
||||
<p style="font-size: 20px;font-weight: 700;text-align: center;">{{ doc.name }}</p><hr>
|
||||
<table class="layui-table" lay-size="sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>创建时间</th>
|
||||
<th>用户</th>
|
||||
<th>查看</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for history in history_list %}
|
||||
<tr>
|
||||
<td>{{ history.create_time }}</td>
|
||||
<td>{{ history.create_user }}</td>
|
||||
<td>
|
||||
<a href="{% url 'diff_doc' history.doc.id history.id %}" >对比</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 左侧工具栏结束 -->
|
||||
|
||||
<!-- 右侧编辑器栏 -->
|
||||
<div class="doc-body">
|
||||
<!-- 文档导航 -->
|
||||
<div class="doc-header" role="navigation">
|
||||
<a class="btn pull-left js-toolbar-action" aria-label="" href="javascript:void(0);" title="切换侧边栏">
|
||||
<i class="layui-icon layui-icon-spread-left"></i>
|
||||
</a>
|
||||
<!-- 顶部工具栏 -->
|
||||
<a class="btn pull-right" aria-label="" href="{% url 'pro_list' %}">
|
||||
<i class="layui-icon layui-icon-home"></i> <span class="layui-hide-xs">首页</span>
|
||||
</a>
|
||||
</div>
|
||||
<!-- 文档主体 -->
|
||||
<div class="doc-body-content" style="padding-left: 15px;">
|
||||
<div class="mrdoc-body-content-div">
|
||||
<!-- 文档内容 -->
|
||||
<div class="mrdoc-editor-content">
|
||||
<div style="display: none">
|
||||
<textarea id="doc">{{ doc.pre_content }}</textarea>
|
||||
<textarea id="history">{{ history.pre_content }}</textarea>
|
||||
</div>
|
||||
<!-- 对比开始 -->
|
||||
<div class="layui-row" style="margin-bottom: 10px;">
|
||||
<div class="layui-col-md6" style="text-align: center;">
|
||||
<span style="font-weight: 700;">当前版本</span>
|
||||
</div>
|
||||
<div class="layui-col-md6" style="text-align: center;">
|
||||
<span style="font-weight: 700;color: darkblue;">历史版本:{{history.create_time}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="markdown-body" id="content">
|
||||
</div>
|
||||
<!-- 对比结束 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧编辑器结束 -->
|
||||
</div>
|
||||
|
||||
<script src="{% static 'jquery/3.1.1/jquery.min.js' %}"></script>
|
||||
<script src="{% static 'layui/layui.all.js' %}"></script>
|
||||
|
||||
<script src="{% static '/docdiff/codemirror.js' %}"></script>
|
||||
<script src="{% static '/docdiff/markdown.js' %}"></script>
|
||||
<script src="{% static '/docdiff/diff_match_patch.js' %}"></script>
|
||||
<script src="{% static '/docdiff/merge.js' %}"></script>
|
||||
|
||||
<script>
|
||||
$.ajaxSetup({
|
||||
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
|
||||
});
|
||||
var tree = layui.tree;
|
||||
//加载页面时执行一次
|
||||
changeSidebar();
|
||||
//监听浏览器宽度的改变
|
||||
window.onresize = function(){
|
||||
changeSidebar();
|
||||
};
|
||||
function changeSidebar(){
|
||||
// 获取匹配指定的媒体查询
|
||||
var screen_width = window.matchMedia('(max-width: 768px)');
|
||||
//判断匹配状态
|
||||
if(screen_width.matches){
|
||||
//如果匹配到,切换侧边栏
|
||||
$("body").addClass("big-page");
|
||||
}else{
|
||||
$("body").removeClass("big-page");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- 切换隐藏侧边栏 -->
|
||||
<script>
|
||||
// 切换侧边栏
|
||||
$(function(){
|
||||
$(".js-toolbar-action").click(toggleSidebar);
|
||||
});
|
||||
//切换侧边栏显示隐藏
|
||||
function toggleSidebar(){
|
||||
console.log("切换侧边栏")
|
||||
$("body").toggleClass("big-page");
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 展开收起左边目录 -->
|
||||
<script>
|
||||
$(function(){
|
||||
$(".switch-toc").click(SwitchToc);
|
||||
});
|
||||
function SwitchToc(i){
|
||||
var $me = $(this);
|
||||
$(this).parent().next("ul").toggleClass("toc-close"); //切换展开收起样式
|
||||
$(this).toggleClass("fa-chevron-left fa-chevron-down");//切换图标
|
||||
};
|
||||
</script>
|
||||
<script>
|
||||
// 初始化一些变量
|
||||
var value, orig1, orig2, dv, panes = 2, highlight = true, connect = "align", collapse = false;
|
||||
|
||||
// 初始化合并对比
|
||||
function initUI() {
|
||||
if (value == null) return;
|
||||
var target = document.getElementById("content");
|
||||
target.innerHTML = "";
|
||||
// 生成合并视图
|
||||
dv = CodeMirror.MergeView(target, {
|
||||
value: value, //编辑值
|
||||
orig: orig2, // 对比值
|
||||
mode: "markdown", // Markdown语法模式
|
||||
lineNumbers: true, // 显示行号
|
||||
lineWrapping: true, // 换行
|
||||
foldGutter: true,
|
||||
highlightDifferences: highlight, // 高亮显示差异
|
||||
revertButtons:false,
|
||||
});
|
||||
}
|
||||
|
||||
// 切换差异对比
|
||||
function toggleDifferences() {
|
||||
dv.setShowDifferences(highlight = !highlight);
|
||||
}
|
||||
|
||||
// 页面加载执行
|
||||
window.onload = function() {
|
||||
value = document.getElementById('doc').innerHTML;
|
||||
orig2 = document.getElementById('history').innerHTML;
|
||||
initUI();
|
||||
let d = document.createElement("div");
|
||||
d.style.cssText = "width: 50px; margin: 7px; height: 14px";
|
||||
dv.editor().addLineWidget(57, d)
|
||||
};
|
||||
|
||||
function mergeViewHeight(mergeView) {
|
||||
function editorHeight(editor) {
|
||||
if (!editor) return 0;
|
||||
return editor.getScrollInfo().height;
|
||||
}
|
||||
return Math.max(editorHeight(mergeView.leftOriginal()),
|
||||
editorHeight(mergeView.editor()),
|
||||
editorHeight(mergeView.rightOriginal()));
|
||||
}
|
||||
|
||||
function resize(mergeView) {
|
||||
var height = mergeViewHeight(mergeView);
|
||||
for(;;) {
|
||||
if (mergeView.leftOriginal())
|
||||
mergeView.leftOriginal().setSize(null, height);
|
||||
mergeView.editor().setSize(null, height);
|
||||
if (mergeView.rightOriginal())
|
||||
mergeView.rightOriginal().setSize(null, height);
|
||||
|
||||
var newHeight = mergeViewHeight(mergeView);
|
||||
if (newHeight >= height) break;
|
||||
else height = newHeight;
|
||||
}
|
||||
mergeView.wrap.style.height = height + "px";
|
||||
}
|
||||
</script>
|
||||
<script src="{% static 'mrdoc.js' %}?version={{mrdoc_version}}"></script>
|
||||
{% block custom_script %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
@ -83,7 +83,7 @@
|
||||
type:1,
|
||||
offset:'r',//坐标
|
||||
maxmin:true, //最大最小化
|
||||
area: ['200px','200px'],
|
||||
area: '200px',
|
||||
shade: 0,
|
||||
content: $("#toc-container"),
|
||||
});
|
||||
|
@ -47,10 +47,11 @@
|
||||
list-style-position:inside;
|
||||
}
|
||||
ul.markdown-toc-list li{
|
||||
list-style: inherit!important;
|
||||
list-style: none!important;
|
||||
line-height: 24px;
|
||||
}
|
||||
ul.markdown-toc-list > li > ul > li,ul.markdown-toc-list > li > ul li{
|
||||
padding-left:10px;
|
||||
padding-left:15px;
|
||||
}
|
||||
ul.markdown-toc-list a{
|
||||
text-decoration: underline!important;
|
||||
@ -197,7 +198,7 @@
|
||||
</div>
|
||||
<!-- 标题结束 -->
|
||||
<!-- 正文开始 -->
|
||||
<div class="markdown-body" id="content">
|
||||
<div class="markdown-body" id="content" style="padding: 0 20px;">
|
||||
{% block page_content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
@ -253,6 +254,7 @@
|
||||
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
|
||||
});
|
||||
//解析Markdown为HTML
|
||||
layer.load(1);
|
||||
editormd.markdownToHTML("content", {
|
||||
htmlDecode : "style,script,iframe",
|
||||
emoji : true, //emoji表情
|
||||
@ -267,6 +269,7 @@
|
||||
atLink : false,//禁用@链接
|
||||
|
||||
});
|
||||
layer.closeAll("loading");
|
||||
//为当前页面的目录链接添加蓝色样式
|
||||
$("nav li a").each(function (i) {
|
||||
var $me = $(this);
|
||||
|
@ -27,16 +27,6 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- 州的先生博客链接 -->
|
||||
<!-- <div class="layui-hide-xs" style="">
|
||||
<ul class="layui-nav" style="">
|
||||
<li class="layui-nav-item">
|
||||
<a href="https://zmister.com" target="_blank">
|
||||
<i class="layui-icon layui-icon-website"></i> <span class="">州的先生</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div> -->
|
||||
<!-- 用户菜单 -->
|
||||
<div class="">
|
||||
<ul class="layui-nav layui-layout-right">
|
||||
|
@ -36,6 +36,10 @@
|
||||
.layui-input{
|
||||
height: 30px !important;
|
||||
}
|
||||
/* layui单选框样式 */
|
||||
.layui-form-radio>i:hover, .layui-form-radioed>i{
|
||||
color: #1E9FFF;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="layui-layout-body">
|
||||
|
@ -56,6 +56,9 @@
|
||||
</td>
|
||||
<td>{{ doc.create_time }}</td>
|
||||
<td>
|
||||
<a href="{% url 'manage_doc_history' doc.id %}" class="layui-btn layui-btn-xs layui-btn-normal">
|
||||
<i class="layui-icon layui-icon-log"></i> 历史
|
||||
</a>
|
||||
<a href="{% url 'modify_doc' doc_id=doc.id %}" target="_blank" class="layui-btn layui-btn-xs layui-btn-normal">
|
||||
<i class="layui-icon layui-icon-edit"></i>修改
|
||||
</a>
|
||||
|
103
template/app_doc/manage_doc_history.html
Normal file
103
template/app_doc/manage_doc_history.html
Normal file
@ -0,0 +1,103 @@
|
||||
{% extends 'app_doc/manage_base.html' %}
|
||||
{% load staticfiles %}
|
||||
{% block title %}文档历史版本管理{% endblock %}
|
||||
{% block content %}
|
||||
<div class="layui-row" style="margin-bottom: 10px;padding-left:15px;">
|
||||
<span class="layui-breadcrumb" lay-separator=">">
|
||||
<a href="{% url 'manage_doc' %}">文档管理</a>
|
||||
<a><cite>历史版本管理</cite></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="layui-card-header" style="margin-bottom: 10px;">
|
||||
<div class="layui-row">
|
||||
<span style="font-size:18px;">文档:{{doc.name}} 的历史版本管理
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-row" >
|
||||
<table class="layui-table" id="doctemp-list" lay-skin="" lay-even>
|
||||
<colgroup>
|
||||
<col width="200">
|
||||
<col width="200">
|
||||
<col>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>创建时间</th>
|
||||
<th>创建人</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for his in historys %}
|
||||
<tr>
|
||||
<td>{{ his.create_time }}</td>
|
||||
<td>{{ his.create_user }}</td>
|
||||
<td>
|
||||
<a href="{% url 'diff_doc' doc.id his.id %}" target="_blank" class="layui-btn layui-btn-xs layui-btn-normal">
|
||||
<i class="layui-icon layui-icon-edit"></i>对比
|
||||
</a>
|
||||
<a href="javascript:void(0);" onclick="delDocHis('{{his.id}}');" class="layui-btn layui-btn-xs layui-btn-normal">
|
||||
<i class="layui-icon layui-icon-delete"></i>删除
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- 分页 -->
|
||||
<div class="layui-row">
|
||||
<div class="layui-box layui-laypage layui-laypage-default">
|
||||
<!-- 上一页 -->
|
||||
{% if historys.has_previous %}
|
||||
<a href="?page={{ historys.previous_page_number }}" class="layui-btn layui-btn-xs layui-btn-normal">上一页</a>
|
||||
{% else %}
|
||||
<a href="javascript:;" class="layui-btn layui-btn-xs layui-btn-disabled">上一页</a>
|
||||
{% endif %}
|
||||
<!-- 当前页 -->
|
||||
<span class="layui-laypage-curr">
|
||||
<em class="layui-laypage-em"></em>
|
||||
<em>{{ historys.number }}/{{ historys.paginator.num_pages }}</em>
|
||||
</span>
|
||||
<!-- 下一页 -->
|
||||
{% if historys.has_next %}
|
||||
<a href="?page={{ historys.next_page_number }}" class="layui-btn layui-btn-xs layui-btn-normal">下一页</a>
|
||||
{% else %}
|
||||
<a class="layui-btn layui-btn-xs layui-btn-disabled">下一页</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block custom_script %}
|
||||
<script>
|
||||
delDocHis = function(history_id){
|
||||
layer.open({
|
||||
type:1,
|
||||
title:'删除历史记录',
|
||||
area:'300px;',
|
||||
id:'delPro',//配置ID
|
||||
content:'<div style="margin-left:10px;">警告:操作将删除此文档历史版本记录!</div>',
|
||||
btn:['确定','取消'], //添加按钮
|
||||
btnAlign:'c', //按钮居中
|
||||
yes:function (index,layero) {
|
||||
layer.load(1);
|
||||
data = {
|
||||
'history_id':history_id,
|
||||
}
|
||||
$.post("{% url 'manage_doc_history' doc.id %}",data,function(r){
|
||||
if(r.status){
|
||||
//修改成功
|
||||
window.location.reload();
|
||||
//layer.close(index)
|
||||
}else{
|
||||
//修改失败,提示
|
||||
layer.close('loading')
|
||||
layer.msg(r.data)
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
@ -33,7 +33,9 @@
|
||||
<input type="text" value="{{ pro.id | project_collaborator_cnt }}人" class="layui-input" disabled>
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">
|
||||
<button class="layui-btn layui-btn-xs" type="button" onclick="addProjectColla({{pro.id}})">添加</button>
|
||||
<button class="layui-btn layui-btn-xs layui-btn-normal" type="button" onclick="addProjectColla('{{pro.id}}')">
|
||||
<i class="layui-icon layui-icon-addition"></i>添加
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -60,8 +62,12 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="javascript:void(0);" onclick="modifyProjectColla('{{colla.user}}')" class="layui-btn layui-btn-xs">修改</a>
|
||||
<a href="javascript:void(0);" onclick="delProColla('{{colla.user}}')" class="layui-btn layui-btn-xs">删除</a>
|
||||
<a href="javascript:void(0);" onclick="modifyProjectColla('{{colla.user}}')" class="layui-btn layui-btn-xs layui-btn-normal">
|
||||
<i class="layui-icon layui-icon-edit"></i>修改
|
||||
</a>
|
||||
<a href="javascript:void(0);" onclick="delProColla('{{colla.user}}')" class="layui-btn layui-btn-xs layui-btn-normal">
|
||||
<i class="layui-icon layui-icon-delete"></i>删除
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -34,9 +34,15 @@
|
||||
<div class="layui-row">
|
||||
<div class="doc-form-label" style="margin-bottom: 10px;">
|
||||
{% if doc.status == 0 %}
|
||||
<strong>*当前文档状态为:草稿</strong>
|
||||
<strong>*当前文档状态:草稿</strong>
|
||||
{% elif doc.status == 1 %}
|
||||
<strong>*当前文档状态为:已发布 </strong><a class="layui-btn layui-btn-xs layui-btn-normal" target="_blank" href="{% url 'doc' doc.top_doc doc.id %}">查看文档</a>
|
||||
<strong>*当前文档状态:已发布 </strong>
|
||||
<a class="layui-btn layui-btn-xs layui-btn-normal" target="_blank" href="{% url 'doc' doc.top_doc doc.id %}">
|
||||
<i class="fa fa-book"></i> 查看
|
||||
</a>
|
||||
<button class="layui-btn layui-btn-xs layui-btn-normal" id="doc-history">
|
||||
<i class="fa fa-history"></i> 历史
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@ -111,9 +117,11 @@
|
||||
<script>
|
||||
//获取文档数和上级文档信息
|
||||
$(function(){
|
||||
layer.load();
|
||||
var doc_parent_id = {{ doc.parent_doc }};
|
||||
$.post("{% url 'get_pro_doc_tree' %}",{'pro_id':$("#project").val()},function(r){
|
||||
if(r.status){
|
||||
layer.closeAll("loading");
|
||||
var doc_tree = tree.render({
|
||||
elem:"#doc-tree",
|
||||
id:'docTree',
|
||||
@ -159,6 +167,7 @@
|
||||
});
|
||||
//发布文档
|
||||
createDoc = function(){
|
||||
layer.load();
|
||||
var data = {
|
||||
'doc_id':{{ doc.id }},
|
||||
'project':$("#project").val(),
|
||||
@ -171,12 +180,14 @@
|
||||
}
|
||||
$.post("{% url 'modify_doc' doc_id=doc.id %}",data,function(r){
|
||||
if(r.status){
|
||||
layer.closeAll("loading");
|
||||
//修改成功
|
||||
layer.msg('发布成功,即将跳转',function(){
|
||||
md_changed = false;
|
||||
window.location.href = "{% url 'doc' pro_id=doc.top_doc doc_id=doc.id %}";
|
||||
});
|
||||
}else{
|
||||
layer.closeAll("loading");
|
||||
//修改失败
|
||||
layer.msg('保存失败');
|
||||
}
|
||||
@ -184,6 +195,7 @@
|
||||
};
|
||||
//保存草稿
|
||||
saveDoc = function(){
|
||||
layer.load();
|
||||
var data = {
|
||||
'doc_id':{{ doc.id }},
|
||||
'project':$("#project").val(),
|
||||
@ -197,11 +209,13 @@
|
||||
$.post("{% url 'modify_doc' doc_id=doc.id %}",data,function(r){
|
||||
if(r.status){
|
||||
//修改成功
|
||||
layer.closeAll("loading");
|
||||
layer.msg('保存成功',function(){
|
||||
md_changed = false;
|
||||
window.location.href = "{% url 'modify_doc' doc.id %}";
|
||||
});
|
||||
}else{
|
||||
layer.closeAll("loading");
|
||||
//修改失败
|
||||
layer.msg('保存失败');
|
||||
}
|
||||
@ -218,20 +232,46 @@
|
||||
});
|
||||
//插入模板
|
||||
insertTemp = function(doctemp_id){
|
||||
layer.load();
|
||||
$.post("{% url 'get_doctemp' %}",{'doctemp_id':doctemp_id},function(r){
|
||||
if(r.status){
|
||||
editor.insertValue(r.data);
|
||||
layer.closeAll()
|
||||
}else{
|
||||
layer.closeAll("loading");
|
||||
layer.msg(r.data)
|
||||
}
|
||||
});
|
||||
};
|
||||
// 查看历史记录
|
||||
$("#doc-history").click(function(){
|
||||
layer.open({
|
||||
type: 1,
|
||||
id:'history-div',
|
||||
content: $('#history-list'),
|
||||
area:['530px','300px'],
|
||||
});
|
||||
});
|
||||
insertHistory = function(doc_id,history_id){
|
||||
layer.load(1);
|
||||
var url = "{% url 'diff_doc' 0 1 %}";
|
||||
url = url.replace(0,doc_id).replace(1,history_id)
|
||||
$.post(url,function(r){
|
||||
if(r.status){
|
||||
editor.setMarkdown(r.data);
|
||||
layer.closeAll()
|
||||
}else{
|
||||
layer.closeAll("loading");
|
||||
layer.msg(r.data)
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block custom_div %}
|
||||
<div class="doctemp-list" id="doctemp-list" style="display: none;width: 500px;">
|
||||
<!-- 模板div块 -->
|
||||
<div class="doctemp-list" id="doctemp-list" style="display: none;width: 500px;">
|
||||
<div style="margin: 10px 0 0 10px;">
|
||||
<a class="layui-btn layui-btn-normal" href="{% url 'create_doctemp' %}" target="_blank">创建新模板</a>
|
||||
<a class="layui-btn layui-btn-normal" href="{% url 'manage_doctemp' %}" target="_blank">管理文档模板</a>
|
||||
@ -256,8 +296,33 @@
|
||||
<td>{{ temp.create_time }}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0);" class="layui-btn layui-btn-normal layui-btn-sm" onclick="insertTemp('{{temp.id}}');">选择模板</a>
|
||||
{# <a href="javascript:void(0);" onclick="modifyTemp();">修改</a>#}
|
||||
{# <a href="javascript:void(0);" onclick="delTemp();">删除</a>#}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- 文档历史div块 -->
|
||||
<div class="history-list" id="history-list" style="display: none;width: 500px;">
|
||||
<table class="layui-table" style="margin: 10px;" lay-size='sm'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>创建时间</th>
|
||||
<th>用户</th>
|
||||
<th>对比</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for history in history_list %}
|
||||
<tr>
|
||||
<td>{{ history.create_time }}</td>
|
||||
<td>{{ history.create_user }}</td>
|
||||
<td>
|
||||
<a href="{% url 'diff_doc' history.doc.id history.id %}" target="_blank">查看差异</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="javascript:void(0);" class="layui-btn layui-btn-normal layui-btn-sm" onclick="insertHistory('{{history.doc.id}}','{{history.id}}');">恢复此版本</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -59,6 +59,7 @@
|
||||
<script>
|
||||
//修改文档模板
|
||||
modifyDocTemp = function(){
|
||||
layer.load();
|
||||
var data = {
|
||||
'doctemp_id':{{ doctemp.id }},
|
||||
'name':$("#doctemp-name").val(),
|
||||
@ -67,12 +68,14 @@
|
||||
$.post("{% url 'modify_doctemp' doctemp_id=doctemp.id %}",data,function(r){
|
||||
if(r.status){
|
||||
//修改成功
|
||||
layer.closeAll("loading");
|
||||
layer.msg('修改成功,即将跳转',function(){
|
||||
md_changed = false;
|
||||
window.location.href = "{% url 'manage_doctemp' %}";
|
||||
});
|
||||
}else{
|
||||
//创建失败
|
||||
layer.closeAll("loading");
|
||||
layer.msg('保存失败');
|
||||
}
|
||||
});
|
||||
|
@ -7,8 +7,8 @@
|
||||
<meta http-equiv="Cache-Control" content="no-transform" />
|
||||
<meta http-equiv="Cache-Control" content="no-siteapp" />
|
||||
<meta http-equiv="Cache-Control" content="max-age=7200" />
|
||||
<meta name="keywords" content="mrdoc,markdown,文档写作,在线教程"/>
|
||||
<meta name="description" content="MrDoc是由州的先生(zmister.com)开发的,基于Python的Django框架的在线MarkDown文档写作系统。" />
|
||||
<meta name="keywords" content="mrdoc,markdown,文档写作,在线教程,Python文档系统,django应用"/>
|
||||
<meta name="description" content="MrDoc是由州的先生(zmister.com)开发,基于Python的Django框架的在线文档写作系统,适合作为个人和小型团队的文档、笔记和知识管理工具。" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title>MrDoc - 一个简单的文档写作系统</title>
|
||||
<link href="{% static 'layui/css/layui.css' %}" rel="stylesheet">
|
||||
|
@ -12,6 +12,11 @@
|
||||
<i class="fa fa-plus-square"></i> 添加
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if request.user == project.create_user %}
|
||||
<a class="btn pull-left" href="{% url 'manage_project' %}" target="_blank">
|
||||
<i class="fa fa-cubes"></i> 管理
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content_head %}
|
||||
|
Loading…
x
Reference in New Issue
Block a user