Selaa lähdekoodia

增加多媒体阅卷功能
1.替换rich-text模块的css和独立的渲染js方法,挪到static下独立的rich-text文件夹
2.修改mark-json下的json-builder改为json-loader,只承担json文件的抓取功能;新增json-view作为single-image-view的替代,实际执行json内容的渲染工作

luoshi 5 vuotta sitten
vanhempi
commit
2d6eee9004

+ 84 - 82
stmms-web/src/main/webapp/WEB-INF/views/modules/mark/markJson.jsp

@@ -1,98 +1,100 @@
-<%@ page language="java" pageEncoding="utf-8"%>
-<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
+<%@ page language="java" pageEncoding="utf-8" %>
+<%@ include file="/WEB-INF/views/include/taglib.jsp" %>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <head>
-<title>云阅卷高校版</title>
-<link href="${ctxStatic}/mark-new/css/bootstrap.css" rel="stylesheet" type="text/css" />
-<link href="${ctxStatic}/mark-new/css/style.css" rel="stylesheet" type="text/css" />
-<link href="${ctxStatic}/mark-json/css/style.css" rel="stylesheet" type="text/css" />
-<link href="${ctxStatic}/mark-json/css/rich-text.css" rel="stylesheet" type="text/css" />
+    <title>云阅卷高校版</title>
+    <link href="${ctxStatic}/mark-new/css/bootstrap.css" rel="stylesheet" type="text/css"/>
+    <link href="${ctxStatic}/mark-new/css/style.css" rel="stylesheet" type="text/css"/>
+    <link href="${ctxStatic}/mark-json/css/style.css" rel="stylesheet" type="text/css"/>
+    <link href="${ctxStatic}/mark-json/css/rich-text.css" rel="stylesheet" type="text/css"/>
 
 
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/json2.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/json2.js"></script>
 
 
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery.min.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery-ui.min.js "></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery.mousewheel.min.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery.min.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery-ui.min.js "></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery.mousewheel.min.js"></script>
 
 
-<script type="text/javascript" src="${ctxStatic}/iviewer/jquery.iviewer.js"></script>
-<link rel="stylesheet" href="${ctxStatic}/iviewer/jquery.iviewer.css" rel="stylesheet" />
+    <script type="text/javascript" src="${ctxStatic}/iviewer/jquery.iviewer.js"></script>
+    <link rel="stylesheet" href="${ctxStatic}/iviewer/jquery.iviewer.css" rel="stylesheet"/>
 
 
-<script src="${ctxStatic}/perfect-scrollbar/min/perfect-scrollbar.min.js"></script>
-<link href="${ctxStatic}/perfect-scrollbar/min/perfect-scrollbar.min.css" rel="stylesheet">
+    <script src="${ctxStatic}/perfect-scrollbar/min/perfect-scrollbar.min.js"></script>
+    <link href="${ctxStatic}/perfect-scrollbar/min/perfect-scrollbar.min.css" rel="stylesheet">
 
 
-<script type="text/javascript" src="${ctxStatic}/mark-json/js/mark-control.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-json/js/json-builder.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/task-control.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/paper-view.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/image-builder.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/mark-board.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/mark-history.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/mark-status.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/warning-info.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/change-name.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/tag-process.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/view-sidebar.js"></script>
+    <script src="${ctxStatic}/rich-text/js/render.js"></script>
+    <link href="${ctxStatic}/rich-text/css/rich-text.css" rel="stylesheet">
+
+    <script type="text/javascript" src="${ctxStatic}/mark-json/js/mark-control.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-json/js/json-view.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-json/js/json-loader.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/task-control.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/paper-view.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/image-builder.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/mark-board.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/mark-history.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/mark-status.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/warning-info.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/change-name.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/tag-process.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/view-sidebar.js"></script>
 
 
 </head>
 </head>
 <body>
 <body>
-	<div class="container-fluid" id="container"></div>
-	<script type="text/javascript">
-		$(document).ready(function() {
-			var mc = new MarkControl({
-				container : $('#container'),
-				staticServer : '${ctxStatic}',
-				imageServer : '${sliceServer}',
-				userId: '${marker.id}',
-				userName : '${web_user.name}',
-				logoutUrl: '${ctx}/mark/logout',
-				//clearUrl: '${ctx}/mark/clear',
-			/* 	<c:if test="${forceMode==false}">
+<div class="container-fluid" id="container"></div>
+<script type="text/javascript">
+    $(document).ready(function () {
+        var mc = new MarkControl({
+            container: $('#container'),
+            staticServer: '${ctxStatic}',
+            imageServer: '${sliceServer}',
+            userId: '${marker.id}',
+            userName: '${web_user.name}',
+            logoutUrl: '${ctx}/mark/logout',
+            //clearUrl: '${ctx}/mark/clear',
+            /* 	<c:if test="${forceMode==false}">
 				switchTrackUrl: '${ctx}/mark/index?mode=track',
 				switchTrackUrl: '${ctx}/mark/index?mode=track',
 				</c:if>
 				</c:if>
 				forceSpecialTag : eval('${forceSpecialTag}'.toLowerCase()), */
 				forceSpecialTag : eval('${forceSpecialTag}'.toLowerCase()), */
-				<c:if test="${defaultSetting!=null}">
-				defaultSetting: '${defaultSetting}',
-				</c:if>
-				settingSyncUrl: '${ctx}/mark/update-setting',
-				modules : {
-					'json-builder': {
-						answerServer : '${answerServer}'
-					},
-					'mark-status': {
-						simple : false,
-						subjectName : '${subject.displayName}'
-					},
-					'mark-history':{
-						pageSize:10
-					},
-					'mark-board' : {
-						showScoreBoard : false,
-						autoSubmit : false,
-						needConfirm : false,
-					},
-					'warning-info': {
-					},
-					'change-name':{
-						url : '${ctx}/mark/change-name'
-					},
-					'view-sidebar':{
-						list: [
-							{title:'试卷',  url:'<c:if test="${subject.hasPaper==true}">${cardServer}${subject.paperUrl}</c:if>'},
-							{title:'答案',  url:'<c:if test="${subject.hasAnswer==true}">${cardServer}${subject.answerUrl}</c:if>'}
-						]
-					}
-				}
-			});
-			mc.start({
-				mode : 'loop',
-				statusUrl : '${ctx}/mark/status',
-				getUrl : '${ctx}/mark/gettask',
-				historyUrl : '${ctx}/mark/gethistory',
-				submitUrl : '${ctx}/mark/savetask'
-				//clearUrl : '${ctx}/mark/clear'
-			});
-		});
-	</script>
+            <c:if test="${defaultSetting!=null}">
+            defaultSetting: '${defaultSetting}',
+            </c:if>
+            settingSyncUrl: '${ctx}/mark/update-setting',
+            modules: {
+                'json-loader': {},
+                'json-view': {},
+                'mark-status': {
+                    simple: false,
+                    subjectName: '${subject.displayName}'
+                },
+                'mark-history': {
+                    pageSize: 10
+                },
+                'mark-board': {
+                    showScoreBoard: false,
+                    autoSubmit: false,
+                    needConfirm: false,
+                },
+                'warning-info': {},
+                'change-name': {
+                    url: '${ctx}/mark/change-name'
+                },
+                'view-sidebar': {
+                    list: [
+                        {title: '试卷', url: '<c:if test="${subject.hasPaper==true}">${cardServer}${subject.paperUrl}</c:if>'},
+                        {title: '答案', url: '<c:if test="${subject.hasAnswer==true}">${cardServer}${subject.answerUrl}</c:if>'}
+                    ]
+                }
+            }
+        });
+        mc.start({
+            mode: 'loop',
+            statusUrl: '${ctx}/mark/status',
+            getUrl: '${ctx}/mark/gettask',
+            historyUrl: '${ctx}/mark/gethistory',
+            submitUrl: '${ctx}/mark/savetask'
+            //clearUrl : '${ctx}/mark/clear'
+        });
+    });
+</script>
 </body>
 </body>
 </html>
 </html>

+ 0 - 45
stmms-web/src/main/webapp/static/mark-json/css/rich-text.css

@@ -1,45 +0,0 @@
-/*rich-text*/
-* {
-    font-family: "微软雅黑", "苹方", Arial, Helvetica, sans-serif;
-}
-
-.rich-text {
-    padding: 10px 0;
-}
-
-.rich-text p {
-    font-size: 16px;
-    line-height: 2em;
-    margin: 10px 0;
-    word-break: break-all;
-}
-
-.rich-text img.inline {
-    width: auto;
-    max-height: 2em;
-}
-
-.rich-text p span.sup {
-    vertical-align: super;
-    font-size: 9px;
-    font-family: Arial, Helvetica, sans-serif;
-    margin: 10px 0px 0px 0px
-}
-
-.rich-text p span.sub {
-    vertical-align: sub;
-    font-size: 9px;
-    font-family: Arial, Helvetica, sans-serif;
-}
-
-.rich-text p span.bold {
-    font-weight: 700;
-}
-
-.rich-text p span.underline {
-    text-decoration: underline;
-}
-
-.rich-text p audio {
-    height: 2em;
-}

+ 0 - 56
stmms-web/src/main/webapp/static/mark-json/css/style.css

@@ -1,56 +0,0 @@
-
-/*======common======*/
-* {
-	font-family: "微软雅黑","苹方";
-	margin: 0;
-	padding: 0;
-	list-style: none;
-	outline: none;
-	-webkit-tap-highlight-color: transparent;
-	-webkit-box-sizing: border-box;
-	box-sizing: border-box;
-}
-html, body {
-	height: 100%;
-	background: #f5f5f5;
-	color: #444;
-}
-a {
-	text-decoration: none;
-}
-.ellipsis {
-	text-overflow: ellipsis;
-	overflow: hidden;
-	white-space: nowrap;
-}
-.cl:after {
-	content: ".";
-	display: block;
-	height: 0;
-	clear: both;
-	visibility: hidden;
-}
-.cl {
-	zoom: 1;
-}
-.wp {
-	width: 1000px;
-	overflow: hidden;
-	margin: 50px auto;
-	padding: 20px 40px;
-	background: #FFF;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-

+ 0 - 77
stmms-web/src/main/webapp/static/mark-json/js/json-builder.js

@@ -1,77 +0,0 @@
-//多媒体显示模块
-var json_builder = function(option, success) {
-    var object = new JsonBuilder(option);
-    success();
-    return object;
-}
-
-function JsonBuilder(option) {
-    this.markControl = option.markControl;
-    this.answerServer = option.answerServer;
-    this.init();
-    this.markControl.on('step.board.show', this, function(event, context, eventObject) {
-        this.container.removeClass('span12');
-        this.container.addClass('span10');
-    });
-    this.markControl.on('step.board.hide', this, function(event, context, eventObject) {
-        this.container.removeClass('span10');
-        this.container.addClass('span12');
-    });
-    this.markControl.on('task.get.before', this, function(event, context, eventObject) {
-        this.task = undefined;
-        this.json = undefined;
-    });
-    this.markControl.on('task.get.success', this, function(event, context, eventObject) {
-        this.task = context.task;
-        this.json = undefined;
-    });
-    this.markControl.on('task.get.none', this, function(event, context, eventObject) {
-        this.task = undefined;
-        this.json = undefined;
-    });
-    this.markControl.on('image.position.change', this, function(event, context, topPercent) {
-        if (this.task != undefined) {
-            this.updateScrollTop(topPercent);
-        }
-    });
-}
-
-JsonBuilder.prototype.init = function() {
-    var self = this;
-    this.container = this.markControl.container.imageContent;
-    this.container.height(this.markControl.container.centerContent.height());
-    this.container.css('overflow', 'scroll');
-    
-    this.container = getDom(this.control_dom, this.markControl).appendTo(this.markControl.container);
-    this.container.hide();
-
-}
-
-JsonBuilder.prototype.build = function(task,callback) {
-    var self = this;
-    if (this.task != undefined && this.task.answerUrl != undefined) {
-    	var url = this.answerServer + this.task.answerUrl;
-    	$.get(url, function(result) {
-    		
-    		self.container.show();
-        	self.markControl.trigger('task.load.finish');
-        }).error(function() {
-        	 callback('image load error');
-        });
-    }
-}
-
-JsonBuilder.prototype.updateScrollTop = function(scrollTopPercent) {
-    var height = this.canvas.height;
-    var minHeight = this.container.height();
-    if (scrollTopPercent != undefined && scrollTopPercent >= 0 && scrollTopPercent <= 1 && height > minHeight) {
-        var left = height * (1 - scrollTopPercent);
-        var scrollTop = left > minHeight ? (height - left) : (height - minHeight);
-        this.container.scrollTop(scrollTop);
-    } else {
-        this.container.scrollTop(0);
-    }
-}
-
-JsonBuilder.prototype.control_dom = '<div class="wp"><div class="rich-text"></div></div>';
-JsonBuilder.prototype.rich_text_dom = '<div class="rich-text"></div>';

+ 23 - 0
stmms-web/src/main/webapp/static/mark-json/js/json-loader.js

@@ -0,0 +1,23 @@
+//多媒体显示模块
+var json_loader = function (option, success) {
+    var object = new JsonLoader(option);
+    success();
+    return object;
+}
+
+function JsonLoader(option) {
+    this.markControl = option.markControl;
+}
+
+JsonLoader.prototype.build = function (task, callback) {
+    var self = this;
+    if (this.task != undefined && this.task.answerUrl != undefined) {
+        var url = this.answerServer + this.task.answerUrl;
+        $.get(url, function (result) {
+            self.task.jsonData = result;
+            callback();
+        }).error(function () {
+            callback('json load error');
+        });
+    }
+}

+ 57 - 0
stmms-web/src/main/webapp/static/mark-json/js/json-view.js

@@ -0,0 +1,57 @@
+//JSON结构化数据显示模块
+var json_view = function (option, success) {
+    var object = new JsonView(option);
+    success();
+    return object;
+}
+
+function JsonView(option) {
+    this.markControl = option.markControl;
+    this.init();
+    this.markControl.on('step.board.show', this, function (event, context, eventObject) {
+        this.container.removeClass('span12');
+        this.container.addClass('span10');
+    });
+    this.markControl.on('step.board.hide', this, function (event, context, eventObject) {
+        this.container.removeClass('span10');
+        this.container.addClass('span12');
+    });
+    this.markControl.on('task.get.before', this, function (event, context, eventObject) {
+        this.task = undefined;
+        this.render();
+    });
+    this.markControl.on('task.get.success', this, function (event, context, eventObject) {
+        this.task = context.task;
+        this.render();
+    });
+    this.markControl.on('task.get.none', this, function (event, context, eventObject) {
+        this.task = undefined;
+        this.render();
+    });
+    this.markControl.on('mark.setting.init', this, function (event, context, setting) {
+        var scale = setting['image.view.scale'];
+        if (scale != undefined) {
+            this.scale = Number(scale);
+        }
+    });
+}
+
+JsonView.prototype.init = function () {
+    var self = this;
+    this.container = this.markControl.container.imageContent;
+    this.container.height(this.markControl.container.centerContent.height());
+    this.container.css('overflow', 'scroll');
+    this.container.addClass('rich-text');
+}
+
+JsonView.prototype.render = function () {
+    this.container.empty();
+    if (this.task != undefined && this.task.jsonData != undefined) {
+        let questions = this.task.jsonData || [];
+        questions.forEach(question => {
+            // TODO-增加按题号过滤
+            $(renderRichText(question.body)).appendTo(this.container);
+        });
+        this.markControl.trigger('task.load.finish');
+    }
+}

+ 83 - 81
stmms-web/src/main/webapp/static/mark-json/js/mark-control.js

@@ -27,7 +27,7 @@ function MarkControl(option) {
     //}
     //}
 }
 }
 
 
-MarkControl.prototype.initContainer = function() {
+MarkControl.prototype.initContainer = function () {
 
 
     var height = this.option.height;
     var height = this.option.height;
     if (height == undefined) {
     if (height == undefined) {
@@ -58,13 +58,13 @@ MarkControl.prototype.initContainer = function() {
     this.navNumber = 0;
     this.navNumber = 0;
 }
 }
 
 
-MarkControl.prototype.initHeaderAndAssistant = function() {
+MarkControl.prototype.initHeaderAndAssistant = function () {
     var self = this;
     var self = this;
     this.container.header.find('#mark-user-name').html(this.option.userName);
     this.container.header.find('#mark-user-name').html(this.option.userName);
     if (this.option.logoutTitle != undefined) {
     if (this.option.logoutTitle != undefined) {
         this.container.header.find('#logout-title').html(this.option.logoutTitle);
         this.container.header.find('#logout-title').html(this.option.logoutTitle);
     }
     }
-    this.container.header.find('#logout-link').click(function() {
+    this.container.header.find('#logout-link').click(function () {
         self.trigger('logout.link.click');
         self.trigger('logout.link.click');
         return true;
         return true;
     })
     })
@@ -73,7 +73,7 @@ MarkControl.prototype.initHeaderAndAssistant = function() {
     this.container.assistant.positionSet = false;
     this.container.assistant.positionSet = false;
     this.container.assistantButton = this.container.header.find('#assistant-button');
     this.container.assistantButton = this.container.header.find('#assistant-button');
 
 
-    this.container.assistantButton.click(this, function(event) {
+    this.container.assistantButton.click(this, function (event) {
         if (self.container.assistant.positionSet == false) {
         if (self.container.assistant.positionSet == false) {
             self.container.assistant.offset({
             self.container.assistant.offset({
                 top: self.container.assistantButton.position().top + self.container.assistantButton.height() + 5,
                 top: self.container.assistantButton.position().top + self.container.assistantButton.height() + 5,
@@ -86,7 +86,7 @@ MarkControl.prototype.initHeaderAndAssistant = function() {
 
 
 }
 }
 
 
-MarkControl.prototype.initMarkFunction = function() {
+MarkControl.prototype.initMarkFunction = function () {
     var functionList = this.container.assistant.functionList;
     var functionList = this.container.assistant.functionList;
     if (functionList == undefined) {
     if (functionList == undefined) {
         functionList = getDom(this.mark_function_dom, this).appendTo(this.container.assistant).find('#function-list');
         functionList = getDom(this.mark_function_dom, this).appendTo(this.container.assistant).find('#function-list');
@@ -94,7 +94,7 @@ MarkControl.prototype.initMarkFunction = function() {
     }
     }
 }
 }
 
 
-MarkControl.prototype.initSetting = function(option) {
+MarkControl.prototype.initSetting = function (option) {
     this.userId = option.userId;
     this.userId = option.userId;
     this.settingSyncUrl = option.settingSyncUrl;
     this.settingSyncUrl = option.settingSyncUrl;
     this.settingSyncing = false;
     this.settingSyncing = false;
@@ -117,7 +117,7 @@ MarkControl.prototype.initSetting = function(option) {
     this.trigger('mark.setting.init', this.setting);
     this.trigger('mark.setting.init', this.setting);
 }
 }
 
 
-MarkControl.prototype.localSettingSave = function() {
+MarkControl.prototype.localSettingSave = function () {
     if (this.localStore != undefined) {
     if (this.localStore != undefined) {
         this.localStore.setItem('mark.setting', JSON.stringify({
         this.localStore.setItem('mark.setting', JSON.stringify({
             userId: this.userId,
             userId: this.userId,
@@ -126,7 +126,7 @@ MarkControl.prototype.localSettingSave = function() {
     }
     }
 }
 }
 
 
-MarkControl.prototype.updateSetting = function(item) {
+MarkControl.prototype.updateSetting = function (item) {
     if (item == undefined) {
     if (item == undefined) {
         return;
         return;
     }
     }
@@ -137,7 +137,7 @@ MarkControl.prototype.updateSetting = function(item) {
     if (this.settingSyncUrl != undefined && this.settingSyncing == false) {
     if (this.settingSyncUrl != undefined && this.settingSyncing == false) {
         this.settingSyncing = true;
         this.settingSyncing = true;
         var self = this;
         var self = this;
-        setTimeout(function() {
+        setTimeout(function () {
             //同步个性化设置
             //同步个性化设置
             $.post(self.settingSyncUrl, {
             $.post(self.settingSyncUrl, {
                 setting: JSON.stringify(self.setting)
                 setting: JSON.stringify(self.setting)
@@ -148,8 +148,8 @@ MarkControl.prototype.updateSetting = function(item) {
 }
 }
 
 
 //增加某个事件的监听方法
 //增加某个事件的监听方法
-MarkControl.prototype.on = function(eventName, caller, callback, async) {
-    if (eventName && callback && eventName.length > 0 && typeof(callback) == 'function') {
+MarkControl.prototype.on = function (eventName, caller, callback, async) {
+    if (eventName && callback && eventName.length > 0 && typeof (callback) == 'function') {
         if (async) {
         if (async) {
             this.container.bind(eventName, callback);
             this.container.bind(eventName, callback);
         } else {
         } else {
@@ -165,7 +165,7 @@ MarkControl.prototype.on = function(eventName, caller, callback, async) {
 }
 }
 
 
 //触发某个事件,并传递事件相关内容
 //触发某个事件,并传递事件相关内容
-MarkControl.prototype.trigger = function(eventName, eventObject) {
+MarkControl.prototype.trigger = function (eventName, eventObject) {
     var result = true;
     var result = true;
     if (eventName && eventName.length > 0) {
     if (eventName && eventName.length > 0) {
         var array = this.triggers[eventName];
         var array = this.triggers[eventName];
@@ -183,7 +183,7 @@ MarkControl.prototype.trigger = function(eventName, eventObject) {
 }
 }
 
 
 //初始化事件监听
 //初始化事件监听
-MarkControl.prototype.initTriggers = function(option) {
+MarkControl.prototype.initTriggers = function (option) {
     if (this.triggers == undefined) {
     if (this.triggers == undefined) {
         this.triggers = {};
         this.triggers = {};
     }
     }
@@ -194,7 +194,7 @@ MarkControl.prototype.initTriggers = function(option) {
     }
     }
 
 
     var self = this;
     var self = this;
-    this.on('view.sidebar.open', this, function(event, context, eventObject) {
+    this.on('view.sidebar.open', this, function (event, context, eventObject) {
         this.container.assistant.hide();
         this.container.assistant.hide();
         if (this.container.center.hasClass('span12')) {
         if (this.container.center.hasClass('span12')) {
             this.container.center.removeClass('span12');
             this.container.center.removeClass('span12');
@@ -205,7 +205,7 @@ MarkControl.prototype.initTriggers = function(option) {
         }
         }
         this.trigger('center.width.change');
         this.trigger('center.width.change');
     });
     });
-    this.on('view.sidebar.close', this, function(event, context, eventObject) {
+    this.on('view.sidebar.close', this, function (event, context, eventObject) {
         this.container.assistant.hide();
         this.container.assistant.hide();
         if (this.container.center.hasClass('span7')) {
         if (this.container.center.hasClass('span7')) {
             this.container.center.removeClass('span7');
             this.container.center.removeClass('span7');
@@ -216,7 +216,7 @@ MarkControl.prototype.initTriggers = function(option) {
         }
         }
         this.trigger('center.width.change');
         this.trigger('center.width.change');
     });
     });
-    this.on('mark.sidebar.open', this, function(event, context, eventObject) {
+    this.on('mark.sidebar.open', this, function (event, context, eventObject) {
         this.container.assistant.hide();
         this.container.assistant.hide();
         if (this.container.center.hasClass('span12')) {
         if (this.container.center.hasClass('span12')) {
             this.container.center.removeClass('span12');
             this.container.center.removeClass('span12');
@@ -227,7 +227,7 @@ MarkControl.prototype.initTriggers = function(option) {
         }
         }
         this.trigger('center.width.change');
         this.trigger('center.width.change');
     });
     });
-    this.on('mark.sidebar.close', this, function(event, context, eventObject) {
+    this.on('mark.sidebar.close', this, function (event, context, eventObject) {
         this.container.assistant.hide();
         this.container.assistant.hide();
         if (this.container.center.hasClass('span10')) {
         if (this.container.center.hasClass('span10')) {
             this.container.center.removeClass('span10');
             this.container.center.removeClass('span10');
@@ -238,68 +238,70 @@ MarkControl.prototype.initTriggers = function(option) {
         }
         }
         this.trigger('center.width.change');
         this.trigger('center.width.change');
     });
     });
-    this.on('task.load.finish', this, function(event, context, eventObject) {
+    this.on('task.load.finish', this, function (event, context, eventObject) {
         if (context.task != undefined) {
         if (context.task != undefined) {
             context.task.spent = new Date().getTime();
             context.task.spent = new Date().getTime();
         }
         }
     });
     });
-    this.on('mark.focus.change', this, function(event, context, eventObject) {});
-    this.on('task.get.finish', this, function(event, context, eventObject) {
+    this.on('mark.focus.change', this, function (event, context, eventObject) {
+    });
+    this.on('task.get.finish', this, function (event, context, eventObject) {
         context.prefetchCallback = undefined;
         context.prefetchCallback = undefined;
     });
     });
-    this.on('task.get.none', this, function(event, context, eventObject) {
+    this.on('task.get.none', this, function (event, context, eventObject) {
         self.getStatus();
         self.getStatus();
         if (context.task == undefined && self.option.clearUrl != undefined) {
         if (context.task == undefined && self.option.clearUrl != undefined) {
-            $.post(self.option.clearUrl, {}, function() {});
+            $.post(self.option.clearUrl, {}, function () {
+            });
         }
         }
-        context.prefetchCallback = function() {
+        context.prefetchCallback = function () {
             context.prefetchCallback = undefined;
             context.prefetchCallback = undefined;
             if (self.context.task == undefined && self.context.prefetchTask.length > 0) {
             if (self.context.task == undefined && self.context.prefetchTask.length > 0) {
                 self.getTask();
                 self.getTask();
             }
             }
         }
         }
     });
     });
-    this.on('task.prefetch.success', this, function(event, context, eventObject) {
+    this.on('task.prefetch.success', this, function (event, context, eventObject) {
         if (context.prefetchCallback != undefined) {
         if (context.prefetchCallback != undefined) {
             context.prefetchCallback();
             context.prefetchCallback();
         }
         }
-        setTimeout(function() {
+        setTimeout(function () {
             self.prefetch();
             self.prefetch();
         }, 500);
         }, 500);
     });
     });
-    this.on('task.prefetch.error', this, function(event, context, eventObject) {
-        setTimeout(function() {
+    this.on('task.prefetch.error', this, function (event, context, eventObject) {
+        setTimeout(function () {
             self.prefetch();
             self.prefetch();
         }, 500);
         }, 500);
     });
     });
-    this.on('task.prefetch.none', this, function(event, context, eventObject) {
+    this.on('task.prefetch.none', this, function (event, context, eventObject) {
         if (context.prefetchCallback != undefined) {
         if (context.prefetchCallback != undefined) {
             context.prefetchCallback();
             context.prefetchCallback();
         }
         }
         if (context.isFinish != true) {
         if (context.isFinish != true) {
-            setTimeout(function() {
+            setTimeout(function () {
                 self.prefetch();
                 self.prefetch();
             }, 1000);
             }, 1000);
         }
         }
     });
     });
-    this.on('task.prefetch.finish', this, function(event, context, eventObject) {
+    this.on('task.prefetch.finish', this, function (event, context, eventObject) {
         self.getTask();
         self.getTask();
     });
     });
-    this.on('task.submit.before', this, function(event, context, eventObject) {
+    this.on('task.submit.before', this, function (event, context, eventObject) {
         context.submitting = true;
         context.submitting = true;
     });
     });
-    this.on('history.submit.success', this, function(event, context, eventObject) {
+    this.on('history.submit.success', this, function (event, context, eventObject) {
         context.submitting = false;
         context.submitting = false;
     });
     });
-    this.on('task.submit.success', this, function(event, context, eventObject) {
+    this.on('task.submit.success', this, function (event, context, eventObject) {
         context.submitting = false;
         context.submitting = false;
         context.task = undefined;
         context.task = undefined;
         self.getTask();
         self.getTask();
     });
     });
-    this.on('task.submit.error', this, function(event, context, eventObject) {
+    this.on('task.submit.error', this, function (event, context, eventObject) {
         context.submitting = false;
         context.submitting = false;
     });
     });
-    this.on('task.pass.success', this, function(event, context, eventObject) {
+    this.on('task.pass.success', this, function (event, context, eventObject) {
         if (context.task != undefined && self.option.clearUrl != undefined) {
         if (context.task != undefined && self.option.clearUrl != undefined) {
             $.post(self.option.clearUrl, {
             $.post(self.option.clearUrl, {
                 libraryId: context.task.libraryId
                 libraryId: context.task.libraryId
@@ -308,26 +310,26 @@ MarkControl.prototype.initTriggers = function(option) {
         context.task = undefined;
         context.task = undefined;
         self.getTask();
         self.getTask();
     });
     });
-    this.on('mark.setting.change', this, function(event, context, eventObject) {
+    this.on('mark.setting.change', this, function (event, context, eventObject) {
         self.updateSetting(eventObject);
         self.updateSetting(eventObject);
     });
     });
-    $(document).keypress(this, function(event) {
+    $(document).keypress(this, function (event) {
         if (self.context.listenKeyboard != false) {
         if (self.context.listenKeyboard != false) {
             return self.trigger('key.press', event);
             return self.trigger('key.press', event);
         }
         }
     });
     });
-    $(document).keydown(this, function(event) {
+    $(document).keydown(this, function (event) {
         if (self.context.listenKeyboard != false) {
         if (self.context.listenKeyboard != false) {
             return self.trigger('key.down', event);
             return self.trigger('key.down', event);
         }
         }
     });
     });
-    $(document).keyup(this, function(event) {
+    $(document).keyup(this, function (event) {
         if (self.context.listenKeyboard != false) {
         if (self.context.listenKeyboard != false) {
             return self.trigger('key.up', event);
             return self.trigger('key.up', event);
         }
         }
     });
     });
 
 
-    window.onbeforeunload = function(e) {
+    window.onbeforeunload = function (e) {
         if (self.option.clearUrl != undefined) {
         if (self.option.clearUrl != undefined) {
             $.post(self.option.clearUrl);
             $.post(self.option.clearUrl);
         }
         }
@@ -335,7 +337,7 @@ MarkControl.prototype.initTriggers = function(option) {
 }
 }
 
 
 //初始化功能模块
 //初始化功能模块
-MarkControl.prototype.initModules = function(option) {
+MarkControl.prototype.initModules = function (option) {
     if (this.modules == undefined) {
     if (this.modules == undefined) {
         this.modules = {};
         this.modules = {};
     }
     }
@@ -361,18 +363,18 @@ MarkControl.prototype.initModules = function(option) {
 }
 }
 
 
 //指定初始化某个名称的模块
 //指定初始化某个名称的模块
-MarkControl.prototype.initModule = function(names, options, success) {
+MarkControl.prototype.initModule = function (names, options, success) {
     for (var i in names) {
     for (var i in names) {
         var name = names[i];
         var name = names[i];
         var option = options[name];
         var option = options[name];
         var moduleInit = name.replace(/-/g, '_');
         var moduleInit = name.replace(/-/g, '_');
-        if (option == undefined || typeof(option) != 'object') {
+        if (option == undefined || typeof (option) != 'object') {
             option = {};
             option = {};
         }
         }
         option.markControl = this;
         option.markControl = this;
         eval('this.modules[name]=' + moduleInit + '(option, function(){})');
         eval('this.modules[name]=' + moduleInit + '(option, function(){})');
     }
     }
-    if (success != undefined && typeof(success) == 'function') {
+    if (success != undefined && typeof (success) == 'function') {
         success();
         success();
     }
     }
 }
 }
@@ -385,12 +387,12 @@ function initModuleAsync(markControl, names, index, option) {
         var moduleInit = name.replace(/-/g, '_');
         var moduleInit = name.replace(/-/g, '_');
         var modules = markControl.modules;
         var modules = markControl.modules;
         if (modules[name] == undefined) {
         if (modules[name] == undefined) {
-            if (typeof(moduleOption) != 'object') {
+            if (typeof (moduleOption) != 'object') {
                 moduleOption = {};
                 moduleOption = {};
             }
             }
             moduleOption.markControl = markControl;
             moduleOption.markControl = markControl;
-            $.getScript(moduleUrl, function() {
-                var success = function() {
+            $.getScript(moduleUrl, function () {
+                var success = function () {
                     initModule(markControl, names, index + 1, option);
                     initModule(markControl, names, index + 1, option);
                 }
                 }
                 eval('modules[name]=' + moduleInit + '(moduleOption, success)');
                 eval('modules[name]=' + moduleInit + '(moduleOption, success)');
@@ -399,27 +401,27 @@ function initModuleAsync(markControl, names, index, option) {
             initModule(markControl, names, index + 1, option);
             initModule(markControl, names, index + 1, option);
         }
         }
     } else {
     } else {
-        if (markControl.option.success != undefined && typeof(markControl.option.success) == 'function') {
+        if (markControl.option.success != undefined && typeof (markControl.option.success) == 'function') {
             markControl.option.success();
             markControl.option.success();
         }
         }
     }
     }
 }
 }
 
 
-MarkControl.prototype.start = function(taskOption) {
+MarkControl.prototype.start = function (taskOption) {
     taskOption.markControl = this;
     taskOption.markControl = this;
     var markControl = this;
     var markControl = this;
-    taskOption.success = function() {
-        markControl.context.prefetchCallback = function() {
+    taskOption.success = function () {
+        markControl.context.prefetchCallback = function () {
             markControl.context.prefetchCallback = undefined;
             markControl.context.prefetchCallback = undefined;
             markControl.getTask();
             markControl.getTask();
         }
         }
-        markControl.context.statusCallback = function() {
+        markControl.context.statusCallback = function () {
             markControl.context.statusCallback = undefined;
             markControl.context.statusCallback = undefined;
             markControl.prefetch();
             markControl.prefetch();
         }
         }
         markControl.getStatus();
         markControl.getStatus();
     };
     };
-    taskOption.error = function(message) {
+    taskOption.error = function (message) {
         alert('初始化失败,请刷新页面重新加载');
         alert('初始化失败,请刷新页面重新加载');
     };
     };
     this.taskControl = new TaskControl(taskOption);
     this.taskControl = new TaskControl(taskOption);
@@ -427,11 +429,11 @@ MarkControl.prototype.start = function(taskOption) {
 }
 }
 
 
 //task预加载
 //task预加载
-MarkControl.prototype.prefetch = function() {
+MarkControl.prototype.prefetch = function () {
     var taskControl = this.taskControl;
     var taskControl = this.taskControl;
     var markControl = this;
     var markControl = this;
     var context = this.context;
     var context = this.context;
-    var jsonBuilder = this.modules['json-builder'];
+    var jsonLoader = this.modules['json-loader'];
 
 
     if (context.isFinish != true) {
     if (context.isFinish != true) {
         if (taskControl.isFinish()) {
         if (taskControl.isFinish()) {
@@ -442,9 +444,9 @@ MarkControl.prototype.prefetch = function() {
             context.prefetching = true;
             context.prefetching = true;
             markControl.trigger('task.prefetch.before');
             markControl.trigger('task.prefetch.before');
 
 
-            taskControl.fetch(function(task) {
-                if (jsonBuilder != undefined) {
-                	jsonBuilder.build(task, function(error) {
+            taskControl.fetch(function (task) {
+                if (jsonLoader != undefined) {
+                    jsonLoader.build(task, function (error) {
                         if (error) {
                         if (error) {
                             context.prefetching = false;
                             context.prefetching = false;
                             markControl.trigger('task.prefetch.error');
                             markControl.trigger('task.prefetch.error');
@@ -461,11 +463,11 @@ MarkControl.prototype.prefetch = function() {
                     context.prefetching = false;
                     context.prefetching = false;
                     markControl.trigger('task.prefetch.success');
                     markControl.trigger('task.prefetch.success');
                 }
                 }
-            }, function(task) {
+            }, function (task) {
                 context.prefetchStatus = task.message;
                 context.prefetchStatus = task.message;
                 context.prefetching = false;
                 context.prefetching = false;
                 markControl.trigger('task.prefetch.none');
                 markControl.trigger('task.prefetch.none');
-            }, function() {
+            }, function () {
                 context.prefetching = false;
                 context.prefetching = false;
                 markControl.trigger('task.prefetch.none');
                 markControl.trigger('task.prefetch.none');
             });
             });
@@ -475,12 +477,12 @@ MarkControl.prototype.prefetch = function() {
     }
     }
 }
 }
 
 
-MarkControl.prototype.getStatus = function() {
+MarkControl.prototype.getStatus = function () {
     if (this.taskControl == undefined) {
     if (this.taskControl == undefined) {
         return;
         return;
     }
     }
     var self = this;
     var self = this;
-    this.taskControl.status(function(status) {
+    this.taskControl.status(function (status) {
         self.context.status = status;
         self.context.status = status;
         if (status != undefined) {
         if (status != undefined) {
             self.context.isFinish = status.blockTotalCount > 0 && status.blockTotalCount == (status.blockMarkedCount + status.blockExceptionCount);
             self.context.isFinish = status.blockTotalCount > 0 && status.blockTotalCount == (status.blockMarkedCount + status.blockExceptionCount);
@@ -492,7 +494,7 @@ MarkControl.prototype.getStatus = function() {
     })
     })
 }
 }
 
 
-MarkControl.prototype.getTask = function() {
+MarkControl.prototype.getTask = function () {
     if (this.taskControl == undefined) {
     if (this.taskControl == undefined) {
         return;
         return;
     }
     }
@@ -523,29 +525,29 @@ MarkControl.prototype.getTask = function() {
     }
     }
 }
 }
 
 
-MarkControl.prototype.getHistory = function(data) {
+MarkControl.prototype.getHistory = function (data) {
     if (this.taskControl == undefined) {
     if (this.taskControl == undefined) {
         return;
         return;
     }
     }
-    this.taskControl.history(data, function(result) {
+    this.taskControl.history(data, function (result) {
         data.result = result;
         data.result = result;
         this.option.markControl.trigger('history.get.success', data);
         this.option.markControl.trigger('history.get.success', data);
-    }, function(message) {
+    }, function (message) {
         data.message = message;
         data.message = message;
         this.option.markControl.trigger('history.get.error', data);
         this.option.markControl.trigger('history.get.error', data);
     });
     });
 }
 }
 
 
-MarkControl.prototype.setTask = function(task) {
-    var imageBuilder = this.modules['image-builder'];
+MarkControl.prototype.setTask = function (task) {
+    var jsonLoader = this.modules['json-loader'];
     var self = this;
     var self = this;
 
 
     if (this.context.task != undefined && !this.context.task.previous && this.context.waitTask == undefined) {
     if (this.context.task != undefined && !this.context.task.previous && this.context.waitTask == undefined) {
         this.context.waitTask = this.context.task;
         this.context.waitTask = this.context.task;
     }
     }
     this.trigger('task.get.before');
     this.trigger('task.get.before');
-    if (imageBuilder != undefined && task != undefined) {
-        imageBuilder.build(task, function(error) {
+    if (jsonLoader != undefined && task != undefined) {
+        jsonLoader.build(task, function (error) {
             self.context.task = task;
             self.context.task = task;
             self.trigger('task.get.success');
             self.trigger('task.get.success');
         });
         });
@@ -557,7 +559,7 @@ MarkControl.prototype.setTask = function(task) {
     }
     }
 }
 }
 
 
-MarkControl.prototype.submitTask = function(submitUrl) {
+MarkControl.prototype.submitTask = function (submitUrl) {
     var task = this.context.task;
     var task = this.context.task;
     var markControl = this;
     var markControl = this;
     var submitUrl = submitUrl != undefined && submitUrl.length > 0 ? submitUrl : this.option.submitUrl;
     var submitUrl = submitUrl != undefined && submitUrl.length > 0 ? submitUrl : this.option.submitUrl;
@@ -586,7 +588,7 @@ MarkControl.prototype.submitTask = function(submitUrl) {
 
 
         if (this.taskControl != undefined) {
         if (this.taskControl != undefined) {
             //已定义任务引擎
             //已定义任务引擎
-            this.taskControl.submit(submitObj, function(status) {
+            this.taskControl.submit(submitObj, function (status) {
                 if (status != undefined && status.valid == true) {
                 if (status != undefined && status.valid == true) {
                     markControl.context.status = status;
                     markControl.context.status = status;
                     markControl.trigger('mark.status.change', status);
                     markControl.trigger('mark.status.change', status);
@@ -596,7 +598,7 @@ MarkControl.prototype.submitTask = function(submitUrl) {
                 } else {
                 } else {
                     markControl.trigger('task.submit.success');
                     markControl.trigger('task.submit.success');
                 }
                 }
-            }, function(message) {
+            }, function (message) {
                 markControl.trigger('task.submit.error', message);
                 markControl.trigger('task.submit.error', message);
             });
             });
         } else if (submitUrl != undefined && submitUrl.length > 0) {
         } else if (submitUrl != undefined && submitUrl.length > 0) {
@@ -607,14 +609,14 @@ MarkControl.prototype.submitTask = function(submitUrl) {
                 data: JSON.stringify(submitObj),
                 data: JSON.stringify(submitObj),
                 dataType: "json",
                 dataType: "json",
                 contentType: 'application/json;charset=utf-8',
                 contentType: 'application/json;charset=utf-8',
-                success: function(result) {
+                success: function (result) {
                     if (result.success == true) {
                     if (result.success == true) {
                         markControl.trigger('task.submit.success');
                         markControl.trigger('task.submit.success');
                     } else {
                     } else {
                         markControl.trigger('task.submit.error', result.message);
                         markControl.trigger('task.submit.error', result.message);
                     }
                     }
                 },
                 },
-                error: function(message) {
+                error: function (message) {
                     markControl.trigger('task.submit.error', message);
                     markControl.trigger('task.submit.error', message);
                 }
                 }
             });
             });
@@ -623,13 +625,13 @@ MarkControl.prototype.submitTask = function(submitUrl) {
         }
         }
     }
     }
 }
 }
-MarkControl.prototype.addNavGroup = function(title, content) {
+MarkControl.prototype.addNavGroup = function (title, content) {
     var self = this;
     var self = this;
     self.navNumber++;
     self.navNumber++;
     var nav = $('<a href="#"><span>' + title + '</span></a>').appendTo(self.container.centerNavbar);
     var nav = $('<a href="#"><span>' + title + '</span></a>').appendTo(self.container.centerNavbar);
     nav.attr('data-number', self.navNumber);
     nav.attr('data-number', self.navNumber);
     content.attr('data-number', self.navNumber);
     content.attr('data-number', self.navNumber);
-    nav.click(function() {
+    nav.click(function () {
         self.container.centerNavbar.find('a').removeClass('selected');
         self.container.centerNavbar.find('a').removeClass('selected');
         $(this).addClass('selected');
         $(this).addClass('selected');
 
 
@@ -649,7 +651,7 @@ MarkControl.prototype.addNavGroup = function(title, content) {
 //默认要初始化的模块名称
 //默认要初始化的模块名称
 MarkControl.prototype.defaultModules = {
 MarkControl.prototype.defaultModules = {
     'json-builder': {
     'json-builder': {
-    	answerServer : '${answerServer}'
+        answerServer: '${answerServer}'
     }
     }
 };
 };
 
 
@@ -677,15 +679,15 @@ MarkControl.prototype.mark_function_dom = '<h3 class="popover-title">评卷功
 </p></div>';
 </p></div>';
 
 
 //其他通用方法
 //其他通用方法
-String.prototype.startWith = function(prefix) {
+String.prototype.startWith = function (prefix) {
     return this.indexOf(prefix) === 0;
     return this.indexOf(prefix) === 0;
 }
 }
-String.prototype.endWith = function(suffix) {
+String.prototype.endWith = function (suffix) {
     return this.match(suffix + "$") == suffix;
     return this.match(suffix + "$") == suffix;
 };
 };
 
 
 //日期格式化
 //日期格式化
-Date.prototype.format = function(fmt) { //author: meizz 
+Date.prototype.format = function (fmt) { //author: meizz
     var o = {
     var o = {
         "M+": this.getMonth() + 1, //月份 
         "M+": this.getMonth() + 1, //月份 
         "d+": this.getDate(), //日 
         "d+": this.getDate(), //日 

+ 0 - 18
stmms-web/src/main/webapp/static/mark-json/js/rich-text-builder.js

@@ -1,18 +0,0 @@
-//富文本json解析模块
-var rich_text_dom = '<div class="wp"><div class="rich-text"></div></div>';
-var sectionsch_text_dom = '<p></p>';
-function RichTextBuilder(jsonText) {
-	var dom = rich_text_dom;
-	var questionArray = JSON.parse(jsonText);
-	 for (var i = 0; i < questionArray.length; i++) {
-		 var question = questionArray[i];
-		 var title = '<span>题号:'+ question.mainNumber+'-'+question.subNumber +'<span><br/>';
-		 var body = question.body.sections;
-		 for(var j = 0; j < body.length; j++){
-			 
-		 }
-		 
-	 }
-	 return dom;
-}
-

+ 103 - 0
stmms-web/src/main/webapp/static/rich-text/css/rich-text.css

@@ -0,0 +1,103 @@
+/*rich-text*/
+* {
+    font-family: "微软雅黑", "苹方", Arial, Helvetica, sans-serif;
+}
+
+.rich-text {
+    padding: 10px 0;
+}
+
+.rich-text p {
+    font-size: 16px;
+    line-height: 2em;
+    margin: 10px 0;
+    word-break: break-all;
+}
+
+.rich-text .text.bold {
+    font-weight: 700;
+}
+
+.rich-text .text.underline {
+    text-decoration: underline;
+}
+
+.rich-text .text.sup {
+    vertical-align: super;
+    font-size: 9px;
+    font-family: Arial, Helvetica, sans-serif;
+    margin: 10px 0px 0px 0px
+}
+
+.rich-text .text.sub {
+    vertical-align: sub;
+    font-size: 9px;
+    font-family: Arial, Helvetica, sans-serif;
+}
+
+.rich-text audio {
+    vertical-align: middle;
+    height: 2em;
+}
+
+.rich-text img {
+    vertical-align: middle;
+}
+
+.rich-text .image.inline>img {
+    width: auto;
+    max-height: 2em;
+}
+
+.rich-text .loading {
+    position: relative;
+    display: inline-block;
+    width: 4em;
+    height: 4em;
+    color: #ccc;
+    vertical-align: middle;
+    pointer-events: none;
+    border: .2em solid currentcolor;
+    border-bottom-color: transparent;
+    border-radius: 50%;
+    -webkit-animation: 1s loading linear infinite;
+    animation: 1s loading linear infinite;
+    margin: 20px;
+}
+
+.rich-text .loading img {
+    opacity: 0;
+    filter: alpha(opacity=40);
+}
+
+.rich-text .inline.loading {
+    width: 1.2em;
+    height: 1.2em;
+    border: .1em solid currentcolor;
+    border-bottom-color: transparent;
+    margin: 0 20px;
+}
+
+@-webkit-keyframes loading {
+    0% {
+        -webkit-transform: rotate(0deg);
+        transform: rotate(0deg);
+    }
+
+    100% {
+        -webkit-transform: rotate(360deg);
+        transform: rotate(360deg);
+    }
+}
+
+@keyframes loading {
+    0% {
+        -webkit-transform: rotate(0deg);
+        transform: rotate(0deg);
+    }
+
+    100% {
+        -webkit-transform: rotate(360deg);
+        transform: rotate(360deg);
+    }
+}

+ 37 - 0
stmms-web/src/main/webapp/static/rich-text/js/render.js

@@ -0,0 +1,37 @@
+function renderRichText(body) {
+    let sections = body.sections || [];
+    let html = [];
+    sections.forEach(section => {
+        html.push(renderSection(section));
+    });
+    return html.join('');
+}
+
+function renderSection(section) {
+    let blocks = section.blocks || [];
+    let inline = blocks.length === 1;
+    let html = [];
+    blocks.forEach(block => {
+        html.push(renderBlock(block, inline));
+    });
+    return '<p>' + html.join('') + '</p>';
+}
+
+function renderBlock(block, inline) {
+    let type = '';
+    let inner = '';
+    if (block.type === 'text') {
+        type = 'text';
+        inner = block.value;
+    } else if (block.type === 'image') {
+        type = 'image';
+        if (inline === true) {
+            type += ' inline';
+        }
+        inner = '<img src="' + block.value + '"/>';
+    } else if (block.type === 'audio') {
+        type = 'audio';
+        inner = '<audio controls><source src="' + block.value + '" type="audio/mpeg"></audio>';
+    }
+    return '<span class="' + type + '">' + inner + '</span>';
+}