-
+
{/* {discussMessage.author.name} */}
{ discussMessage.author && {discussMessage.author} }
{discussMessage.commit_count===undefined?"":{discussMessage.commit_count} 已交 }
@@ -357,6 +357,15 @@ class ShixunhomeWorkItem extends Component{
:
{discussMessage.status_time}
}
+ {
+ discussMessage && discussMessage.upper_category_name &&
+ 22 }>
+ { {discussMessage.upper_category_name} }
+
+ }
+
+
+
{/* { discussMessage.replies_count != 0 && {discussMessage.replies_count} 回复 }
{ discussMessage.praise_num != 0 && {discussMessage.praise_num} 点赞 }
@@ -378,7 +387,7 @@ class ShixunhomeWorkItem extends Component{
`
}
- {this.props.isAdmin?this.stopPro(event)} className={this.props.isAdminOrCreator()?"homepagePostSetting homepagePostSettingname":"homepagePostSetting homepagePostSettingbox"} style={{"right":"-2px","top":"44px","display":"block"}}>
+ {this.props.isAdmin?
this.stopPro(event)} className={this.props.isAdminOrCreator()?"homepagePostSetting homepagePostSettingname":"homepagePostSetting homepagePostSettingbox"} style={{"right":"-2px","top":"46px","display":"block"}}>
实训详情
{this.props.isAdminOrCreator()?
this.editname(discussMessage.name,discussMessage.homework_id,event)} className={"btn colorblue ml20 font-16"}>重命名 :""}
{/*
设置 */}
diff --git a/public/react/src/modules/courses/shixunHomework/Shixunworkdetails/ShixunCustomsPass.js b/public/react/src/modules/courses/shixunHomework/Shixunworkdetails/ShixunCustomsPass.js
index 8ffa8a1dd..0b5b04cfc 100644
--- a/public/react/src/modules/courses/shixunHomework/Shixunworkdetails/ShixunCustomsPass.js
+++ b/public/react/src/modules/courses/shixunHomework/Shixunworkdetails/ShixunCustomsPass.js
@@ -1,10 +1,10 @@
import React, {Component} from "react";
import {WordsBtn} from 'educoder';
-import {Table} from "antd";
+import {Table,InputNumber} from "antd";
import {Link,Switch,Route,Redirect} from 'react-router-dom';
import moment from 'moment';
import { MonacoDiffEditor } from 'react-monaco-editor';
-
+import axios from 'axios';
class ShixunCustomsPass extends Component {
constructor(props) {
@@ -18,10 +18,51 @@ class ShixunCustomsPass extends Component {
componentDidMount() {
}
+ editgame_scores=(e,id,maxsum,code_rate,copy_user_id)=>{
+
+ let{datas}=this.state;
+ let newdatas=datas;
+ let score=e.target.value;
+
+ if(score!=null&&score!=undefined&&score!=""){
+ if(score<0){
+ this.props.showNotification("不能小于0");
+ this.setState({
+ customsids:id
+ })
+ }else if(score>maxsum){
+ this.props.showNotification(`不能大于关卡分值${maxsum}`);
+ this.setState({
+ customsids:id
+ })
+ }else{
+ let work_id=this.props.data.work_id;
+ let url=`/student_works/${work_id}/adjust_review_score.json`
+ axios.post(url,{
+ type:"review",
+ score:score,
+ challenge_id:id,
+ code_rate:code_rate,
+ copy_user_id:copy_user_id
+ }).then((result)=>{
+ if(result.data.status===0){
+ this.props.updatas();
+ this.props.showNotification(result.data.message);
+ }else{
+ this.props.showNotification(result.data.message);
+ }
+ }).catch((error)=>{
+ })
+ }
+ }else{
+ this.props.showNotification("调分为空将不会修改之前的分数");
+ }
+ }
render() {
let {data}=this.props;
- console.log(data)
+ let {customsids}=this.state;
+ // console.log(data)
let datas=[];
data&&data.challenge_list.forEach((item,key)=>{
@@ -33,6 +74,8 @@ class ShixunCustomsPass extends Component {
finishtime:item.copy_username,
elapsedtime:item.copy_end_time===null?"无":item.copy_end_time===undefined?"无":item.copy_end_time===""?"无":moment(item.copy_end_time).format('YYYY-MM-DD HH:mm:ss'),
empvalue:item.code_rate,
+ challenge_id:{id:item.id},
+ copy_user_id:item.copy_user_id
// adjustmentminute:asdasd
})
})
@@ -112,6 +155,22 @@ class ShixunCustomsPass extends Component {
render: (text, record) => (
{record.elapsedtime}
+
+ ),
+ },{
+ title: '调分',
+ key: 'adjustmentminute',
+ dataIndex: 'adjustmentminute',
+
+ render: (text, record) => (
+
+
+ {record.copy_user_id===null?"": this.editgame_scores(e,record.challenge_id.id,record.evaluating.all_score,record.empvalue,record.copy_user_id)}
+ // min={0} max={record.game_scores.game_score_full}
+ />}
+
+ {/*查看 */}
),
}, {
@@ -138,7 +197,15 @@ class ShixunCustomsPass extends Component {
// },
-
+ if(this.props.isAdmin()===false){
+ columns.some((item,key)=> {
+ if (item.title === "调分") {
+ columns.splice(key, 1)
+ return true
+ }
+ }
+ )
+ }
return (
@@ -177,6 +244,9 @@ class ShixunCustomsPass extends Component {
.customsPass{
text-align: left !important;
}
+ .ant-table-thead > tr > th, .ant-table-tbody > tr > td {
+ padding: 16px 12px;
+ }
`}
{datas===undefined?"":
:
-
@@ -286,7 +286,7 @@ class ShixunWorkModal extends Component{
group_list&&group_list.length===0?"":group_list.map((item,key)=>{
return(
-
+
{item===undefined?"":item.name}
+ className="task-hide color-grey-name" title={item===undefined?"":item.name}>{item===undefined?"":item.name}
diff --git a/public/react/src/modules/courses/shixunHomework/ShowAppraiseList.js b/public/react/src/modules/courses/shixunHomework/ShowAppraiseList.js
new file mode 100644
index 000000000..f52491ed0
--- /dev/null
+++ b/public/react/src/modules/courses/shixunHomework/ShowAppraiseList.js
@@ -0,0 +1,176 @@
+import React,{ Component } from "react";
+import { Modal,Checkbox,Upload,Button,Icon,message,Input,Radio} from "antd";
+import { WordNumberTextarea,markdownToHTML } from 'educoder';
+import axios from 'axios';
+import './style.css';
+
+class ShowAppraiseList extends Component{
+ constructor(props){
+ super(props);
+ this.state={
+
+ }
+ }
+
+
+
+ render(){
+ let {data, work_comment,work_comment_hidden}=this.props;
+ let work_commenttype=work_comment===undefined||work_comment===null||work_comment==="";
+ let work_comment_hiddentype=work_comment_hidden===undefined||work_comment_hidden===null||work_comment_hidden==="";
+
+ return(
+
+
+ {data===undefined?"":work_commenttype===true&&work_comment_hiddentype===true?"":
+
+
+
+
+
+ {this.props&&this.props.isAdmin()===true?
+
+
+ {work_commenttype===true?"":
+ 学生可见(学生可查看老师的评阅内容)
+
}
+
+ {work_commenttype===true?"":
}
+
+ {work_comment_hiddentype===true?"":
+ 学生不可见(仅对课堂老师可见)
+
}
+
+ {work_comment_hiddentype===true?"":
}
+
+
+
:
}
+
+
+
}
+
+
+
+
+
+ {data===undefined?"":data.stage_list===undefined||data.stage_list===null?"":data.stage_list.map((item,key)=>{
+ let challenge_comment_hidden=item.challenge_comment_hidden===undefined||item.challenge_comment_hidden===null||item.challenge_comment_hidden==="";
+ let challenge_comment=item.challenge_comment===undefined||item.challenge_comment===null||item.challenge_comment==="";
+
+ return(
+
+ {challenge_comment===false||challenge_comment_hidden==false?
+
+
+
+
+ {this.props&&this.props.isAdmin()===true?
+
+
+ {challenge_comment===true?"":
+ 学生可见(学生可查看老师的评阅内容)
+
}
+
+ {challenge_comment===true?"":
}
+
+ {challenge_comment_hidden===true?"":
+ 学生不可见(仅对课堂老师可见)
+
}
+ {challenge_comment_hidden===true?"":
}
+
+
:
}
+
+
+
:""}
+
+
)
+ })
+ }
+
+
+
+ )
+ }
+}
+export default ShowAppraiseList;
+
diff --git a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js
index bd90db42f..bee373bd9 100644
--- a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js
+++ b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js
@@ -862,19 +862,19 @@ class Trainingjobsetting extends Component {
var exams = parseFloat(Proportion.toFixed(1));
var intk = srorelength*exams;
intkk=oushution - intk;
- console.log("奇数");
- console.log(srorelength);//3
- console.log(oushution);//79
- console.log(exams);//26.3
- console.log(intk);//78.9
- console.log(intkk);
+ // console.log("奇数");
+ // console.log(srorelength);//3
+ // console.log(oushution);//79
+ // console.log(exams);//26.3
+ // console.log(intk);//78.9
+ // console.log(intkk);
}else {
// 偶数
var examsy =parseFloat(Proportion.toFixed(1));
intkks=oushution - (examsy*srorelength);
- console.log("偶数");
- console.log(oushution);
- console.log((examsy*srorelength));
+ // console.log("偶数");
+ // console.log(oushution);
+ // console.log((examsy*srorelength));
}
var mact=0;
diff --git a/public/react/src/modules/courses/shixunHomework/shixunHomework.js b/public/react/src/modules/courses/shixunHomework/shixunHomework.js
index fe959b691..55dfac7fc 100644
--- a/public/react/src/modules/courses/shixunHomework/shixunHomework.js
+++ b/public/react/src/modules/courses/shixunHomework/shixunHomework.js
@@ -6,6 +6,7 @@ import axios from'axios';
import HomeworkModal from "../coursesPublic/HomeworkModal";
import ShixunModal from "../coursesPublic/ShixunModal";
import PathModal from "../coursesPublic/PathModal";
+import NewShixunModel from '../coursesPublic/NewShixunModel';
import AddcoursesNav from "../coursesPublic/AddcoursesNav";
import Modals from '../../modals/Modals';
import moment from 'moment';
@@ -445,18 +446,18 @@ class ShixunHomework extends Component{
}
- // 选用实训
- createCommonWork=()=>{
-
- this.setState({
- hometypepvisible:true,
- shixunmodal:true,
- patheditarry:[],
- checkBoxValues:[]
- })
-
-
- }
+ // // 选用实训
+ // createCommonWork=()=>{
+ //
+ // this.setState({
+ // hometypepvisible:true,
+ // shixunmodal:true,
+ // patheditarry:[],
+ // checkBoxValues:[]
+ // })
+ //
+ //
+ // }
// 选用实训路径
createCommonpath=()=>{
@@ -502,9 +503,9 @@ class ShixunHomework extends Component{
// }).then((result)=>{
// if(result.status===200){
//
- // let shixun_list=result.data.shixun_list;
- // for(var i=0; i{
// if(result.status===200){
//
- // let shixun_list=result.data.subject_list;
- // for(var i=0; i{
+ this.setState({
+ NewShixunModelType:true,
+ patheditarry:[],
+ checkBoxValues:[]
+ })
+ }
+ hideNewShixunModelType=()=>{
+ this.setState({
+ NewShixunModelType:false
+ })
+ }
render(){
let {
modalname,
@@ -931,7 +944,7 @@ class ShixunHomework extends Component{
course_modules,
shixunpath,
order,
- antIcon,
+ NewShixunModelType,
}=this.state;
let main_id=this.props.match.params.main_id;
@@ -940,6 +953,23 @@ class ShixunHomework extends Component{
return(
+
+ {/*新版实训model*/}
+ {NewShixunModelType===true?
this.hideNewShixunModelType()}
+ coursesId={this.props.match.params.coursesId}
+ homeworkupdatalists={(Coursename,page,order)=>this.homeworkupdatalist(Coursename,page,order)}
+ Coursename={Coursename}
+ page={page}
+ order={order}
+ statustype={'published'}
+ />:""}
+
+
{/*提示*/}
{Modalstype&&Modalstype===true?this.getcourse_groupslist(id)}
/>:""}
- {/*选择实训*/}
- {shixunmodal===true?this.homeworkupdatalist(Coursename,page,order)}
- hometypepvisible={hometypepvisible}
- hidecouseShixunModal={this.hidecouseShixunModal}
- newshixunmodallist={newshixunmodallist}
- coursesId={this.props.match.params.coursesId}
- courseshomeworkstart={(category_id,homework_ids)=>this.newhomeworkstart(category_id,homework_ids)}
- funpatheditarry={(patheditarry)=>this.funpatheditarry(patheditarry)}
- patheditarry={patheditarry}
- />:""}
+ {/*/!*选择实训*!/*/}
+ {/*{shixunmodal===true?this.homeworkupdatalist(Coursename,page,order)}*/}
+ {/*hometypepvisible={hometypepvisible}*/}
+ {/*hidecouseShixunModal={this.hidecouseShixunModal}*/}
+ {/*newshixunmodallist={newshixunmodallist}*/}
+ {/*coursesId={this.props.match.params.coursesId}*/}
+ {/*courseshomeworkstart={(category_id,homework_ids)=>this.newhomeworkstart(category_id,homework_ids)}*/}
+ {/*funpatheditarry={(patheditarry)=>this.funpatheditarry(patheditarry)}*/}
+ {/*patheditarry={patheditarry}*/}
+ {/*/>:""}*/}
{shixunmodal===true||shixunpath===true?
{
- datas.push({
- customs: key+1,
- taskname:{name:item.name,complete_status:item.complete_status},
- openingtime:item.open_time,
- evaluating: item.evaluate_count,
- finishtime:item.finished_time,
- elapsedtime:item.time_consuming,
- empvalue:{myself:item.myself_experience,experience:item.experience},
- game_scores:{game_score:item.game_score,game_score_full:item.game_score_full},
- challenge_id:{id:item.challenge_id}
- // adjustmentminute:asdasd
- })
- })
- this.setState({
- datas:datas
- })
- }
}
myjumptopic=(e)=>{
console.log("获取到值");
@@ -96,7 +74,28 @@ class OfficialAcademicTranscript extends Component {
}
render() {
- let {datas,customsids}=this.state;
+ let {customsids}=this.state;
+ let {data}=this.props;
+
+ let datas=[];
+ if(data!=undefined){
+ data.stage_list===undefined?"":data.stage_list.forEach((item,key)=>{
+ datas.push({
+ customs: key+1,
+ taskname:{name:item.name,complete_status:item.complete_status},
+ openingtime:item.open_time,
+ evaluating: item.evaluate_count,
+ finishtime:item.finished_time,
+ elapsedtime:item.time_consuming,
+ empvalue:{myself:item.myself_experience,experience:item.experience},
+ game_scores:{game_score:item.game_score,game_score_full:item.game_score_full},
+ challenge_id:{id:item.challenge_id},
+ challenge_comment: item.challenge_comment,
+ challenge_comment_hidden: item.challenge_comment_hidden,
+ // adjustmentminute:asdasd
+ })
+ })
+ }
let columns=[{
title: '关卡',
@@ -150,7 +149,7 @@ class OfficialAcademicTranscript extends Component {
),
}, {
- title: '耗时',
+ title: '实战耗时',
key: 'elapsedtime',
dataIndex: 'elapsedtime',
@@ -196,6 +195,20 @@ class OfficialAcademicTranscript extends Component {
// min={0} max={record.game_scores.game_score_full}
/>
{/*查看 */}
+
+ ),
+ },{
+ title: '操作',
+ key: 'operation',
+ dataIndex: 'operation',
+
+ render: (text, record) => (
+
+
+ this.props.showAppraiseModal("child",record.challenge_id.id,record.challenge_comment,record.challenge_comment_hidden)}
+ >评阅
),
}];
@@ -210,6 +223,13 @@ class OfficialAcademicTranscript extends Component {
}
}
)
+ columns.some((item,key)=> {
+ if (item.title === "操作") {
+ columns.splice(key, 1)
+ return true
+ }
+ }
+ )
}
return (
@@ -276,6 +296,9 @@ class OfficialAcademicTranscript extends Component {
.linhe15{
line-height: 15px;
}
+ .mr22{
+ margin-right: 22px;
+ }
`}
{datas===undefined?"":
{data&&data.username}
- {data!==undefined?"--":data.student_id===undefined?"--":data.student_id===null?"--":data.student_id}
+ {data===undefined?"--":data.student_id===undefined?"--":data.student_id===null?"--":data.student_id}
{data&&data.echart_data===undefined?"":data&&data.echart_data.myself_eff[1]}
{data&&data.echart_data===undefined?"":data&&data.echart_data.myself_eff[0]}
@@ -396,7 +400,7 @@ class Shixunechart extends Component {
{data&&data.username}
- {data!==undefined?"--":data.student_id===undefined?"--":data.student_id===null?"--":data.student_id}
+ {data===undefined?"--":data.student_id===undefined?"--":data.student_id===null?"--":data.student_id}
{data&&data.echart_data===undefined?"":data&&data.echart_data.myself_object[1]}
diff --git a/public/react/src/modules/courses/shixunHomework/style.css b/public/react/src/modules/courses/shixunHomework/style.css
index f2b0c39ed..1af39e7ce 100644
--- a/public/react/src/modules/courses/shixunHomework/style.css
+++ b/public/react/src/modules/courses/shixunHomework/style.css
@@ -45,7 +45,28 @@
cursor: default;
}
.maxnamewidth110{
- max-width: 110px;
+ max-width: 100px;
+ overflow:hidden;
+ text-overflow:ellipsis;
+ white-space:nowrap;
+ cursor: default;
+}
+.maxnamewidth200{
+ max-width: 200px;
+ overflow:hidden;
+ text-overflow:ellipsis;
+ white-space:nowrap;
+ cursor: default;
+}
+.maxnamewidth145{
+ max-width: 145px;
+ overflow:hidden;
+ text-overflow:ellipsis;
+ white-space:nowrap;
+ cursor: default;
+}
+.maxnamewidth145{
+ max-width: 145px;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
@@ -53,4 +74,17 @@
}
.ysyslxh{
background: #fafafa;
+}
+
+.z666{
+ color: #666;
+ font-size:14px;
+}
+.z000{
+ color: #000;
+ font-size:16px;
+}
+
+.pd30bt{
+ padding: 10px 30px 0px 30px;
}
\ No newline at end of file
diff --git a/public/react/src/modules/forums/RightSection.css b/public/react/src/modules/forums/RightSection.css
index f151585c8..a31b1dff8 100644
--- a/public/react/src/modules/forums/RightSection.css
+++ b/public/react/src/modules/forums/RightSection.css
@@ -10,9 +10,12 @@
/*margin-right: 35px;*/
}
.search-newysl {
- width:237px!important;
+
height: 30px;
margin-bottom: 30px;
+}
+.search-newyslw{
+ width:237px!important;
}
.search-new-input {
padding-left: 16px;
diff --git a/public/react/src/modules/help/AboutUs.js b/public/react/src/modules/help/AboutUs.js
new file mode 100644
index 000000000..06198418c
--- /dev/null
+++ b/public/react/src/modules/help/AboutUs.js
@@ -0,0 +1,54 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Card } from "antd";
+import axios from 'axios';
+
+import { MarkdownToHtml } from 'educoder';
+
+class AboutUs extends React.Component {
+ constructor (props) {
+ super(props);
+
+ this.state = {
+ loading: true,
+ content: null
+ }
+ }
+
+ componentDidMount(){
+ window.document.title = "关于我们";
+ this.getContent();
+ }
+
+ getContent(){
+ axios.get("/helps/about.json").then((result) => {
+ if(result){
+ this.setState({
+ content: result.data.content,
+ loading: false
+ })
+ }
+ }).catch((error) => {
+ console.log(error);
+ this.setState({ loading: false });
+ })
+ }
+
+ render() {
+ let { loading, content } = this.state;
+
+ return (
+
+
+
+
+ { content && }
+
+
+
+
+ )
+ }
+}
+
+export default AboutUs;
\ No newline at end of file
diff --git a/public/react/src/modules/help/Agreement.js b/public/react/src/modules/help/Agreement.js
new file mode 100644
index 000000000..4e483b719
--- /dev/null
+++ b/public/react/src/modules/help/Agreement.js
@@ -0,0 +1,54 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Card } from "antd";
+import axios from 'axios';
+
+import { MarkdownToHtml } from 'educoder';
+
+class Agreement extends React.Component {
+ constructor (props) {
+ super(props);
+
+ this.state = {
+ loading: true,
+ content: null
+ }
+ }
+
+ componentDidMount(){
+ window.document.title = "服务协议";
+ this.getContent();
+ }
+
+ getContent(){
+ axios.get("/helps/agreement.json").then((result) => {
+ if(result){
+ this.setState({
+ content: result.data.content,
+ loading: false
+ })
+ }
+ }).catch((error) => {
+ console.log(error);
+ this.setState({ loading: false });
+ })
+ }
+
+ render() {
+ let { loading, content } = this.state;
+
+ return (
+
+
+
+
+ { content && }
+
+
+
+
+ )
+ }
+}
+
+export default Agreement;
\ No newline at end of file
diff --git a/public/react/src/modules/help/ContactUs.css b/public/react/src/modules/help/ContactUs.css
new file mode 100644
index 000000000..e516196e6
--- /dev/null
+++ b/public/react/src/modules/help/ContactUs.css
@@ -0,0 +1,24 @@
+.contact-us-container {
+
+}
+.contact-us-container .contact-item {
+ padding: 20px 15px;
+ border-bottom: 1px solid #EEEEEE;
+}
+.contact-us-container .contact-item:first-child {
+ padding-top: 0;
+}
+.contact-us-container .contact-item:last-child {
+ border-bottom: unset;
+}
+
+.contact-us-container .contact-item-label {
+ font-size: 16px;
+ padding-bottom: 10px;
+}
+.contact-us-container .contact-item-content {
+ margin-left: 10px;
+}
+.contact-us-container .contact-item-content .ant-row {
+ margin-top: 10px;
+}
\ No newline at end of file
diff --git a/public/react/src/modules/help/ContactUs.js b/public/react/src/modules/help/ContactUs.js
new file mode 100644
index 000000000..04e3404fe
--- /dev/null
+++ b/public/react/src/modules/help/ContactUs.js
@@ -0,0 +1,89 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Card, Row, Col } from "antd";
+import axios from 'axios';
+
+import './ContactUs.css';
+
+class ContactUs extends React.Component {
+ constructor (props) {
+ super(props);
+
+ this.state = {
+ loading: true,
+ contacts: null,
+ address: null
+ }
+ }
+
+ componentDidMount(){
+ window.document.title = "联系我们";
+ this.getData();
+ }
+
+ getData(){
+ axios.get("/helps/contact.json").then((result) => {
+ if(result){
+ this.setState({
+ contacts: result.data.contacts,
+ address: result.data.address,
+ loading: false
+ })
+ }
+ }).catch((error) => {
+ console.log(error);
+ this.setState({ loading: false });
+ })
+ }
+
+ render() {
+ let { loading, contacts, address } = this.state;
+
+ return (
+
+
+
+
+ {
+ contacts && contacts.map((item, _key) => {
+ return (
+
+
{ item.type }
+
+
+ { item.name }
+
+
+ QQ:
+ { item.qq }
+
+
+ Email:
+ { item.mail }
+
+
+
+ )
+ })
+ }
+ {
+ address && (
+
+
公司地址
+
+
+ { address }
+
+
+
+ )
+ }
+
+
+
+
+ )
+ }
+}
+
+export default ContactUs;
\ No newline at end of file
diff --git a/public/react/src/modules/help/Cooperatives.css b/public/react/src/modules/help/Cooperatives.css
new file mode 100644
index 000000000..1ab232a26
--- /dev/null
+++ b/public/react/src/modules/help/Cooperatives.css
@@ -0,0 +1,17 @@
+.cooperatives-container {
+
+}
+
+.cooperatives-container .cooperative-item-title {
+ margin-bottom: 20px;
+ font-size: 16px;
+}
+.cooperatives-container .cooperative-item-list-item {
+ padding: 10px 0;
+ height: 60px;
+ border: 1px solid #eee;
+}
+.cooperatives-container .cooperative-item-list-item img {
+ width: 100%;
+ height: 100%;
+}
\ No newline at end of file
diff --git a/public/react/src/modules/help/Cooperatives.js b/public/react/src/modules/help/Cooperatives.js
new file mode 100644
index 000000000..b64657488
--- /dev/null
+++ b/public/react/src/modules/help/Cooperatives.js
@@ -0,0 +1,82 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { List, Card } from "antd";
+import axios from 'axios';
+import { getImageUrl } from 'educoder';
+
+import './Cooperatives.css';
+
+class Cooperatives extends React.Component {
+ constructor (props) {
+ super(props);
+
+ this.state = {
+ loading: true,
+ data: [
+ { name: "产学联盟" },
+ { name: "知名企业" },
+ { name: "各类院校" }
+ ]
+ }
+ }
+
+ componentDidMount(){
+ window.document.title = "合作伙伴";
+ this.getCooperatives();
+ }
+
+ getCooperatives(){
+ axios.get("/helps/cooperatives.json").then((result) => {
+ if(result){
+ this.setState({
+ data: result.data.data,
+ loading: false
+ })
+ }
+ }).catch((error) => {
+ console.log(error);
+ this.setState({ loading: false });
+ })
+ }
+
+ render() {
+ let { loading, data } = this.state;
+
+ return (
+
+
+
+
+ {
+ data && data.length > 0 && data.map((item, _key) => {
+ return (
+
+ )
+ })
+ }
+
+
+
+
+ )
+ }
+}
+
+export default Cooperatives;
\ No newline at end of file
diff --git a/public/react/src/modules/help/Feedback.css b/public/react/src/modules/help/Feedback.css
new file mode 100644
index 000000000..081370254
--- /dev/null
+++ b/public/react/src/modules/help/Feedback.css
@@ -0,0 +1,7 @@
+.feedback-container {
+
+}
+.feedback-container .feedback-message {
+ line-height: 26px;
+ color: #999999;
+}
\ No newline at end of file
diff --git a/public/react/src/modules/help/Feedback.js b/public/react/src/modules/help/Feedback.js
new file mode 100644
index 000000000..c7f13f809
--- /dev/null
+++ b/public/react/src/modules/help/Feedback.js
@@ -0,0 +1,51 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Link } from 'react-router-dom';
+import { Card, Form } from "antd";
+
+import "./Feedback.css";
+
+import FeedbackForm from './FeedbackForm';
+const NewFeedbackForm = Form.create({ name: 'feedback_form' })(FeedbackForm);
+
+class Feedback extends React.Component {
+ constructor (props) {
+ super(props);
+ }
+
+ componentDidMount() {
+ window.document.title = "意见反馈";
+ }
+
+ componentDidUpdate(prevProps) {
+ if (prevProps.current_user !== this.props.current_user) {
+ if(!this.props.checkIfLogin()) {
+ this.props.showLoginDialog();
+ }
+ }
+ }
+
+ render() {
+ return (
+
+
+
+
+
+ 想对我们的平台提供功能建议?
+ 发现网页中的问题或bug想告诉我们?
+ 期望与我们展开合作?
+ 在这里把你想说的一切告诉我们吧?
+
+
* 看看帮助中心是否有你想要的答案
+
+
+
+
+
+
+ )
+ }
+}
+
+export default Feedback;
\ No newline at end of file
diff --git a/public/react/src/modules/help/FeedbackForm.js b/public/react/src/modules/help/FeedbackForm.js
new file mode 100644
index 000000000..ae2c4e095
--- /dev/null
+++ b/public/react/src/modules/help/FeedbackForm.js
@@ -0,0 +1,87 @@
+import React from 'react';
+import { Form, Input, Radio, Button } from "antd";
+import axios from 'axios';
+
+const { TextArea } = Input;
+
+class FeedbackForm extends React.Component {
+ constructor (props) {
+ super(props);
+ }
+
+ handleSubmit = e => {
+ e.preventDefault();
+
+ this.props.form.validateFields((err, fieldsValue) => {
+ if(err){ return }
+
+ axios.post("/helps/feedback.json", fieldsValue)
+ .then((result) => {
+ if (result.status === 200 && result.data.status === 0) {
+ this.props.history.push(`/messages/${this.props.current_user.login}/message_detail?target_ids=1`);
+ }
+ }).catch((error) => {
+ console.log(error)
+ })
+ })
+ }
+
+ render() {
+ const { getFieldDecorator } = this.props.form;
+
+ return (
+
+
+ {getFieldDecorator('question_kind', {
+ initialValue: "登录注册",
+ rules: [
+ {
+ required: true,
+ message: '不能为空',
+ },
+ ],
+ })(
+
+ 登录注册
+ 信息认证
+ 实训编程
+ 实训课程
+ 课堂
+ 其它
+
+ )}
+
+
+
+ {getFieldDecorator('url', {
+ rules: [
+ {
+ required: true,
+ message: '不能为空',
+ },
+ ],
+ })( )}
+
+
+
+ {getFieldDecorator('description', {
+ rules: [
+ {
+ required: true,
+ message: '不能为空',
+ },
+ ],
+ })()}
+
+
+
+ 提交
+
+
+
+ )
+ }
+}
+
+export default FeedbackForm;
\ No newline at end of file
diff --git a/public/react/src/modules/help/Help.css b/public/react/src/modules/help/Help.css
new file mode 100644
index 000000000..db2e77307
--- /dev/null
+++ b/public/react/src/modules/help/Help.css
@@ -0,0 +1,9 @@
+.help-container {
+ margin-top: 30px;
+}
+.help-container .help-menu {
+ text-align: center;
+}
+.help-container .help-content {
+ margin-bottom: 40px;
+}
\ No newline at end of file
diff --git a/public/react/src/modules/help/Help.js b/public/react/src/modules/help/Help.js
new file mode 100644
index 000000000..f04079bcc
--- /dev/null
+++ b/public/react/src/modules/help/Help.js
@@ -0,0 +1,77 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Switch, Route, Link } from 'react-router-dom';
+import { Affix, Menu, Row, Col } from "antd";
+import { SnackbarHOC } from 'educoder';
+
+import './Help.css';
+
+import CustomLoadable from "../../CustomLoadable";
+import {TPMIndexHOC} from "../tpm/TPMIndexHOC";
+
+const AboutUs = CustomLoadable(() => import('./AboutUs'));
+const ContactUs = CustomLoadable(() => import('./ContactUs'));
+const Cooperatives = CustomLoadable(() => import('./Cooperatives'));
+const Agreement = CustomLoadable(() => import('./Agreement'));
+const HelpCenter = CustomLoadable(() => import('./HelpCenter'));
+const Feedback = CustomLoadable(() => import('./Feedback'));
+
+class Help extends React.Component {
+ constructor (props) {
+ super(props);
+
+ this.state = {
+ type: props.match.params.type || 'about_us'
+ }
+ }
+
+ componentDidUpdate(prevProps) {
+ console.log('update', prevProps, this.props);
+ if(prevProps.match.params.type !== this.props.match.params.type){
+ this.setState({ type: this.props.match.params.type });
+ }
+ }
+
+ render() {
+ return (
+
+
+
+
+
+
+
+
+ 关于我们
+ 联系我们
+ 合作伙伴
+ 服务协议
+ 帮助中心
+ 意见反馈
+
+
+
+
+
+
+
+
+
+
+
+
+ ( ) }>
+
+
+
+
+
+
+
+ )
+ }
+}
+
+export default SnackbarHOC() (TPMIndexHOC ( Help ));
\ No newline at end of file
diff --git a/public/react/src/modules/help/HelpCenter.js b/public/react/src/modules/help/HelpCenter.js
new file mode 100644
index 000000000..077f4b90e
--- /dev/null
+++ b/public/react/src/modules/help/HelpCenter.js
@@ -0,0 +1,54 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Card } from "antd";
+import axios from 'axios';
+
+import { MarkdownToHtml } from 'educoder';
+
+class HelpCenter extends React.Component {
+ constructor (props) {
+ super(props);
+
+ this.state = {
+ loading: true,
+ content: null
+ }
+ }
+
+ componentDidMount(){
+ window.document.title = "帮助中心";
+ this.getContent();
+ }
+
+ getContent(){
+ axios.get("/helps/help_center.json").then((result) => {
+ if(result){
+ this.setState({
+ content: result.data.content,
+ loading: false
+ })
+ }
+ }).catch((error) => {
+ console.log(error);
+ this.setState({ loading: false });
+ })
+ }
+
+ render() {
+ let { loading, content } = this.state;
+
+ return (
+
+
+
+
+ { content && }
+
+
+
+
+ )
+ }
+}
+
+export default HelpCenter;
\ No newline at end of file
diff --git a/public/react/src/modules/home/shixunsHome.js b/public/react/src/modules/home/shixunsHome.js
index 690baf28b..a60080379 100644
--- a/public/react/src/modules/home/shixunsHome.js
+++ b/public/react/src/modules/home/shixunsHome.js
@@ -12,7 +12,7 @@ import SiderBar from '../tpm/SiderBar';
import UpgradeModals from '../modals/UpgradeModals';
-import { SnackbarHOC , getImageUrl} from 'educoder';
+import { SnackbarHOC , getImageUrl, configShareForIndex} from 'educoder';
import Slider from '@icedesign/base/lib/slider';
@@ -37,6 +37,7 @@ class ShixunsHome extends Component {
}
}
componentDidMount(){
+ configShareForIndex()
const upsystem=`/users/system_update.json`;
axios.get(upsystem).then((response)=>{
let updata=response.data;
diff --git a/public/react/src/modules/modals/Modals.js b/public/react/src/modules/modals/Modals.js
index b3029966e..b67bc0ddc 100644
--- a/public/react/src/modules/modals/Modals.js
+++ b/public/react/src/modules/modals/Modals.js
@@ -13,6 +13,7 @@ render() {
const antIcons =
return(
{
+ let url="/question_banks/my_courses.json";
+ axios.get(url,{params:{
+ search
+ }
+ }).then((result)=>{
+ this.setState({
+ courses:result.data.courses
+ })
+ }).catch((error)=>{
+ console.log(error);
+ })
+ }
+
+ onSearchChange=(e)=>{
+ this.setState({
+ search:e.target.value
+ })
+ // this.onupdatalist(e.target.value)
+ }
+
+ onSearch=(search)=>{
+ this.onupdatalist(search)
+ }
+
+
+ onChange=(e)=>{
+ this.setState({
+ Radiolist:e.target.value
+ })
+ }
+
+ submitInfo=()=>{
+ this.setState({
+ smallisSpin:true
+ })
+ let{Radiolist}=this.state;
+ let url=`/question_banks/send_to_course.json`;
+ let object_id=this.props.checkBoxValues;
+ let object_type=this.props.category;
+ if(Radiolist===undefined){
+ this.setState({
+ showcheck:true,
+ smallisSpin:false
+ })
+ }else{
+ axios.post(url,{
+ object_id: object_id,
+ object_type:object_type,
+ course_id:Radiolist
+ }
+ ).then((result)=>{
+ this.setState({
+ smallisSpin:false
+ })
+ if(result.data.status===0){
+ this.props.showNotification(result.data.message)
+ this.props.topicscancelmodel()
+ this.props.updataslist()
+ }else{
+ this.props.showNotification(result.data.message)
+ }
+ }).catch((error)=>{
+ console.log(error)
+ this.setState({
+ smallisSpin:false
+ })
+ })
+ }
+
+ }
+ render(){
+ let{courses,Radiolist,showcheck,smallisSpin}= this.state;
+
+ const radioStyle = {
+ display: 'block',
+ height: '30px',
+ lineHeight: '30px',
+ };
+
+ return(
+
+
+
+
+
+
+
+ 温馨提示:选择的题将会发送到指定课堂
+
+
+
+
+
+
+ {
+ courses && courses.map((item,key)=>{
+ return(
+
+
+ {item.course_name}
+
+
+ )
+ })
+ }
+
+
+
+ {showcheck===true?
请先选择课堂
:""}
+
+
+
+
+ )
+ }
+
+}
+export default SendTopics;
\ No newline at end of file
diff --git a/public/react/src/modules/modals/WordNumberTextarea.css b/public/react/src/modules/modals/WordNumberTextarea.css
index 9bd8c820c..7599f46ea 100644
--- a/public/react/src/modules/modals/WordNumberTextarea.css
+++ b/public/react/src/modules/modals/WordNumberTextarea.css
@@ -10,9 +10,8 @@
resize:none; /*禁止拉伸*/
border: none; /*去掉默认边框*/
width: 100%;
- height:150px;
+ height:130px;
border:none;
- padding: 10px;
display: block;
}
@@ -26,17 +25,14 @@
height: auto;
border: 1px solid rgba(234,234,234,1);
border-radius: 0.125rem;
- margin: 0.31rem;
- padding: 0.19rem;
+ margin: 10px 10px 0px 10px;
+ padding: 10px 10px 5px 10px;
backgroud:rgba(234,234,234,1);
- padding-bottom: 10px;
- padding-right: 10px;
-
}
.WordNumberTextarea-count {
display: inline-block;
float: right;
- font-size: 0.28rem;
+ font-size: 16px;
color: #adadad;
padding-right: 0.25rem;
}
diff --git a/public/react/src/modules/modals/WordNumberTextarea.js b/public/react/src/modules/modals/WordNumberTextarea.js
index 52711de2c..f93dff978 100644
--- a/public/react/src/modules/modals/WordNumberTextarea.js
+++ b/public/react/src/modules/modals/WordNumberTextarea.js
@@ -14,7 +14,7 @@ render() {
onInput={(e)=>this.props.onInput(e)}
maxlength={this.props.maxlength}
/>
- {this.props.value===undefined?0:this.props.value.length} /{this.props.maxlength}
+ {this.props.value===undefined||this.props.value===null?0:this.props.value.length} /{this.props.maxlength}
)
}
diff --git a/public/react/src/modules/moop_cases/CaseNew.js b/public/react/src/modules/moop_cases/CaseNew.js
index ae9ef4f6f..2fbe993bc 100644
--- a/public/react/src/modules/moop_cases/CaseNew.js
+++ b/public/react/src/modules/moop_cases/CaseNew.js
@@ -49,7 +49,7 @@ class CaseNew extends Component{
onAttachmentRemove = (file, stateName) => {
if(!file.percent || file.percent == 100){
this.props.confirm({
- content: '是否确认删除?',
+ content: '是否确认删除?',
onOk: () => {
this.deleteAttachment(file, stateName)
},
@@ -244,7 +244,10 @@ class CaseNew extends Component{
// 选择标签
changeType=(type)=>{
+ // console.log(this.state.casesTags);
+ // debugger
let tags = [];
+
if(this.state.casesTags.indexOf(type) > -1){
tags = this.state.casesTags.filter(item => item != type);
}else{
diff --git a/public/react/src/modules/moop_cases/css/moopCases.css b/public/react/src/modules/moop_cases/css/moopCases.css
index 7a58cee61..932ea0db2 100644
--- a/public/react/src/modules/moop_cases/css/moopCases.css
+++ b/public/react/src/modules/moop_cases/css/moopCases.css
@@ -1,172 +1,172 @@
-.winput-300-35{
- width: 300px;
- height: 35px;
- padding: 5px;
- box-sizing: border-box;
-}
-.library_nav li {
- float: left;
- cursor: pointer;
- margin-right: 30px;
- position: relative;
- color: #05101A;
- height: 40px;
- line-height: 20px;
- font-size: 16px;
-}
-.library_nav li.active a, .library_nav li:hover a{
- color: #4cacff!important;
-}
-.library_list {
- margin-bottom: 30px;
-}
-.library_list_item:hover {
- box-shadow: 0px 4px 8px rgba(158,158,158,0.16);
-}
-.library_list_item {
- background: #fff;
- padding: 20px 30px;
- margin-bottom: 30px;
- display: flex;
-}
-.library_list_item .library_l_name {
- max-width: 600px;
- float: left;
-}
-
-.edu-activity-red {
- background-color: #FC2B6A;
- color: #fff!important;
- cursor: pointer;
- border: 1px solid #FC2B6A;
- line-height: 17px;
-}
-.edu-activity-green {
- background-color: green;
- color: #fff!important;
- cursor: pointer;
- border: 1px solid green;
- line-height: 17px;
-}
-.edu-activity-orange {
- background-color: #ff6800;
- color: #fff!important;
- cursor: pointer;
- border: 1px solid #ff6800;
- line-height: 17px;
-}
-.edu-activity-blue {
- background-color: #4CACFF;
- color: #fff!important;
- cursor: pointer;
- border: 1px solid #4CACFF;
- line-height: 17px;
-}
-.edu-activity-orange-sub {
- background-color: #FF781B;
- color: #fff!important;
- cursor: pointer;
- border: 1px solid #ff6800;
- line-height: 17px;
-}
-
-.pointsBtn {
- width: 70px;
- height: 70px;
- background-color: #4cacff;
- border-radius: 50%;
- color: #fff;
- text-align: center;
- margin: 0 auto;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- padding: 2px 0;
- cursor: pointer;
- line-height: 22px;
- padding-top: 12px;
-}
-.pointedBtn{
- background: #BCD1E3;
- cursor: default
-}
-.pointsBtn span{
- display: block;
-}
-.upload_Title {
- position: relative;
- margin-right: 20px;
- float: left;
- line-height: 35px;
- font-size: 16px;
- width: 56px;
- color:rgba(0, 0, 0, 0.85);
- text-align: center
-}
-.upload_Title.must:before {
- display: inline-block;
- margin-right: 4px;
- color: #f5222d;
- font-size: 14px;
- font-family: SimSun, sans-serif;
- line-height: 1;
- content: '*';
-}
-.upload_Title:after{
- content: ':';
- position: relative;
- top: -0.5px;
- margin: 0 8px 0 2px;
-}
-.libraries_tab li {
- width: 120px;
- height: 35px;
- line-height: 33px;
- border-radius: 18px;
- border: 1px solid #4C98FF;
- color: #4C98FF;
- cursor: pointer;
- margin-right: 30px;
- float: left;
- text-align: center;
-}
-.libraries_tab li.active {
- background: #4C98FF;
- color: #fff;
-}
-.librariesField .ant-upload{
- width: 100%;
- background: #F2F9FF;
- justify-content: center;
- align-items: center;
- display: -webkit-flex;
- text-align: center;
- height: 120px!important;
- border-radius: 4px;
- border: 1px dashed #4cacff!important;
- display: block;
- cursor: pointer;
-}
-.librariesField .ant-upload.ant-upload-drag{
- border:none!important;
-}
-.uploadImage .ant-upload.ant-upload-select-picture-card{
- width:120px;
- height: 90px;
-}
-.uploadImage .ant-upload.ant-upload-select-picture-card > .ant-upload{
- padding:0px!important;
-}
-.successPage {
- justify-content: center;
- align-items: center;
- display: -webkit-flex;
- height: 570px;
- text-align: center;
- margin-bottom: 50px;
-}
-.changebtn {
- width:166px;
- font-size: 16px;
- height: 45px;
- line-height: 45px;
+.winput-300-35{
+ width: 300px;
+ height: 35px;
+ padding: 5px;
+ box-sizing: border-box;
+}
+.library_nav li {
+ float: left;
+ cursor: pointer;
+ margin-right: 30px;
+ position: relative;
+ color: #05101A;
+ height: 40px;
+ line-height: 20px;
+ font-size: 16px;
+}
+.library_nav li.active a, .library_nav li:hover a{
+ color: #4cacff!important;
+}
+.library_list {
+ margin-bottom: 30px;
+}
+.library_list_item:hover {
+ box-shadow: 0px 4px 8px rgba(158,158,158,0.16);
+}
+.library_list_item {
+ background: #fff;
+ padding: 20px 30px;
+ margin-bottom: 30px;
+ display: flex;
+}
+.library_list_item .library_l_name {
+ max-width: 600px;
+ float: left;
+}
+
+.edu-activity-red {
+ background-color: #FC2B6A;
+ color: #fff!important;
+ cursor: pointer;
+ border: 1px solid #FC2B6A;
+ line-height: 17px;
+}
+.edu-activity-green {
+ background-color: green;
+ color: #fff!important;
+ cursor: pointer;
+ border: 1px solid green;
+ line-height: 17px;
+}
+.edu-activity-orange {
+ background-color: #ff6800;
+ color: #fff!important;
+ cursor: pointer;
+ border: 1px solid #ff6800;
+ line-height: 17px;
+}
+.edu-activity-blue {
+ background-color: #4CACFF;
+ color: #fff!important;
+ cursor: pointer;
+ border: 1px solid #4CACFF;
+ line-height: 17px;
+}
+.edu-activity-orange-sub {
+ background-color: #FF781B;
+ color: #fff!important;
+ cursor: pointer;
+ border: 1px solid #ff6800;
+ line-height: 17px;
+}
+
+.pointsBtn {
+ width: 70px;
+ height: 70px;
+ background-color: #4cacff;
+ border-radius: 50%;
+ color: #fff;
+ text-align: center;
+ margin: 0 auto;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 2px 0;
+ cursor: pointer;
+ line-height: 22px;
+ padding-top: 12px;
+}
+.pointedBtn{
+ background: #BCD1E3;
+ cursor: default
+}
+.pointsBtn span{
+ display: block;
+}
+.upload_Title {
+ position: relative;
+ margin-right: 20px;
+ float: left;
+ line-height: 35px;
+ font-size: 16px;
+ /*width: 56px;*/
+ color:rgba(0, 0, 0, 0.85);
+ text-align: center
+}
+.upload_Title.must:before {
+ display: inline-block;
+ margin-right: 4px;
+ color: #f5222d;
+ font-size: 14px;
+ font-family: SimSun, sans-serif;
+ line-height: 1;
+ content: '*';
+}
+.upload_Title:after{
+ content: ':';
+ position: relative;
+ top: -0.5px;
+ margin: 0 8px 0 2px;
+}
+.libraries_tab li {
+ width: 120px;
+ height: 35px;
+ line-height: 33px;
+ border-radius: 18px;
+ border: 1px solid #4C98FF;
+ color: #4C98FF;
+ cursor: pointer;
+ margin-right: 30px;
+ float: left;
+ text-align: center;
+}
+.libraries_tab li.active {
+ background: #4C98FF;
+ color: #fff;
+}
+.librariesField .ant-upload{
+ width: 100%;
+ background: #F2F9FF;
+ justify-content: center;
+ align-items: center;
+ display: -webkit-flex;
+ text-align: center;
+ height: 120px!important;
+ border-radius: 4px;
+ border: 1px dashed #4cacff!important;
+ display: block;
+ cursor: pointer;
+}
+.librariesField .ant-upload.ant-upload-drag{
+ border:none!important;
+}
+.uploadImage .ant-upload.ant-upload-select-picture-card{
+ width:120px;
+ height: 90px;
+}
+.uploadImage .ant-upload.ant-upload-select-picture-card > .ant-upload{
+ padding:0px!important;
+}
+.successPage {
+ justify-content: center;
+ align-items: center;
+ display: -webkit-flex;
+ height: 570px;
+ text-align: center;
+ margin-bottom: 50px;
+}
+.changebtn {
+ width:166px;
+ font-size: 16px;
+ height: 45px;
+ line-height: 45px;
}
\ No newline at end of file
diff --git a/public/react/src/modules/page/Index.js b/public/react/src/modules/page/Index.js
index b2daa27e4..aa395e5cf 100644
--- a/public/react/src/modules/page/Index.js
+++ b/public/react/src/modules/page/Index.js
@@ -210,6 +210,8 @@ class Index extends Component {
{/* 区分下repo和evaluate模块的,以及子模块的 */}
{
// console.log('onResizeButtonClick')
}
+ onRunCodeTest = () => {
+ const vncContainer = this.refs['vncContainer']
+ if (vncContainer) {
+ vncContainer.showCodeEvaluate && vncContainer.showCodeEvaluate()
+ }
+ this.props.onRunCodeTest();
+ }
+ hideCodeEvaluate = () => {
+ const vncContainer = this.refs['vncContainer']
+ if (vncContainer) {
+ vncContainer.onBottomDrawerClose && vncContainer.onBottomDrawerClose()
+ }
+ }
render() {
- const { challenge, output_sets, onRunCodeTest, latest_output, record, st, readRepoTimeout,
+ const onRunCodeTest = this.onRunCodeTest
+ const { challenge, output_sets, latest_output, record, st, readRepoTimeout,
onTestSetHeaderClick, loading, codeLoading, shixun, vnc_url} = this.props
// if (output_sets && output_sets.test_sets) {
@@ -94,9 +108,28 @@ class MainContent extends Component {
*/}
- { showIframeContent && vnc_url ?
+ { showIframeContent && vnc_url ?
+
+
+
+
+ }
+ >
+
+
:
@@ -151,7 +184,7 @@ class MainContent extends Component {
diff --git a/public/react/src/modules/page/MainContentContainer.js b/public/react/src/modules/page/MainContentContainer.js
index 3a9643f8d..50eacdd4b 100644
--- a/public/react/src/modules/page/MainContentContainer.js
+++ b/public/react/src/modules/page/MainContentContainer.js
@@ -208,11 +208,12 @@ class MainContentContainer extends Component {
if (!this.props || !this.props.game
|| ( newProps.game.identifier !== this.props.game.identifier ) ) {
setTimeout(this.fetchRepositoryCode( newProps), 1500);
- } else if ( this.props.challenge.pathIndex != newProps.challenge.pathIndex
- && newProps.challenge.pathIndex !== -1) { // 切换到只读文件
+ }
+ // else if ( this.props.challenge.pathIndex != newProps.challenge.pathIndex
+ // && newProps.challenge.pathIndex !== -1) { // 切换到只读文件
// pathIndex切换
- setTimeout(this.fetchRepositoryCode( newProps), 1500);
- }
+ // setTimeout(this.fetchRepositoryCode( newProps), 1500);
+ // }
}
if (newProps.myshixun) {
var stageId = newProps.match.params.stageId;
@@ -231,9 +232,13 @@ class MainContentContainer extends Component {
// 切换关卡时,停止轮训
this.oldGameIdentifier = prevProps.game.identifier;
this.doFileUpdateRequestOnCodeMirrorBlur(prevProps)
- } else if (challenge && (challenge.pathIndex || prevProps.challenge.pathIndex) && challenge.pathIndex != prevProps.challenge.pathIndex) {
+ }
+ // else if (this.props.shixun && this.props.shixun.code_edit_permission && this.state.currentPath != prevState.currentPath) {
+ // this.doFileUpdateRequestOnCodeMirrorBlur(prevProps)
+ // }
+ else if (challenge && (challenge.pathIndex || prevProps.challenge.pathIndex) && challenge.pathIndex != prevProps.challenge.pathIndex) {
this.doFileUpdateRequestOnCodeMirrorBlur(prevProps)
- }
+ }
}
@@ -253,6 +258,9 @@ class MainContentContainer extends Component {
this.setState({ codeLoading: false });
return;
}
+ if (type && arg_path) {
+ this.doFileUpdateRequestOnCodeMirrorBlur(this.props)
+ }
const stageId = game.identifier
let path;
let isEditablePath = false;
@@ -383,9 +391,16 @@ class MainContentContainer extends Component {
// var fileUpdatePromise = this.doFileUpdateRequest(true)
// });
// }
+
+ window.addEventListener("beforeunload", this.beforeunload);
+
}
componentWillUnmount() {
// window.$(window).off( "unload" )
+ window.removeEventListener("beforeunload", this.beforeunload);
+ }
+ beforeunload = () => {
+ this.doFileUpdateRequestOnCodeMirrorBlur()
}
@@ -535,7 +550,9 @@ class MainContentContainer extends Component {
this.oldRepositoryCode = codeContent;
let argPath;
- if (challenge.pathIndex === -1) { // 当前是只读文件
+ if (this.props.shixun && this.props.shixun.code_edit_permission == true) {
+ argPath = this.state.currentPath
+ } else if (challenge.pathIndex === -1) { // 当前是只读文件
argPath = challenge.multiPath === true ? challenge.path[0] : challenge.path
} else {
argPath = challenge.multiPath === true ? challenge.path[challenge.pathIndex] : challenge.path
@@ -624,6 +641,11 @@ class MainContentContainer extends Component {
gameBuilding: false
})
}
+ onPathChange = (index, isDropDown) => {
+ this.props.onPathChange(index, () => {
+ isDropDown && this.fetchRepositoryCode()
+ })
+ }
onRunCodeTest() {
// tipContent(0, 100, 30, 360, 1)
// return; // for test
@@ -634,7 +656,7 @@ class MainContentContainer extends Component {
showDialog({
contentText: '需要先切回可编辑的文件才可评测,确认要现在切换吗?',
callback: () => {
- onPathChange(0)
+ this.onPathChange(0, true)
handleGdialogClose();
}
})
@@ -957,7 +979,9 @@ class MainContentContainer extends Component {
onRepositoryCodeUpdate={this.onRepositoryCodeUpdate} onRunCodeTest={this.onRunCodeTest}
codemirrorDidMount={this.codemirrorDidMount} fetchRepositoryCode={this.fetchRepositoryCode}
showResetCodeDialog={this.showResetCodeDialog} showResetPassedCodeDialog={this.showResetPassedCodeDialog}
- doFileUpdateRequestOnCodeMirrorBlur={this.doFileUpdateRequestOnCodeMirrorBlur} >
+ doFileUpdateRequestOnCodeMirrorBlur={this.doFileUpdateRequestOnCodeMirrorBlur}
+ onPathChange={this.onPathChange}
+ >
);
diff --git a/public/react/src/modules/page/VNC.css b/public/react/src/modules/page/VNC.css
new file mode 100644
index 000000000..cf47d7207
--- /dev/null
+++ b/public/react/src/modules/page/VNC.css
@@ -0,0 +1,17 @@
+.float_button {
+ background-image: url(./images/float_switch.jpg);
+ height: 112px;
+ width: 38px;
+ position: absolute;
+ left: -38px;
+ top: 32%;
+ cursor: pointer;
+}
+.float_button .text {
+ position: relative;
+ writing-mode: vertical-rl;
+ top: 36px;
+ color: #fff;
+ left: 13px;
+ user-select: none;
+}
\ No newline at end of file
diff --git a/public/react/src/modules/page/VNCContainer.js b/public/react/src/modules/page/VNCContainer.js
new file mode 100644
index 000000000..3a85976bb
--- /dev/null
+++ b/public/react/src/modules/page/VNCContainer.js
@@ -0,0 +1,327 @@
+import React, { Component } from 'react';
+import axios from 'axios'
+import { Spin, Icon } from 'antd'
+import ClipboardJS from 'clipboard'
+
+import VNCDisplay from './VNCDisplay'
+import FloatButton from './component/FloatButton'
+import SecondDrawer from './component/SecondDrawer'
+import RepoTree from './component/repo/RepoTree'
+import TPIMonaco from './component/monaco/TPIMonaco'
+import notEditablePathImg from '../../images/tpi/notEditablePath.png'
+
+import { Drawer } from "antd";
+
+import './VNC.css'
+const $ = window.$;
+const firstDrawerWidth = 260;
+class VNCContainer extends Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ fileTreeSelectedKeys: [],
+ repositoryCode: '',
+ displayKey: 1,
+ vnc_reseting: false,
+
+ }
+ }
+ componentDidMount() {
+ if (!this.clipboard) {
+ const clipboard = new ClipboardJS('.copybtn');
+ clipboard.on('success', (e) => {
+ this.props.showSnackbar('复制成功')
+ });
+ this.clipboard = clipboard
+ }
+
+ }
+ getSecondDrawerWidth = () => {
+ return $('#game_right_contents').width() - firstDrawerWidth
+ }
+ renderSecondDrawerChildren = () => {
+ const { readingCodeLoading, repositoryCode } = this.state;
+ const height = $(window).height() - 130
+
+ const isEditablePath = false;
+ return (
+
+
+ );
+ }
+ fetchReadRepositoryCode = (path) => {
+ const status = 1
+ const fetchRepoCodeUrl = `/tasks/${this.props.game.identifier}/rep_content.json?path=${path}&status=${status}`
+ this.setState({ readingCodeLoading: true });
+ axios.get(fetchRepoCodeUrl, {
+ }).then((fetchReadRepositoryCodeResponse) => {
+
+
+ if (fetchReadRepositoryCodeResponse.data.content || fetchReadRepositoryCodeResponse.data.content == "") {
+ this.setState({
+ repositoryCode: fetchReadRepositoryCodeResponse.data.content,
+ readingCodeLoading: false
+ })
+ } else {
+ this.setState({ readingCodeLoading: false });
+ }
+ // this.setState({ isEditablePath, currentPath: path });
+
+ }).catch(error =>{
+ console.log(error)
+ this.setState({ readingCodeLoading: false });
+ this.props.showSnackbar(`服务端异常,请联系管理员!`);
+ })
+ }
+ onTreeSelect = (selectedKeys, info) => {
+ const isLeaf = info.node.props.isLeaf;
+ if (isLeaf) { // 叶子节点
+ selectedKeys.length && this.setState({
+ fileTreeSelectedKeys: selectedKeys
+ })
+ this.refs["secondDrawer"].showSecondDrawer()
+
+ console.log('leaf clicked')
+ const nodePath = info.node.props.eventKey;
+ if (nodePath) {
+ const filetype = nodePath.split('.').pop().toLowerCase();
+ if (filetype == 'jpg' || filetype == 'png' || filetype == 'gif' || filetype == 'jpeg'
+ || filetype == 'jar' || filetype == 'exe'
+ || filetype == 'doc' || filetype == 'pdf' || filetype == 'xsl' || filetype == 'ppt') {
+ this.props.showSnackbar(`不支持加载${filetype}类型的文件。`)
+ return;
+ }
+ this.fetchReadRepositoryCode(nodePath);
+ } else {
+ console.error('no eventKey:', info.node)
+ }
+ }
+ }
+ onBottomDrawerClose = () => {
+ this.setState({ bottomDrawer: false })
+ }
+ swtichBottomDrawer = () => {
+ this.setState({ bottomDrawer: !this.state.bottomDrawer })
+ }
+ showCodeEvaluate = () => {
+ this.setState({ bottomDrawer: true })
+ }
+ onResetVNC = () => {
+ if (this.state.vnc_reseting) return;
+ // 桌面系统将恢复到初始状态,您在系统中创建的数据可能会丢失
+ // 请确保您的数据已保存(如:版本库代码已推送到服务器)
+ // 是否确认重置?
+ this.props.confirm({
+ content:
+
桌面系统将恢复到初始状态,您在系统中创建的数据可能会丢失
+
请确保您的数据已保存(如:版本库代码已推送到服务器)
+
是否确认重置?
+
,
+ onOk: () => {
+ const url = `/tasks/${this.props.game.identifier}/reset_vnc_link.json`
+ this.setState({ vnc_reseting: true })
+ axios.get(url, {
+ }).then((response) => {
+ if (response.data.data && response.data.data.vnc_url) {
+ // reset
+ this.setState({
+ displayKey: this.state.displayKey + 1,
+ vnc_url: response.data.data.vnc_url,
+ vnc_reseting: false
+ })
+ } else {
+ }
+ // this.setState({ isEditablePath, currentPath: path });
+
+ }).catch(error =>{
+ console.log(error)
+ this.setState({ vnc_reseting: false });
+ this.props.showSnackbar(`服务端异常,请联系管理员!`);
+ })
+
+ console.log('doooo')
+ },
+ onCancel() {
+ console.log('Cancel');
+ },
+ })
+ }
+
+ /*
+ selectedKeys={fileTreeSelectedKeys}
+ onSelect={onTreeSelect}
+ */
+ render() {
+ const { challenge, vnc_url, git_url } = this.props
+
+ const secondDrawerChildren = this.renderSecondDrawerChildren();
+ return (
+
+
+
+
+
+
+
+ {/* */}
+ {this.state.vnc_reseting ?
+ : }
+ 重置桌面系统
+
+
+ {/*
+ */}
+
+
+
+
+
+ {
+ if (visible) {
+ const canvas = $('.vncDisply canvas')[0]
+ canvas && canvas.focus()
+ }
+
+ }}
+ >
+ { this.props.codeEvaluate }
+
+ 测试集
+
+
+
+
+ );
+ }
+}
+
+export default VNCContainer;
diff --git a/public/react/src/modules/page/VNCDisplay.js b/public/react/src/modules/page/VNCDisplay.js
index 1c65e6b4d..536ba43cb 100644
--- a/public/react/src/modules/page/VNCDisplay.js
+++ b/public/react/src/modules/page/VNCDisplay.js
@@ -6,6 +6,7 @@ const $ = window.$;
// const showIframeContent = window.location.search.indexOf('vnc=1') != -1;
class VNCDisplay extends Component {
componentDidMount() {
+ console.log('vnc init')
console.log(RFB)
let rfb;
@@ -131,19 +132,19 @@ class VNCDisplay extends Component {
return (
-
+
-
+
+
+
+ {this.props.children}
);
}
diff --git a/public/react/src/modules/page/component/FloatButton.js b/public/react/src/modules/page/component/FloatButton.js
new file mode 100644
index 000000000..4e7f794c6
--- /dev/null
+++ b/public/react/src/modules/page/component/FloatButton.js
@@ -0,0 +1,24 @@
+import React, { Component } from 'react';
+
+const $ = window.$;
+class FloatButton extends Component {
+ componentDidMount() {
+
+
+ }
+
+ render() {
+ const { challenge, vnc_url, children, className } = this.props
+
+ return (
+
+
+ {children || '版本库'}
+
+ );
+ }
+}
+
+export default FloatButton;
diff --git a/public/react/src/modules/page/component/SecondDrawer.js b/public/react/src/modules/page/component/SecondDrawer.js
new file mode 100644
index 000000000..f0551bf5e
--- /dev/null
+++ b/public/react/src/modules/page/component/SecondDrawer.js
@@ -0,0 +1,101 @@
+import React from "react";
+import ReactDOM from "react-dom";
+import { Drawer } from "antd";
+import FloatButton from './FloatButton'
+import PropTypes from 'prop-types';
+
+class SecondDrawer extends React.Component {
+ state = { visible: false, childrenDrawer: false };
+
+ showDrawer = () => {
+ this.setState({
+ visible: true
+ });
+ };
+
+ onClose = () => {
+ this.setState({
+ visible: false
+ });
+ };
+
+ showSecondDrawer = () => {
+ this.setState({
+ childrenDrawer: true
+ });
+ };
+
+ onChildrenDrawerClose = () => {
+ this.setState({
+ childrenDrawer: false
+ });
+ };
+
+ swtichFirstDrawer = () => {
+ this.setState({
+ visible: !this.state.visible,
+ childrenDrawer: false
+ });
+ };
+ componentDidMount() {
+ this.setState({ visible: true }, () => {
+ this.setState({ visible: false });
+ });
+ }
+ render() {
+ const { floatText, maskClosable, children, secondDrawerChildren, firstDrawerWidth, getSecondDrawerWidth
+ ,firstDrawerClassName, secondDrawerClassName
+ } = this.props
+ const secondDrawerWidth = getSecondDrawerWidth();
+ // 180 不知道为什么会偏移 180px
+ return (
+
+ {/*
+ Two-level drawer
+ */}
+ {floatText}
+ { children }
+
+
+ { secondDrawerChildren }
+ {/*
+ Cancel
+ */}
+
+
+ );
+ }
+}
+
+SecondDrawer.propTypes = {
+ floatText: PropTypes.string,
+ maskClosable: PropTypes.bool,
+ secondDrawerChildren: PropTypes.element,
+};
+// firstDrawerWidth={firstDrawerWidth}
+// getSecondDrawerWidth={this.getSecondDrawerWidth}
+export default SecondDrawer
\ No newline at end of file
diff --git a/public/react/src/modules/page/component/WebSSHTimer.js b/public/react/src/modules/page/component/WebSSHTimer.js
index b1e67f0be..844c29785 100644
--- a/public/react/src/modules/page/component/WebSSHTimer.js
+++ b/public/react/src/modules/page/component/WebSSHTimer.js
@@ -145,6 +145,9 @@ class WebSSHTimer extends Component {
}
// 重置命令行的时候调用的接口,会删pod
closeWebssh = (callback) => {
+ // 先关socket
+ this.closeWebsshSocket()
+
const { game } = this.props;
// const url = `/api/v1/games/${game.identifier}/close_webssh`
const url = `/tasks/${game.identifier}/close_webssh.json`
diff --git a/public/react/src/modules/page/component/monaco/TPIMonaco.js b/public/react/src/modules/page/component/monaco/TPIMonaco.js
index 83d55915e..6d53cfac7 100644
--- a/public/react/src/modules/page/component/monaco/TPIMonaco.js
+++ b/public/react/src/modules/page/component/monaco/TPIMonaco.js
@@ -222,7 +222,7 @@ class TPIMonaco extends Component {
// https://github.com/Microsoft/monaco-editor/issues/539
window.monaco.editor.setModelLanguage(editor_monaco.getModel(), lang)
} else if (prevProps.isEditablePath != this.props.isEditablePath) {
- if (this.props.isEditablePath) {
+ if (this.props.isEditablePath || this.props.shixun && this.props.shixun.code_edit_permission == true) {
editor_monaco.updateOptions({readOnly: false})
} else {
editor_monaco.updateOptions({readOnly: true})
@@ -271,6 +271,7 @@ class TPIMonaco extends Component {
const lang = getLanguageByMirrorName(this.props.mirror_name);
const editor = window.monaco.editor.create(document.getElementById('extend-challenge-file-edit'), {
value: value,
+ readOnly: !this.props.isEditablePath && this.props.shixun && this.props.shixun.code_edit_permission != true,
// 属性说明
// http://testeduplus2.educoder.net/react/build/static/node_modules/_monaco-editor@0.15.6@monaco-editor/esm/vs/editor/common/config/commonEditorConfig.js
// https://github.com/Microsoft/monaco-editor/issues/29
@@ -303,7 +304,7 @@ class TPIMonaco extends Component {
notCallCodeMirrorOnChangeFlag = false
return;
}
- this.props.onRepositoryCodeUpdate(editor.getValue())
+ this.props.onRepositoryCodeUpdate && this.props.onRepositoryCodeUpdate(editor.getValue())
})
this.props.codemirrorDidMount && this.props.codemirrorDidMount()
diff --git a/public/react/src/modules/page/component/repo/RepoTree.js b/public/react/src/modules/page/component/repo/RepoTree.js
new file mode 100644
index 000000000..fbf1286c3
--- /dev/null
+++ b/public/react/src/modules/page/component/repo/RepoTree.js
@@ -0,0 +1,54 @@
+import React, { useState, useEffect, useContext, useRef, memo } from 'react';
+
+// import { Tree } from 'antd';
+// const { TreeNode } = Tree;
+import Tree, { TreeNode } from 'rc-tree';
+import 'rc-tree/assets/index.css';
+
+const $ = window.$;
+export default function RepoTree(props) {
+ const { fileTreeData, onLoadData, onTreeSelect, fileTreeSelectedKeys, loadRepoFiles } = props;
+ const [expandedKeys, setExpandedKeys] = useState([])
+ useEffect(() => {
+ loadRepoFiles()
+ }, [])
+
+ if (!fileTreeData || fileTreeData.length === 0) {
+ return ""
+ }
+
+ const onExpand = (expandedKeys) => {
+ // console.log('onExpand', arguments);
+ // if not set autoExpandParent to false, if children expanded, parent can not collapse.
+ // or, you can remove all expanded children keys.
+ setExpandedKeys(expandedKeys)
+ }
+
+ const loop = (data) => {
+ return data.map((item) => {
+ if (item.children) {
+ return
{loop(item.children)} ;
+ }
+ return (
+
+ );
+ });
+ };
+ const treeNodes = loop(fileTreeData);
+
+
+ // selectable={false}
+ return (
+
+ {treeNodes}
+
+ )
+}
diff --git a/public/react/src/modules/page/images/float_switch.jpg b/public/react/src/modules/page/images/float_switch.jpg
new file mode 100644
index 000000000..12fc6f878
Binary files /dev/null and b/public/react/src/modules/page/images/float_switch.jpg differ
diff --git a/public/react/src/modules/page/main/ActionView.js b/public/react/src/modules/page/main/ActionView.js
index 4933ba0ac..5504b5e7e 100644
--- a/public/react/src/modules/page/main/ActionView.js
+++ b/public/react/src/modules/page/main/ActionView.js
@@ -1,204 +1,206 @@
-import React, { Component } from 'react';
-import { Link } from 'react-router-dom'
-
-import { withStyles } from 'material-ui/styles';
-import Button from 'material-ui/Button';
-
-import Tooltip from 'material-ui/Tooltip';
-
-import './ActionView.css'
-
-/*
-
- color: #1B4061 !important;
- background-color: transparent;
- border: 1px solid #1B4061 !important;
- */
-const styles = theme => ({
- button: {
- margin: theme.spacing.unit,
- border: '1px solid #1B4061',
- color: '#1B4061',
- height: '30px',
- padding: '0 16px',
- '&:hover': {
- color: '#4CACFF',
- border: '1px solid #4CACFF'
- }
- },
- hoverButton: {
- margin: theme.spacing.unit,
- height: '30px',
- padding: '0 16px',
-
- color: '#4CACFF',
- border: '1px solid #4CACFF'
- },
- buttonText: {
- color: '#1B4061 !important',
- '&:hover': {
- color: '#1B4061',
- }
- }
-});
-
-class ActionView extends Component {
-
- constructor(props) {
- super(props)
-
- }
-
- componentDidMount() {
- // request
- window._tpiWidthResize = () => {
- const _w = window.$('#actionView').width();
- // if (_w < 446) {
- // window.$('#time-consuming').hide()
- // // window.$('#time-consuming').hide()
- // } else if (_w < 746) {
- // // 文字放出来之前是 580
- // window.$('#time-consuming').show()
- // window.$('.time_limit').hide()
- // } else {
- // window.$('#time-consuming').show()
- // window.$('.time_limit').show()
- // }
- }
- }
-
- showWebDisplay(challenge) {
- window.open(challenge.webDisplayUrl, '_blank');
- }
- /*
耗时:0 天 3 小时 11 分钟 57 秒 */
- render() {
- const { onRunCodeTest, onShowPrevStage, onShowNextStage, gameBuilding
- , game, classes, st, shixun, record, challenge, time_limit, real_time_limit } = this.props;
- return (
-
-
-
- {!!time_limit &&
- {`本关最大执行时间:${real_time_limit}秒`}
- {!gameBuilding && record && }
- }
- {!gameBuilding && record ?
- //
- 本次评测耗时(编译、运行总时间):{ record } 秒
-
- : ""}
-
-
- {/*将第一个按钮改为visibility方式隐藏,不然加载时测评按钮会出现没有垂直居中的情况*/}
-
- this.showWebDisplay(challenge)}
- style={{ visibility: challenge.showWebDisplayButton ? '': 'hidden',
- minWidth: challenge.showWebDisplayButton ? '': '1px',
- width: challenge.showWebDisplayButton ? '': '1px',
- flex: `0 0 ${challenge.showWebDisplayButton ? '110px': '1px'}`
- }}
- id="showWebDisplayButton"
- // style={{ display: challenge.showWebDisplayButton ? 'flex': 'none'}}
- >
- 查看效果
-
-
-
- {
- !gameBuilding &&
- (game && !!game.prev_game) ?
-
-
- 上一关
-
-
- : ''}
-
- {/*未发布的都能跳转*/}
- { !gameBuilding &&
- ((game && (game.status === 2 || shixun.status < 2) || shixun && shixun.task_pass ) && !!game.next_game) ?
-
-
- 下一关
-
-
- : ''}
-
-
-
-
-
- );
- /*
-
离开
-
-
下一关
-
- onclick="training_task_submmit();"
-
-
- {game && !!game.prev_game ?
-
- 上一关
-
- : ''}
-
- {game && !!game.next_game ?
-
- 下一关
-
- : ''}
- */
- }
-}
-
-export default withStyles(styles)( ActionView );
+import React, { Component } from 'react';
+import { Link } from 'react-router-dom'
+
+import { withStyles } from 'material-ui/styles';
+import Button from 'material-ui/Button';
+
+import Tooltip from 'material-ui/Tooltip';
+
+import './ActionView.css'
+
+/*
+
+ color: #1B4061 !important;
+ background-color: transparent;
+ border: 1px solid #1B4061 !important;
+ */
+const styles = theme => ({
+ button: {
+ margin: theme.spacing.unit,
+ border: '1px solid #1B4061',
+ color: '#1B4061',
+ height: '30px',
+ padding: '0 16px',
+ '&:hover': {
+ color: '#4CACFF',
+ border: '1px solid #4CACFF'
+ }
+ },
+ hoverButton: {
+ margin: theme.spacing.unit,
+ height: '30px',
+ padding: '0 16px',
+
+ color: '#4CACFF',
+ border: '1px solid #4CACFF'
+ },
+ buttonText: {
+ color: '#1B4061 !important',
+ '&:hover': {
+ color: '#1B4061',
+ }
+ }
+});
+
+class ActionView extends Component {
+
+ constructor(props) {
+ super(props)
+
+ }
+
+ componentDidMount() {
+ // request
+ window._tpiWidthResize = () => {
+ const _w = window.$('#actionView').width();
+ // if (_w < 446) {
+ // window.$('#time-consuming').hide()
+ // // window.$('#time-consuming').hide()
+ // } else if (_w < 746) {
+ // // 文字放出来之前是 580
+ // window.$('#time-consuming').show()
+ // window.$('.time_limit').hide()
+ // } else {
+ // window.$('#time-consuming').show()
+ // window.$('.time_limit').show()
+ // }
+ }
+ }
+
+ showWebDisplay(challenge) {
+ window.open(challenge.webDisplayUrl, '_blank');
+ }
+ /*
耗时:0 天 3 小时 11 分钟 57 秒 */
+ render() {
+ const { onRunCodeTest, onShowPrevStage, onShowNextStage, gameBuilding
+ , game, classes, st, shixun, record, challenge, time_limit, real_time_limit } = this.props;
+
+ console.log(shixun)
+ return (
+
+
+
+ {!!time_limit &&
+ {`本关最大执行时间:${real_time_limit}秒`}
+ {!gameBuilding && record && }
+ }
+ {!gameBuilding && record ?
+ //
+ 本次评测耗时(编译、运行总时间):{ record } 秒
+
+ : ""}
+
+
+ {/*将第一个按钮改为visibility方式隐藏,不然加载时测评按钮会出现没有垂直居中的情况*/}
+
+ this.showWebDisplay(challenge)}
+ style={{ visibility: challenge.showWebDisplayButton ? '': 'hidden',
+ minWidth: challenge.showWebDisplayButton ? '': '1px',
+ width: challenge.showWebDisplayButton ? '': '1px',
+ flex: `0 0 ${challenge.showWebDisplayButton ? '110px': '1px'}`
+ }}
+ id="showWebDisplayButton"
+ // style={{ display: challenge.showWebDisplayButton ? 'flex': 'none'}}
+ >
+ 查看效果
+
+
+
+ {
+ !gameBuilding &&
+ (game && !!game.prev_game) ?
+
+
+ 上一关
+
+
+ : ''}
+
+ {/*未发布的都能跳转*/}
+ { !gameBuilding &&
+ ((game && (game.status === 2 || shixun.status < 2) || shixun && shixun.task_pass ) && !!game.next_game) ?
+
+
+ 下一关
+
+
+ : ''}
+
+
+
+ {(shixun&&!shixun.vnc || shixun&&shixun.vnc_evaluate) &&
}
+
+ );
+ /*
+
离开
+
+
下一关
+
+ onclick="training_task_submmit();"
+
+
+ {game && !!game.prev_game ?
+
+ 上一关
+
+ : ''}
+
+ {game && !!game.next_game ?
+
+ 下一关
+
+ : ''}
+ */
+ }
+}
+
+export default withStyles(styles)( ActionView );
diff --git a/public/react/src/modules/page/main/CodeEvaluateView.css b/public/react/src/modules/page/main/CodeEvaluateView.css
index 137174d95..9c07c4541 100644
--- a/public/react/src/modules/page/main/CodeEvaluateView.css
+++ b/public/react/src/modules/page/main/CodeEvaluateView.css
@@ -127,4 +127,11 @@
height: 10px;
margin: 5px 0;
float: right;
+}
+
+.-task-ces-info .inputTitle {
+ line-height: 16px;
+}
+.-task-ces-info .inputTitle .input{
+ white-space: pre-wrap;
}
\ No newline at end of file
diff --git a/public/react/src/modules/page/main/CodeEvaluateView.js b/public/react/src/modules/page/main/CodeEvaluateView.js
index a121a53d5..44a98ae77 100644
--- a/public/react/src/modules/page/main/CodeEvaluateView.js
+++ b/public/react/src/modules/page/main/CodeEvaluateView.js
@@ -227,7 +227,12 @@ class CodeEvaluateView extends Component {
测试输入:
-
")}}>
+
"))}}
+
+ >
+ {item.input}
+
@@ -307,14 +312,21 @@ class CodeEvaluateView extends Component {
测试结果
-
+ {this.props.inDrawer ?
+ {/*TODO 按钮大小改造,css*/}
+ {/* icon-guanbi */}
+
+
+
+
+ :
{/*TODO 按钮大小改造,css*/}
-
+ }
diff --git a/public/react/src/modules/page/main/CodeRepositoryView.js b/public/react/src/modules/page/main/CodeRepositoryView.js
index 135edcf6e..6da34c541 100644
--- a/public/react/src/modules/page/main/CodeRepositoryView.js
+++ b/public/react/src/modules/page/main/CodeRepositoryView.js
@@ -157,13 +157,13 @@ class CodeRepositoryView extends Component {
}
- onPathChange(index) {
+ onPathChange(index, isDropDown) {
const { challenge, onPathChange, doFileUpdateRequestOnCodeMirrorBlur } = this.props;
if (challenge.pathIndex !== index) {
// 切换时保存文件
// doFileUpdateRequestOnCodeMirrorBlur(true)
- onPathChange(index)
+ onPathChange(index, isDropDown)
}
}
@@ -174,7 +174,7 @@ class CodeRepositoryView extends Component {
const pathArray = path.forEach ? path : [path]
pathArray.forEach( (item, index) => {
domArray.push(
- this.onPathChange(index) } >
+
this.onPathChange(index, true) } >
{item}
)
})
@@ -501,7 +501,8 @@ class CodeRepositoryView extends Component {
} */}
+ style={{ backgroundImage: `url('${notEditablePathImg}')`
+ , display: (isEditablePath || this.props.shixun && this.props.shixun.code_edit_permission ? 'none' : 'block') }}>
{/**/}
{/* cm monaco 切换 */}
{/* */}
diff --git a/public/react/src/modules/page/main/CodeRepositoryViewContainer.js b/public/react/src/modules/page/main/CodeRepositoryViewContainer.js
index 8efe7b0e1..111ccd253 100644
--- a/public/react/src/modules/page/main/CodeRepositoryViewContainer.js
+++ b/public/react/src/modules/page/main/CodeRepositoryViewContainer.js
@@ -1,288 +1,318 @@
-
-import React, { Component } from 'react';
-
-import CodeRepositoryView from './CodeRepositoryView'
-
-import axios from 'axios'
-
-import './CodeRepositoryView.css'
-
-// 自己处理path,加上父节点的path, 这里是处理树节点了,所以是set key
-function addPrePath(treeData, parentNodePath) {
- return treeData.map(item => {
- return {
- ...item,
- key: `${parentNodePath}/${item.name}`
- }
- })
-}
-function getNewTreeData(treeData, curKey, child, level) {
- const loop = (data) => {
- data.forEach((item) => {
- // 这里不能用indexOf 同一级可能出现test目录和test.py文件
- if (item.key == curKey) {
- child = addPrePath(child, curKey);
- item.children = child;
- } else {
- if (item.children) {
- loop(item.children);
- }
- }
- });
- };
- loop(treeData);
-}
-
-function fileData2TreeData(repoFilesData) {
- const fileTreeData = [];
- repoFilesData.forEach((item) => {
- if (item.kind === 'file') {
- fileTreeData.push({
- key: item.path,
- name: item.name,
- isLeaf: true
- })
- } else {
- fileTreeData.push({
- key: item.path,
- name: item.name,
- // isLeaf: false
- })
- }
- })
- return fileTreeData;
-}
-
-class CodeRepositoryViewContainer extends Component {
-
- constructor(props) {
- super(props)
-
- this.showFilesDrawer = this.showFilesDrawer.bind(this)
- this.onRepositoryViewExpand = this.onRepositoryViewExpand.bind(this)
-
- this.state = {
- drawerOpen: false,
- loadingFirstRepoFiles: false,
- fileTreeData: "",
- fileTreeSelectedKeys: [],
- codeRepositoryViewExpanded: false,
- tabIndex: 0,
-
- settingDrawerOpen: false
- }
- }
- showSettingDrawer = (open) => {
- this.setState({settingDrawerOpen: open})
- }
- tabIndexChange = (index) => {
- this.setState({tabIndex: index});
- }
- onRepositoryViewExpand() {
- window.repository_extend_and_zoom();
- this.setState({
- evaluateViewExpanded: !this.state.evaluateViewExpanded
- }, () => {
- setTimeout(()=>{
- window.__tpiOnResize()
- }, 300)
- })
- }
-
- showFilesDrawer(open) {
- if (this.props.loading === true) {
- return;
- }
- if (!this.state.fileTreeData) {
- this.fetchRepoFiles();
- }
-
- this.setState({
- drawerOpen: open,
- })
- }
-
- componentWillReceiveProps(newProps, oldProps) {
-
- }
- componentDidMount() {
-
- }
-
- componentDidUpdate(prevProps, prevState, snapshot) {
- const { game, challenge } = this.props
- if (this.props.game && (!prevProps.game || prevProps.game.identifier !== this.props.game.identifier) ) {
- this.setState({
- fileTreeSelectedKeys: [ challenge.multiPath ? challenge.path[0] : challenge.path ]
- })
- // 初始化
- } else if (this.state.fileTreeSelectedKeys.length === 0 && challenge && challenge.path) {
- this.setState({
- fileTreeSelectedKeys: [ challenge.multiPath ? challenge.path[0] : challenge.path ]
- })
- } else if (challenge && prevProps && prevProps.challenge
- && challenge.pathIndex != prevProps.challenge.pathIndex
- && challenge.pathIndex !== -1) {
- this.setState({
- fileTreeSelectedKeys: [ challenge.multiPath ? challenge.path[challenge.pathIndex] : challenge.path ]
- })
- }
- }
-
- handleDialogClose() {
- this.setState({
- dialogOpen: false
- })
- }
- onLoadData = (treeNode) => {
- if (treeNode.props.children && treeNode.props.children.length) {
- return new Promise((resolve) => {
- resolve();
- });
- }
- return new Promise((resolve, reject) => {
- this.fetchRepoFiles(treeNode, resolve, reject)
- });
- }
- map2OldData = (treeData) => {
- if (!treeData || treeData.length === 0) return treeData;
- treeData = treeData.map(item => {
- return {
- kind: item.type == "blob" ? "file" : "dir", // blob->file tree->dir
- path: item.name,
- name: item.name
- }
- })
- return treeData;
- }
-
- fetchRepoFiles(treeNode, resolve, reject) {
- // http://localhost:3000/api/v1/games/829al3mst4fy/entries?path=src/step1&rev=master
- if (!this.props.challenge || !this.props.game) {
- return;
- }
- // var ar = this.props.challenge.path.split('/');
- // ar.length = ar.length - 2;
- // var _path = ar.join('/');
- var _path = treeNode ? treeNode.props.eventKey : '' ;
- if (_path.charAt(0) === '/') {
- _path = _path.substring(1)
- }
- // var url = `/api/v1/games/${this.props.game.identifier}/entries?path=${_path}&rev=master&gpid=${this.props.myshixun.gpid}`
- let url = `/myshixuns/${this.props.myshixun.identifier}/repository.json`
-
-
- if (!this.state.fileTreeData || this.state.fileTreeData.length === 0) {
- this.setState({
- loadingFirstRepoFiles: true,
- })
- }
- var that = this;
- axios.post(url, {
- path: _path
- // withCredentials: true,
- })
- .then((response) => {
- const repoFilesData = this.map2OldData(response.data.trees)
- if (!this.state.fileTreeData || this.state.fileTreeData.length === 0) { // 还没树节点,没加载过
-
- const fileTreeData = fileData2TreeData(repoFilesData)
- this.setState({
- fileTreeData,
- loadingFirstRepoFiles: false,
- });
- } else {
- var _treeNode = treeNode;
- var _eventKey = _treeNode.props.eventKey;
-
- const fileTreeData = that.state.fileTreeData;
- // 新的数组放置到treenode下
-
- const tempFileTreeData = fileData2TreeData(repoFilesData)
-
- getNewTreeData(fileTreeData, _eventKey, tempFileTreeData, 2);
- this.setState({
- fileTreeData,
- })
- }
-
- resolve && resolve();
-
- })
- .catch(function (error) {
- console.log(error);
- reject && reject();
- });
- }
- onTreeSelect = (selectedKeys, info) => {
- const isLeaf = info.node.props.isLeaf;
- if (isLeaf) { // 叶子节点
- selectedKeys.length && this.setState({
- fileTreeSelectedKeys: selectedKeys
- })
- const { fetchRepositoryCode, onPathChange, showSnackbar, challenge } = this.props;
-
- const nodePath = info.node.props.eventKey;
- // 设置pathIndex为-1,那么代码文件下拉可以切回可编辑的文件
- if (!challenge.multiPath) { // 单path任务 多path任务 path是数组
- if (challenge.path.trim() == nodePath.trim()) {
- if (challenge.pathIndex === 0) {
- showSnackbar(`当前编辑文件已经是${nodePath}`)
- } else {
- onPathChange(0)
- }
- return;
- } else {
- onPathChange(-1)
- }
- } else {
- let isCurrentFile = false;
- let cur_index = -1;
- if (challenge.path && challenge.path.forEach) {
- challenge.path.forEach((item, index) => {
- if (nodePath == item) {
- isCurrentFile = true;
- cur_index = index;
- }
- })
- }
- if (isCurrentFile) {
- onPathChange(cur_index)
- showSnackbar(`当前编辑文件已经是${nodePath}`)
- } else {
- onPathChange(-1)
- }
- }
- if (nodePath) {
- const filetype = nodePath.split('.').pop().toLowerCase();
- if (filetype == 'jpg' || filetype == 'png' || filetype == 'gif' || filetype == 'jpeg'
- || filetype == 'jar'
- || filetype == 'doc' || filetype == 'pdf' || filetype == 'xsl' || filetype == 'ppt') {
- showSnackbar(`不支持加载${filetype}类型的文件。`)
- return;
- }
- fetchRepositoryCode(null, nodePath, 1);
- } else {
- console.error('no eventKey:', info.node)
- }
- }
- }
-// /shixuns/mnf6b7z3/shixun_discuss?challenge_id=88
- render() {
-
- return (
-
- );
- }
-}
-
-export default CodeRepositoryViewContainer;
+
+import React, { Component } from 'react';
+
+import CodeRepositoryView from './CodeRepositoryView'
+
+import axios from 'axios'
+
+import './CodeRepositoryView.css'
+
+// 自己处理path,加上父节点的path, 这里是处理树节点了,所以是set key
+function addPrePath(treeData, parentNodePath) {
+ return treeData.map(item => {
+ return {
+ ...item,
+ key: `${parentNodePath}/${item.name}`
+ }
+ })
+}
+function getNewTreeData(treeData, curKey, child, level) {
+ const loop = (data) => {
+ data.forEach((item) => {
+ // 这里不能用indexOf 同一级可能出现test目录和test.py文件
+ if (item.key == curKey) {
+ child = addPrePath(child, curKey);
+ item.children = child;
+ } else {
+ if (item.children) {
+ loop(item.children);
+ }
+ }
+ });
+ };
+ loop(treeData);
+}
+
+function fileData2TreeData(repoFilesData) {
+ if(repoFilesData!=null){
+ const fileTreeData = [];
+ repoFilesData.forEach((item) => {
+ if (item.kind === 'file') {
+ fileTreeData.push({
+ key: item.path,
+ name: item.name,
+ isLeaf: true
+ })
+ } else {
+ fileTreeData.push({
+ key: item.path,
+ name: item.name,
+ // isLeaf: false
+ })
+ }
+ })
+ return fileTreeData;
+ }
+
+}
+
+class CodeRepositoryViewContainer extends Component {
+
+ constructor(props) {
+ super(props)
+
+ this.showFilesDrawer = this.showFilesDrawer.bind(this)
+ this.onRepositoryViewExpand = this.onRepositoryViewExpand.bind(this)
+
+ this.state = {
+ drawerOpen: false,
+ loadingFirstRepoFiles: false,
+ fileTreeData: "",
+ fileTreeSelectedKeys: [],
+ codeRepositoryViewExpanded: false,
+ tabIndex: 0,
+
+ settingDrawerOpen: false
+ }
+ }
+ showSettingDrawer = (open) => {
+ this.setState({settingDrawerOpen: open})
+ }
+ tabIndexChange = (index) => {
+ this.setState({tabIndex: index});
+ }
+ onRepositoryViewExpand() {
+ window.repository_extend_and_zoom();
+ this.setState({
+ evaluateViewExpanded: !this.state.evaluateViewExpanded
+ }, () => {
+ setTimeout(()=>{
+ window.__tpiOnResize()
+ }, 300)
+ })
+ }
+
+ showFilesDrawer(open) {
+ if (this.props.loading === true) {
+ return;
+ }
+ if (!this.state.fileTreeData) {
+ this.fetchRepoFiles();
+ }
+
+ this.setState({
+ drawerOpen: open,
+ })
+ }
+ loadRepoFiles = () => {
+ if (!this.state.fileTreeData) {
+ this.fetchRepoFiles();
+ }
+ }
+
+ componentWillReceiveProps(newProps, oldProps) {
+
+ }
+ componentDidMount() {
+
+ }
+
+ componentDidUpdate(prevProps, prevState, snapshot) {
+ const { game, challenge } = this.props
+ if (this.props.game && (!prevProps.game || prevProps.game.identifier !== this.props.game.identifier) ) {
+ this.setState({
+ fileTreeSelectedKeys: [ challenge.multiPath ? challenge.path[0] : challenge.path ]
+ })
+ // 初始化
+ } else if (this.state.fileTreeSelectedKeys.length === 0 && challenge && challenge.path) {
+ this.setState({
+ fileTreeSelectedKeys: [ challenge.multiPath ? challenge.path[0] : challenge.path ]
+ })
+ } else if (challenge && prevProps && prevProps.challenge
+ && challenge.pathIndex != prevProps.challenge.pathIndex
+ && challenge.pathIndex !== -1) {
+ this.setState({
+ fileTreeSelectedKeys: [ challenge.multiPath ? challenge.path[challenge.pathIndex] : challenge.path ]
+ })
+ }
+ }
+
+ handleDialogClose() {
+ this.setState({
+ dialogOpen: false
+ })
+ }
+ onLoadData = (treeNode) => {
+ if (treeNode.props.children && treeNode.props.children.length) {
+ return new Promise((resolve) => {
+ resolve();
+ });
+ }
+ return new Promise((resolve, reject) => {
+ this.fetchRepoFiles(treeNode, resolve, reject)
+ });
+ }
+ map2OldData = (treeData) => {
+ if (!treeData || treeData.length === 0) return treeData;
+ treeData = treeData.map(item => {
+ return {
+ kind: item.type == "blob" ? "file" : "dir", // blob->file tree->dir
+ path: item.name,
+ name: item.name
+ }
+ })
+ return treeData;
+ }
+
+ fetchRepoFiles(treeNode, resolve, reject) {
+ // http://localhost:3000/api/v1/games/829al3mst4fy/entries?path=src/step1&rev=master
+ if (!this.props.challenge || !this.props.game) {
+ return;
+ }
+ // var ar = this.props.challenge.path.split('/');
+ // ar.length = ar.length - 2;
+ // var _path = ar.join('/');
+ var _path = treeNode ? treeNode.props.eventKey : '' ;
+ if (_path.charAt(0) === '/') {
+ _path = _path.substring(1)
+ }
+ // var url = `/api/v1/games/${this.props.game.identifier}/entries?path=${_path}&rev=master&gpid=${this.props.myshixun.gpid}`
+ let url = `/myshixuns/${this.props.myshixun.identifier}/repository.json`
+
+
+ if (!this.state.fileTreeData || this.state.fileTreeData.length === 0) {
+ this.setState({
+ loadingFirstRepoFiles: true,
+ })
+ }
+ var that = this;
+ axios.post(url, {
+ path: _path
+ // withCredentials: true,
+ })
+ .then((response) => {
+ const repoFilesData = this.map2OldData(response.data.trees)
+ if (!this.state.fileTreeData || this.state.fileTreeData.length === 0) { // 还没树节点,没加载过
+
+ const fileTreeData = fileData2TreeData(repoFilesData)
+ this.setState({
+ fileTreeData,
+ loadingFirstRepoFiles: false,
+ });
+ } else {
+ var _treeNode = treeNode;
+ var _eventKey = _treeNode.props.eventKey;
+
+ const fileTreeData = that.state.fileTreeData;
+ // 新的数组放置到treenode下
+
+ const tempFileTreeData = fileData2TreeData(repoFilesData)
+
+ getNewTreeData(fileTreeData, _eventKey, tempFileTreeData, 2);
+ this.setState({
+ fileTreeData,
+ })
+ }
+
+ resolve && resolve();
+
+ })
+ .catch(function (error) {
+ console.log(error);
+ reject && reject();
+ });
+ }
+ onTreeSelect = (selectedKeys, info) => {
+ const isLeaf = info.node.props.isLeaf;
+ if (isLeaf) { // 叶子节点
+ selectedKeys.length && this.setState({
+ fileTreeSelectedKeys: selectedKeys
+ })
+ const { fetchRepositoryCode, onPathChange, showSnackbar, challenge } = this.props;
+
+ const nodePath = info.node.props.eventKey;
+ let isCurrentFile = false;
+ // 设置pathIndex为-1,那么代码文件下拉可以切回可编辑的文件
+ if (!challenge.multiPath) { // 单path任务 多path任务 path是数组
+ if (challenge.path.trim() == nodePath.trim()) {
+ if (challenge.pathIndex === 0) {
+ showSnackbar(`当前编辑文件已经是${nodePath}`)
+ } else {
+ fetchRepositoryCode(null, nodePath, 1);
+ onPathChange(0)
+ }
+ return;
+ } else {
+ onPathChange(-1)
+ }
+ } else {
+
+ let cur_index = -1;
+ if (challenge.path && challenge.path.forEach) {
+ challenge.path.forEach((item, index) => {
+ if (nodePath == item) {
+ isCurrentFile = true;
+ cur_index = index;
+ }
+ })
+ }
+
+ if (isCurrentFile) {
+ if (challenge.pathIndex == cur_index) {
+ showSnackbar(`当前编辑文件已经是${nodePath}`)
+ }
+ onPathChange(cur_index)
+ // showSnackbar(`当前编辑文件已经是${nodePath}`)
+ } else {
+ onPathChange(-1)
+ }
+ }
+ if (nodePath) {
+ const filetype = nodePath.split('.').pop().toLowerCase();
+ if (filetype == 'jpg' || filetype == 'png' || filetype == 'gif' || filetype == 'jpeg'
+ || filetype == 'jar'
+ || filetype == 'doc' || filetype == 'pdf' || filetype == 'xsl' || filetype == 'ppt') {
+ showSnackbar(`不支持加载${filetype}类型的文件。`)
+ return;
+ }
+ fetchRepositoryCode(null, nodePath, 1);
+ } else {
+ console.error('no eventKey:', info.node)
+ }
+ }
+ }
+// /shixuns/mnf6b7z3/shixun_discuss?challenge_id=88
+ render() {
+
+ return (
+
+ {this.props.isOnlyContainer == true ?
+ React.Children.map(this.props.children, child => {
+ if(!child) {
+ return ''
+ }
+ return React.cloneElement(child, Object.assign({...this.state}, {
+ loadRepoFiles: this.loadRepoFiles,
+ onTreeSelect: this.onTreeSelect,
+ onLoadData: this.onLoadData,
+ }))
+ })
+
+ :
+
+ }
+
+ );
+ }
+}
+
+export default CodeRepositoryViewContainer;
diff --git a/public/react/src/modules/paths/PathDetail/DetailCards.js b/public/react/src/modules/paths/PathDetail/DetailCards.js
index eec340249..6c4fb9a09 100644
--- a/public/react/src/modules/paths/PathDetail/DetailCards.js
+++ b/public/react/src/modules/paths/PathDetail/DetailCards.js
@@ -531,6 +531,7 @@ class DetailCards extends Component{
{ editbuttomtypeadd===true?'':
{
this.setState({
selectShixun:false,
- page:1,
patheditarry:[]
})
}
- clickShixunchoose=()=>{
-
- let{patheditarry,shixuns_listeditlist,shixuns_listedit}=this.state
+ showNotification = (description, message = "提示", icon) => {
+ const data = {
+ message,
+ description
+ }
+ if (icon) {
+ data.icon = icon;
+ }
+ notification.open(data);
+ }
+ clickShixunchoose=(patheditarry)=>{
+
+ let{shixuns_listeditlist,shixuns_listedit}=this.state
let newshixuns_listedit=shixuns_listedit;
let list=shixuns_listeditlist
- if(patheditarry.length===0){
- this.setState({
- Modalstype:true,
- Modalstopval:'请选择实训',
- cardsModalsave:this.cardsModalsave
- })
-
- return
- }
-
let url='/paths/append_to_stage.json'
axios.post(url,{
shixun_id:patheditarry
}).then((response) => {
- let newshixun_lists=response.data.shixun_lists;
+ if(response){
+ if(response.data){
+ let newshixun_lists=response.data.shixun_lists;
+
+ for(var j=0; j {
console.log(error)
});
@@ -117,10 +135,11 @@ class DetailCardsEditAndAdd extends Component{
//点击新建阶段
addStage=()=>{
- this.props.editeditbuttomtypes();
this.setState({
editPanel:true
})
+ this.props.editeditbuttomtypes();
+
}
//取消新建阶段
@@ -141,38 +160,7 @@ class DetailCardsEditAndAdd extends Component{
})
}
- //打开选择实训弹框初始化tag标签和列表
- changeTag=(id,search)=>{
-
- this.setState({
- ChooseShixunListshixun_list:[],
- page:1,
- hometypepvisible:true,
- })
- let pathId=this.props.pathid;
-
- let url='/paths/'+pathId+'/choose_subject_shixun.json?page='+1
- if(search!="" && search!=undefined){
- url+="&search="+search;
- }
- if(id!=0){
- url+="&type="+id;
- }
-
- axios.get(encodeURI(url)).then((result)=>{
- if(result.status===200){
- this.setState({
- ChooseShixunList:result.data,
- hometypepvisible:false,
- type:id,
- ChooseShixunListshixun_list:result.data.shixun_list
- })
- }
- }).catch((error)=>{
- console.log(error);
- })
- }
//勾选实训
shixunhomeworkedit=(list)=>{
@@ -276,77 +264,31 @@ class DetailCardsEditAndAdd extends Component{
let pathId=this.props.pathid;
- let url='/stages.json?subject_id='+pathId
+ let url;
+ if(this.props.ysldetailcards===undefined){
+ url='/stages.json?subject_id='+pathId;
+ }else{
+ url=`/courses/${this.props.coursesId}/course_stages.json`;
+ }
axios.post(url, {
name:stage_names,
description:newstage_descriptions,
shixun_id:shixuns_listeditlist
}).then((response) => {
// window.location.href = "/paths/" + response.data.subject_id
- this.props.getPathCardsLists();
-
this.cancelAddState();
this.setState({
stage_nametype:false,
descriptiontype:false
})
+ this.props.getPathCardsLists();
}).catch((error) => {
console.log(error)
});
}
- contentViewScrolladd=(e)=>{
- const {ChooseShixunList}=this.state;
- //滑动到底判断
- let newscrollTop=parseInt(e.currentTarget.scrollTop);
- let allclientHeight=e.currentTarget.clientHeight+newscrollTop;
-
- if(e.currentTarget.scrollHeight-allclientHeight===0||e.currentTarget.scrollHeight-allclientHeight===1||e.currentTarget.scrollHeight-allclientHeight===-1){
-
- if(ChooseShixunList.shixun_list.length===0){
- return
- }else{
- // console.log("到达底部");
- this.setState({
- hometypepvisible:true
- })
- let pathId=this.props.pathid;
- let {search,page,type,ChooseShixunListshixun_list}=this.state;
- let newpage=page+1;
- let newChooseShixunListshixun_list=ChooseShixunListshixun_list;
- let url='/paths/'+pathId+'/choose_subject_shixun.json?page='+newpage
- if(search!="" && search!=undefined){
- url+="&search="+search;
- }
- if(type!=0){
- url+="&type="+type;
- }
- axios.get(encodeURI(url)).then((result)=>{
- if(result.status===200){
- let list =result.data.shixun_list;
-
- for(var i=0; i{
- console.log(error);
- })
-
- }
-
- }
- }
onDragEnd (result) {
let {shixuns_listedit,shixuns_listeditlist} =this.state;
@@ -400,7 +342,7 @@ class DetailCardsEditAndAdd extends Component{
{ editPanel &&
-
+
{/* 可拖拽选择实训列表*/}
@@ -632,8 +488,192 @@ class DetailCardsEditAndAdd extends Component{
{this.props.detailInfoList===undefined?"":this.props.detailInfoList.allow_statistics===true?editPanel===false?
+点击新建阶段 (选择1至多个实训项目,组成一个阶段)
:'':''}
+ {this.props.detailInfoList===undefined&&this.props.isAdmin()&&editPanel===false?
+ +点击新建阶段 (选择1至多个实训项目,组成一个阶段)
+
:''}
)
}
}
-export default DetailCardsEditAndAdd;
\ No newline at end of file
+export default DetailCardsEditAndAdd;
+
+
+
+
+//
+//
+//
+//
+//
+//
+//
+// 实训名称
+// 使用院校
+// 使用人数
+// 评价等级
+//
+//
+//
+//
+// {ChooseShixunListshixun_list && ChooseShixunListshixun_list.length===0?"":
+//
+// {
+// ChooseShixunListshixun_list && ChooseShixunListshixun_list.map((item,key)=>{
+// return(
+//
+//
+//
+// {item.shixun_name}
+//
+//
+//
{item.school_users}
+//
{item.myshixuns_count}
+//
{item.preference}
+//
详情
+//
+// )
+// })
+// }
+//
+//
}
+//
+//
+//
+//
+// contentViewScrolladd=(e)=>{
+// const {ChooseShixunList}=this.state;
+// //滑动到底判断
+// let newscrollTop=parseInt(e.currentTarget.scrollTop);
+// let allclientHeight=e.currentTarget.clientHeight+newscrollTop;
+//
+// if(e.currentTarget.scrollHeight-allclientHeight===0||e.currentTarget.scrollHeight-allclientHeight===1||e.currentTarget.scrollHeight-allclientHeight===-1){
+//
+// if(ChooseShixunList.shixun_list.length===0){
+// return
+// }else{
+// // console.log("到达底部");
+// this.setState({
+// hometypepvisible:true
+// })
+// let pathId=this.props.pathid;
+// let {search,page,type,ChooseShixunListshixun_list}=this.state;
+// let newpage=page+1;
+// let newChooseShixunListshixun_list=ChooseShixunListshixun_list;
+// let url='/paths/'+pathId+'/choose_subject_shixun.json?page='+newpage
+// if(search!="" && search!=undefined){
+// url+="&search="+search;
+// }
+// if(type!=0){
+// url+="&type="+type;
+// }
+// axios.get(encodeURI(url)).then((result)=>{
+// if(result.status===200){
+// let list =result.data.shixun_list;
+//
+// for(var i=0; i
{
+// console.log(error);
+// })
+//
+// }
+//
+// }
+//
+// }
+//
+// //打开选择实训弹框初始化tag标签和列表
+// changeTag=(id,search)=>{
+//
+// this.setState({
+// ChooseShixunListshixun_list:[],
+// page:1,
+// hometypepvisible:true,
+// })
+//
+// let pathId=this.props.pathid;
+//
+// let url='/paths/'+pathId+'/choose_subject_shixun.json?page='+1
+// if(search!="" && search!=undefined){
+// url+="&search="+search;
+// }
+// if(id!=0){
+// url+="&type="+id;
+// }
+//
+// axios.get(encodeURI(url)).then((result)=>{
+// if(result.status===200){
+// this.setState({
+// ChooseShixunList:result.data,
+// hometypepvisible:false,
+// type:id,
+// ChooseShixunListshixun_list:result.data.shixun_list
+// })
+// }
+// }).catch((error)=>{
+// console.log(error);
+// })
+// }
\ No newline at end of file
diff --git a/public/react/src/modules/paths/PathDetail/DetailCardsEditAndEdit.js b/public/react/src/modules/paths/PathDetail/DetailCardsEditAndEdit.js
index b4b8cb536..3d1559797 100644
--- a/public/react/src/modules/paths/PathDetail/DetailCardsEditAndEdit.js
+++ b/public/react/src/modules/paths/PathDetail/DetailCardsEditAndEdit.js
@@ -1,8 +1,9 @@
import React, { Component } from 'react';
import {getImageUrl} from 'educoder';
-import {Modal,Input,Checkbox,Tooltip,Spin} from "antd";
+import {Modal,Input,Checkbox,Tooltip,Spin,notification} from "antd";
import { DragDropContext,Draggable, Droppable} from 'react-beautiful-dnd';
import Modals from '../../modals/Modals';
+import NewShixunModel from '../../courses/coursesPublic/NewShixunModel';
import '../ShixunPaths.css';
import axios from 'axios';
const $ = window.$;
@@ -63,7 +64,7 @@ class DetailCardsEditAndEdit extends Component{
selectShixun:true,
patheditarry:[]
})
- this.changeTag(0,"");
+ // this.changeTag(0,"");
}
//关闭选择实训弹框
cloasShixunBox =()=>{
@@ -79,36 +80,7 @@ class DetailCardsEditAndEdit extends Component{
})
}
- //打开选择实训弹框初始化tag标签和列表
- changeTag(id,search){
- this.setState({
- ChooseShixunListshixun_list:[],
- page:1,
- hometypepvisible:true
- })
- let pathId=this.props.pathid;
- let url='/paths/'+pathId+'/choose_subject_shixun.json?page='+1
- if(search!="" && search!=undefined){
- url+="&search="+search;
- }
- if(id!=0){
- url+="&type="+id;
- }
- axios.get(encodeURI(url)).then((result)=>{
- if(result.status===200){
- this.setState({
- ChooseShixunList:result.data,
- hometypepvisible:false,
- type:id,
- search:search,
- ChooseShixunListshixun_list:result.data.shixun_list
- })
- }
- }).catch((error)=>{
- console.log(error);
- })
- }
shixunhomeworkedit=(list)=>{
@@ -158,22 +130,12 @@ class DetailCardsEditAndEdit extends Component{
}
- clickShixunchoose=()=>{
+ clickShixunchoose=(patheditarry)=>{
- let{patheditarry,shixuns_listedit,shixuns_listeditlist}=this.state
+ let{shixuns_listedit,shixuns_listeditlist}=this.state
let newshixuns_listedit=shixuns_listedit;
let list=shixuns_listeditlist
- if(patheditarry.length===0){
- this.setState({
- Modalstype:true,
- Modalstopval:'请选择实训',
-
- })
-
- return
- }
-
let url='/paths/append_to_stage.json'
axios.post(url,{
shixun_id:patheditarry
@@ -184,10 +146,11 @@ class DetailCardsEditAndEdit extends Component{
for(var j=0; j{
- //滑动到底判断
- const {ChooseShixunList}=this.state;
- let newscrollTop=parseInt(e.currentTarget.scrollTop);
- let allclientHeight=e.currentTarget.clientHeight+newscrollTop;
-
- if(e.currentTarget.scrollHeight-allclientHeight===0||e.currentTarget.scrollHeight-allclientHeight===1||e.currentTarget.scrollHeight-allclientHeight===-1){
-
- if(ChooseShixunList.shixun_list.length===0){
- return
- }else{
- this.setState({
- hometypepvisible:true
- })
- // console.log("到达底部");
-
- let {page,type,search,ChooseShixunListshixun_list}=this.state;
-
- let newpage=page+1;
-
- let pathId=this.props.pathid;
-
- let newChooseShixunListshixun_list=ChooseShixunListshixun_list;
-
- let url='/paths/'+pathId+'/choose_subject_shixun.json?page='+newpage
-
- if(search!="" && search!=undefined){
- url+="&search="+search;
- }
-
- if(type!=0){
- url+="&type="+type;
- }
- axios.get(encodeURI(url)).then((result)=>{
- if(result.status===200){
-
- let list =result.data.shixun_list;
-
- for(var i=0; i{
- console.log(error);
- })
-
-
- }
-
-
-
- }
-
- }
-
+ showNotification = (description, message = "提示", icon) => {
+ const data = {
+ message,
+ description
+ }
+ if (icon) {
+ data.icon = icon;
+ }
+ notification.open(data);
+ }
render(){
let {selectShixun,
@@ -481,99 +393,13 @@ class DetailCardsEditAndEdit extends Component{
}
:""}
+ {selectShixun===true? :""}
-
-
-
-
-
-
- 实训名称
- 使用院校
- 使用人数
- 评价等级
-
-
-
-
- {ChooseShixunListshixun_list && ChooseShixunListshixun_list.length===0?"":
-
- {
- ChooseShixunListshixun_list && ChooseShixunListshixun_list.map((item,key)=>{
- return(
-
-
-
- {item.shixun_name}
-
-
-
{item.school_users}
-
{item.myshixuns_count}
-
{item.preference}
-
详情
-
- )
- })
- }
-
-
}
-
-
-
-
{/* 可拖拽选择实训列表*/}
@@ -714,4 +540,191 @@ export default DetailCardsEditAndEdit;
//
// )
// })
-// }
\ No newline at end of file
+// }
+
+//
+//
+//
+//
+//
+//
+// 实训名称
+// 使用院校
+// 使用人数
+// 评价等级
+//
+//
+//
+//
+// {ChooseShixunListshixun_list && ChooseShixunListshixun_list.length===0?"":
+//
+// {
+// ChooseShixunListshixun_list && ChooseShixunListshixun_list.map((item,key)=>{
+// return(
+//
+//
+//
+// {item.shixun_name}
+//
+//
+//
{item.school_users}
+//
{item.myshixuns_count}
+//
{item.preference}
+//
详情
+//
+// )
+// })
+// }
+//
+//
}
+//
+//
+//
+//
+
+// //打开选择实训弹框初始化tag标签和列表
+// changeTag(id,search){
+//
+// this.setState({
+// ChooseShixunListshixun_list:[],
+// page:1,
+// hometypepvisible:true
+// })
+// let pathId=this.props.pathid;
+// let url='/paths/'+pathId+'/choose_subject_shixun.json?page='+1
+// if(search!="" && search!=undefined){
+// url+="&search="+search;
+// }
+// if(id!=0){
+// url+="&type="+id;
+// }
+// axios.get(encodeURI(url)).then((result)=>{
+// if(result.status===200){
+// this.setState({
+// ChooseShixunList:result.data,
+// hometypepvisible:false,
+// type:id,
+// search:search,
+// ChooseShixunListshixun_list:result.data.shixun_list
+// })
+// }
+// }).catch((error)=>{
+// console.log(error);
+// })
+// }
+
+// contentViewScrolledit=(e)=>{
+// //滑动到底判断
+// const {ChooseShixunList}=this.state;
+// let newscrollTop=parseInt(e.currentTarget.scrollTop);
+// let allclientHeight=e.currentTarget.clientHeight+newscrollTop;
+//
+// if(e.currentTarget.scrollHeight-allclientHeight===0||e.currentTarget.scrollHeight-allclientHeight===1||e.currentTarget.scrollHeight-allclientHeight===-1){
+//
+// if(ChooseShixunList.shixun_list.length===0){
+// return
+// }else{
+// this.setState({
+// hometypepvisible:true
+// })
+// // console.log("到达底部");
+//
+// let {page,type,search,ChooseShixunListshixun_list}=this.state;
+//
+// let newpage=page+1;
+//
+// let pathId=this.props.pathid;
+//
+// let newChooseShixunListshixun_list=ChooseShixunListshixun_list;
+//
+// let url='/paths/'+pathId+'/choose_subject_shixun.json?page='+newpage
+//
+// if(search!="" && search!=undefined){
+// url+="&search="+search;
+// }
+//
+// if(type!=0){
+// url+="&type="+type;
+// }
+// axios.get(encodeURI(url)).then((result)=>{
+// if(result.status===200){
+//
+// let list =result.data.shixun_list;
+//
+// for(var i=0; i{
+// console.log(error);
+// })
+//
+//
+// }
+//
+//
+//
+// }
+//
+// }
\ No newline at end of file
diff --git a/public/react/src/modules/paths/PathDetail/DetailCardsTemp.js b/public/react/src/modules/paths/PathDetail/DetailCardsTemp.js
index 5150d14da..45613db41 100644
--- a/public/react/src/modules/paths/PathDetail/DetailCardsTemp.js
+++ b/public/react/src/modules/paths/PathDetail/DetailCardsTemp.js
@@ -166,7 +166,7 @@ class DetailCards extends Component{
})
}
-
+
)
}
diff --git a/public/react/src/modules/paths/PathDetail/PathDetailIndex.js b/public/react/src/modules/paths/PathDetail/PathDetailIndex.js
index f1b0a3bbb..c8cece9fd 100644
--- a/public/react/src/modules/paths/PathDetail/PathDetailIndex.js
+++ b/public/react/src/modules/paths/PathDetail/PathDetailIndex.js
@@ -14,24 +14,24 @@ import TPMRightSection from "../../tpm/component/TPMRightSection";
import styled from "styled-components";
const getItemStyle = (isDragging, draggableStyle) => ({
- // change background colour if dragging
- background: isDragging ? '#dceeff' : '',
- // styles we need to apply on draggables
- ...draggableStyle,
+ // change background colour if dragging
+ background: isDragging ? '#dceeff' : '',
+ // styles we need to apply on draggables
+ ...draggableStyle,
});
const getItems = count =>
- Array.from({ length: count }, (v, k) => k).map(k => ({
- id: `item-${k}`,
- content: `item ${k}`
- }));
+ Array.from({ length: count }, (v, k) => k).map(k => ({
+ id: `item-${k}`,
+ content: `item ${k}`
+ }));
// a little function to help us with reordering the result
const reorder = (list, startIndex, endIndex) => {
- const result = Array.from(list);
- const [removed] = result.splice(startIndex, 1);
- result.splice(endIndex, 0, removed);
+ const result = Array.from(list);
+ const [removed] = result.splice(startIndex, 1);
+ result.splice(endIndex, 0, removed);
- return result;
+ return result;
};
const List = styled.div`
@@ -66,90 +66,90 @@ const DragHandle = styled.div`
`;
const $ =window.$
class PathDetailIndex extends Component{
- constructor(props){
- super(props)
- this.state={
+ constructor(props){
+ super(props)
+ this.state={
progress:undefined,
tags:undefined,
members:undefined,
- detailInfoList:undefined,
- clickdetailInfoListtype:false,
- Modalstype:false,
- Modalstopval:undefined,
- Modalsbottomval:undefined,
- cardsModalcancel:this.cardsModalcancel,
- cardsModalsave:this.cardsModalsave,
- user_id:undefined,
- loadtype:false,
+ detailInfoList:undefined,
+ clickdetailInfoListtype:false,
+ Modalstype:false,
+ Modalstopval:undefined,
+ Modalsbottomval:undefined,
+ cardsModalcancel:this.cardsModalcancel,
+ cardsModalsave:this.cardsModalsave,
+ user_id:undefined,
+ loadtype:false,
courses:undefined,
- items: getItems(10),
+ items: getItems(10),
pathtopskey:1
- }
- this.onDragEnd = this.onDragEnd.bind(this);
+ }
+ this.onDragEnd = this.onDragEnd.bind(this);
- }
- onDragEnd(result) {
- // dropped outside the list
- if (!result.destination) {
- return;
- }
+ }
+ onDragEnd(result) {
+ // dropped outside the list
+ if (!result.destination) {
+ return;
+ }
- try {
+ try {
- }catch (e) {
+ }catch (e) {
- }
- const items = reorder(
- this.state.members,
- result.source.index,
- result.destination.index
- );
+ }
+ const items = reorder(
+ this.state.members,
+ result.source.index,
+ result.destination.index
+ );
- this.setState({
- detailInfoList:this.state.detailInfoList,
+ this.setState({
+ detailInfoList:this.state.detailInfoList,
members:items,
- items
- });
- console.log(this.state.members)
- console.log("items 数组数组数组数组")
- console.log(items)
- }
- cardsModalcancel=()=>{
- this.setState({
- Modalstype:false,
- })
- // TODO 这个是临时处理,还需要优化,这里要看怎么区分处理
- if (this.state.Modalstopval == '你确定要删除该成员吗?') {
- return;
- }
+ items
+ });
+ console.log(this.state.members)
+ console.log("items 数组数组数组数组")
+ console.log(items)
+ }
+ cardsModalcancel=()=>{
+ this.setState({
+ Modalstype:false,
+ })
+ // TODO 这个是临时处理,还需要优化,这里要看怎么区分处理
+ if (this.state.Modalstopval == '你确定要删除该成员吗?') {
+ return;
+ }
this.props.history.goBack()
- }
+ }
- cardsModalsave=()=>{
- this.setState({
- Modalstype:false,
- })
+ cardsModalsave=()=>{
+ this.setState({
+ Modalstype:false,
+ })
this.props.history.goBack()
- }
- // 加载markdown
- updatamakedown=(id)=>{
- setTimeout(()=>{
- var shixunDescr = window.editormd.markdownToHTML(id, {
- htmlDecode: "style,script,iframe",
- taskList: true,
- tex: true,
- flowChart: true,
- sequenceDiagram: true
- });
- $("#"+id+" p:first").addClass("ReactMarkdown");
- }, 200)
- }
+ }
+ // 加载markdown
+ updatamakedown=(id)=>{
+ setTimeout(()=>{
+ var shixunDescr = window.editormd.markdownToHTML(id, {
+ htmlDecode: "style,script,iframe",
+ taskList: true,
+ tex: true,
+ flowChart: true,
+ sequenceDiagram: true
+ });
+ $("#"+id+" p:first").addClass("ReactMarkdown");
+ }, 200)
+ }
- componentDidMount(){
- this.getdatasindex()
- }
+ componentDidMount(){
+ this.getdatasindex()
+ }
- getdatasindex=(key)=>{
+ getdatasindex=(key)=>{
let pathid=this.props.match.params.pathId;
let url="/paths/"+pathid+".json";
@@ -168,7 +168,7 @@ class PathDetailIndex extends Component{
if(result.data.allow_visit===true){
this.setState({
detailInfoList:result.data,
- courses:result.data.courses,
+ courses:result.data.courses,
pathtopskey:key===undefined?1:key,
// items: getItems(result.data.members.length),
})
@@ -205,366 +205,370 @@ class PathDetailIndex extends Component{
})
}
- updatadetailInfoList=()=>{
- this.getdatasindex();
- }
-
- clickNewsubscript=(val)=>{
- if(val===0){
- this.setState({
- clickdetailInfoListtype:true
- })
- }else{
- this.setState({
- clickdetailInfoListtype:false
- })
- }
+ updatadetailInfoList=()=>{
+ this.getdatasindex();
+ }
- }
+ clickNewsubscript=(val)=>{
+ if(val===0){
+ this.setState({
+ clickdetailInfoListtype:true
+ })
+ }else{
+ this.setState({
+ clickdetailInfoListtype:false
+ })
+ }
- timeStamp=(value)=>{
- var secondTime = parseInt(value);// 秒
- var minuteTime = 0;// 分
- var hourTime = 0;// 小时
- if(secondTime > 60) {//如果秒数大于60,将秒数转换成整数
- //获取分钟,除以60取整数,得到整数分钟
- minuteTime = parseInt(secondTime / 60);
- //获取秒数,秒数取佘,得到整数秒数
- secondTime = parseInt(secondTime % 60);
- //如果分钟大于60,将分钟转换成小时
- if(minuteTime > 60) {
- //获取小时,获取分钟除以60,得到整数小时
- hourTime = parseInt(minuteTime / 60);
- //获取小时后取佘的分,获取分钟除以60取佘的分
- minuteTime = parseInt(minuteTime % 60);
- }
- }
- var result = "" + parseInt(secondTime) + "秒";
-
- if(minuteTime > 0) {
- result = "" + parseInt(minuteTime) + "分" + result;
- }
- if(hourTime > 0) {
- result = "" + parseInt(hourTime) + "小时" + result;
- }
- return result;
- }
+ }
+ timeStamp=(value)=>{
+ var secondTime = parseInt(value);// 秒
+ var minuteTime = 0;// 分
+ var hourTime = 0;// 小时
+ if(secondTime > 60) {//如果秒数大于60,将秒数转换成整数
+ //获取分钟,除以60取整数,得到整数分钟
+ minuteTime = parseInt(secondTime / 60);
+ //获取秒数,秒数取佘,得到整数秒数
+ secondTime = parseInt(secondTime % 60);
+ //如果分钟大于60,将分钟转换成小时
+ if(minuteTime > 60) {
+ //获取小时,获取分钟除以60,得到整数小时
+ hourTime = parseInt(minuteTime / 60);
+ //获取小时后取佘的分,获取分钟除以60取佘的分
+ minuteTime = parseInt(minuteTime % 60);
+ }
+ }
+ var result = "" + parseInt(secondTime) + "秒";
+
+ if(minuteTime > 0) {
+ result = "" + parseInt(minuteTime) + "分" + result;
+ }
+ if(hourTime > 0) {
+ result = "" + parseInt(hourTime) + "小时" + result;
+ }
+ return result;
+ }
- shanchuallow=(id)=>{
- this.setState({
- user_id:id,
- Modalstype:true,
- Modalstopval:"你确定要删除该成员吗?",
- cardsModalsave:this.delectshanchuallow,
- loadtype:false
- })
- }
- delectshanchuallow=()=>{
- let{user_id}=this.state;
- let pathid=this.props.match.params.pathId;
- let url="/paths/"+pathid+"/delete_member.json";
- let param={user_id:user_id};
- axios.delete(url,{data:param}).then((response) => {
- if(response.data.status===1){
- if (this.props.current_user.user_id == user_id) {
- this.props.history.push('/paths')
- return;
- }
- this.props.showNotification(response.data.message)
- this.setState({
- Modalstype:false,
- // Modalstopval:response.data.message,
- loadtype:false,
- // cardsModalsave:this.cardsModalsave,
- })
- this.updatadetailInfoList();
- }
- }).catch((error) => {
- console.log(error)
- })
- }
+ shanchuallow=(id)=>{
+ this.setState({
+ user_id:id,
+ Modalstype:true,
+ Modalstopval:"你确定要删除该成员吗?",
+ cardsModalsave:this.delectshanchuallow,
+ loadtype:false
+ })
+ }
+
+ delectshanchuallow=()=>{
+ let{user_id}=this.state;
+ let pathid=this.props.match.params.pathId;
+ let url="/paths/"+pathid+"/delete_member.json";
+ let param={user_id:user_id};
+ axios.delete(url,{data:param}).then((response) => {
+ if(response.data.status===1){
+ if (this.props.current_user.user_id == user_id) {
+ this.props.history.push('/paths')
+ return;
+ }
+ this.props.showNotification(response.data.message)
+ this.setState({
+ Modalstype:false,
+ // Modalstopval:response.data.message,
+ loadtype:false,
+ // cardsModalsave:this.cardsModalsave,
+ })
+ this.updatadetailInfoList();
+ }
+ }).catch((error) => {
+ console.log(error)
+ })
+ }
+
+ //上移
+ moveup=(data)=>{
+ // console.log(data);
+ let pathid=this.props.match.params.pathId;
+ let url=`/paths/${pathid}/up_member_position.json`;
+ axios.post(url,{
+ user_id:data.id
+ }).then((response) => {
+ if(response.status === 200){
+ console.log("上移");
+ // console.log(this.state.detailInfoList.members);
+ // console.log(response);
- //上移
- moveup=(data)=>{
- // console.log(data);
- let pathid=this.props.match.params.pathId;
- let url=`/paths/${pathid}/up_member_position.json`;
- axios.post(url,{
- user_id:data.id
- }).then((response) => {
- if(response.status === 200){
- console.log("上移");
- // console.log(this.state.detailInfoList.members);
- // console.log(response);
-
- this.setState({
- detailInfoList:this.state.detailInfoList,
+ this.setState({
+ detailInfoList:this.state.detailInfoList,
members:response.data.members
- });
- // console.log(this.state.detailInfoList.members);
+ });
+ // console.log(this.state.detailInfoList.members);
- }
+ }
- }).catch((error) => {
- console.log(error)
- })
- }
+ }).catch((error) => {
+ console.log(error)
+ })
+ }
- //下移
- movedown =(data) => {
- // console.log(data);
- let pathid=this.props.match.params.pathId;
- let url=`/paths/${pathid}/down_member_position.json`;
- axios.post(url,{
- user_id:data.id
- }).then((response) => {
- if( response.status === 200){
- console.log("下移");
- // console.log(this.state.detailInfoList.members);
- // console.log(response);
- this.setState({
- detailInfoList:this.state.detailInfoList,
- members:response.data.members
- });
- // console.log(this.state.detailInfoList.members);
- }
-
- }).catch((error) => {
- console.log(error)
- })
- }
- render(){
-
- this.updatamakedown("shixuns_propaedeutics");
- this.updatamakedown("subject_learning_notes");
- let {detailInfoList,
- clickdetailInfoListtype,
- Modalstype,
- Modalstopval,
- Modalsbottomval,
- cardsModalcancel,
- cardsModalsave,
- loadtype,
+ //下移
+ movedown =(data) => {
+ // console.log(data);
+ let pathid=this.props.match.params.pathId;
+ let url=`/paths/${pathid}/down_member_position.json`;
+ axios.post(url,{
+ user_id:data.id
+ }).then((response) => {
+ if( response.status === 200){
+ console.log("下移");
+ // console.log(this.state.detailInfoList.members);
+ // console.log(response);
+ this.setState({
+ detailInfoList:this.state.detailInfoList,
+ members:response.data.members
+ });
+ // console.log(this.state.detailInfoList.members);
+ }
+
+ }).catch((error) => {
+ console.log(error)
+ })
+ }
+ render(){
+
+ this.updatamakedown("shixuns_propaedeutics");
+ this.updatamakedown("subject_learning_notes");
+ let {detailInfoList,
+ clickdetailInfoListtype,
+ Modalstype,
+ Modalstopval,
+ Modalsbottomval,
+ cardsModalcancel,
+ cardsModalsave,
+ loadtype,
progress,
members,
tags,
courses,
- } = this.state
+ } = this.state
- return(
-
+ return(
+
-
-
-
-
this.getdatasindex(key)}>
-
-
-
-
- 简介
- {/*{detailInfoList===undefined?"":detailInfoList.allow_statistics===true?*/}
- {/**/}
- {/* */}
- {/* */}
- {/**/}
- {/* */}
- {/*:""*/}
- {/*}*/}
-
-
-
- {detailInfoList === undefined ? "" :detailInfoList.description===null?"":
-
- }
-
-
-
-
-
-
-
-
-
- 课程须知
- {/*{detailInfoList===undefined?"":detailInfoList.allow_statistics===true?*/}
- {/**/}
- {/* */}
- {/* */}
- {/**/}
- {/* */}
- {/*:""*/}
- {/*}*/}
-
-
- {detailInfoList === undefined ? "" :detailInfoList.learning_notes===null?"":
-
+
+
+
+
this.getdatasindex(key)}>
+
+
+
+
+ 简介
+ {/*{detailInfoList===undefined?"":detailInfoList.allow_statistics===true?*/}
+ {/**/}
+ {/* */}
+ {/* */}
+ {/**/}
+ {/* */}
+ {/*:""*/}
+ {/*}*/}
+
+
+
+ {detailInfoList === undefined ? "" :detailInfoList.description===null?"":
+
}
-
-
- {tags === undefined ? "" :tags === null ? "":
-
-
技能标签 {tags.length}
-
-
-
- {
- tags && tags.map((item,key)=>{
- return(
- {item.tag_name}
- )
- })
- }
-
-
-
-
- 20&&clickdetailInfoListtype===false?"newsubscript mb9 color-grey-9 fr":"newsubscript mb9 color-grey-9 none"}
- onClick={()=>this.clickNewsubscript(0)}
- >...
-
-
-
-
- this.clickNewsubscript(1)}>
-
-
-
-
- }
- {
- this.props.checkIfLogin()===false?"":progress === undefined ? "" : progress === null ? "" :
-
-
- 我的进展
-
- {progress.my_score} / {progress.all_score}
-
-
-
- 已学 {progress.learned}%
- 学习耗时{this.timeStamp(progress.time)}
-
-
-
- }
-
- {
- members ===undefined ?"":members === null ?"":
-
-
教学团队
-
- { members===undefined?
- members && members.map((item,key)=>{
- return(
-
-
-
-
-
-
-
{item.name}
- {/*{*/}
- {/* detailInfoList===undefined?"":detailInfoList.allow_add_member===true?*/}
- {/* this.shanchuallow(item.id)}> :""*/}
- {/*}*/}
-
-
-
{item.school} {item.identity}
-
-
-
- )
- })
- :detailInfoList===undefined?"":detailInfoList.allow_add_member===true?
- members && members.map((item,key)=>{
- return(
-
-
-
-
-
-
-
{item.name}
- {/* 新增role 判断是否能删除 1 管理员 2 合作者 */}
-
- {
- detailInfoList===undefined?"":detailInfoList.allow_add_member===true && item.role == 2?
- this.shanchuallow(item.id)}> :""
- }
-
-
-
{item.school} {item.identity}
-
- {
- detailInfoList===undefined?"":detailInfoList.allow_add_member===true?
- {key!=0?
:""}
- {key+1== members.length?"":
}
-
- :""
- }
-
-
-
- )
- })
- : members && members.map((item,key)=>{
- return(
-
-
-
-
-
-
-
{item.name}
- {/*{*/}
- {/* detailInfoList===undefined?"":detailInfoList.allow_add_member===true?*/}
- {/* this.shanchuallow(item.id)}> :""*/}
- {/*}*/}
-
-
-
{item.school} {item.identity}
-
-
-
- )
- })}
-
-
- }
-
-
-
-
-
-
-
-
-
- )
- }
+
+
+
+
+
+
+
+
+
+ 课程须知
+ {/*{detailInfoList===undefined?"":detailInfoList.allow_statistics===true?*/}
+ {/**/}
+ {/* */}
+ {/* */}
+ {/**/}
+ {/* */}
+ {/*:""*/}
+ {/*}*/}
+
+
+ {detailInfoList === undefined ? "" :detailInfoList.learning_notes===null?"":
+
+ }
+
+
+ {tags === undefined ? "" :tags === null ? "":
+
+
技能标签 {tags.length}
+
+
+
+ {
+ tags && tags.map((item,key)=>{
+ return(
+ {item.tag_name}
+ )
+ })
+ }
+
+
+
+
+ 20&&clickdetailInfoListtype===false?"newsubscript mb9 color-grey-9 fr":"newsubscript mb9 color-grey-9 none"}
+ onClick={()=>this.clickNewsubscript(0)}
+ >...
+
+
+
+
+ this.clickNewsubscript(1)}>
+
+
+
+
+ }
+ {
+ this.props.checkIfLogin()===false?"":progress === undefined ? "" : progress === null ? "" :
+
+
+ 关卡数
+
+ {progress.my_score} / {progress.all_score}
+
+
+
+ 已学 {progress.learned}%
+ 学习耗时{this.timeStamp(progress.time)}
+
+
+
注: “我的进展”以已发布的实训详情关卡数为准。
+
+ }
+
+ {
+ members ===undefined ?"":members === null ?"":
+
+
教学团队
+
+ { members===undefined?
+ members && members.map((item,key)=>{
+ return(
+
+
+
+
+
+
+
{item.name}
+ {/*{*/}
+ {/* detailInfoList===undefined?"":detailInfoList.allow_add_member===true?*/}
+ {/* this.shanchuallow(item.id)}> :""*/}
+ {/*}*/}
+
+
+
{item.school} {item.identity}
+
+
+
+ )
+ })
+ :detailInfoList===undefined?"":detailInfoList.allow_add_member===true?
+ members && members.map((item,key)=>{
+ return(
+
+
+
+
+
+
+
{item.name}
+ {/* 新增role 判断是否能删除 1 管理员 2 合作者 */}
+
+ {
+ detailInfoList===undefined?"":detailInfoList.allow_add_member===true && item.role == 2?
+ this.shanchuallow(item.id)}> :""
+ }
+
+
+
{item.school} {item.identity}
+
+ {
+ detailInfoList===undefined?"":detailInfoList.allow_add_member===true?
+ {key!=0?
:""}
+ {key+1== members.length?"":
}
+
+ :""
+ }
+
+
+
+ )
+ })
+ : members && members.map((item,key)=>{
+ return(
+
+
+
+
+
+
+
{item.name}
+ {/*{*/}
+ {/* detailInfoList===undefined?"":detailInfoList.allow_add_member===true?*/}
+ {/* this.shanchuallow(item.id)}> :""*/}
+ {/*}*/}
+
+
+
{item.school} {item.identity}
+
+
+
+ )
+ })}
+
+
+ }
+
+
+
+
+
+
+
+
+
+ )
+ }
}
export default PathDetailIndex;
\ No newline at end of file
diff --git a/public/react/src/modules/paths/PathNew.js b/public/react/src/modules/paths/PathNew.js
index 95e343b13..4e1621bc5 100644
--- a/public/react/src/modules/paths/PathNew.js
+++ b/public/react/src/modules/paths/PathNew.js
@@ -1,7 +1,7 @@
import React,{ Component } from "react";
import {getUrl,markdownToHTML} from 'educoder';
-import {Input} from 'antd';
+import {Input,Button} from 'antd';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import axios from 'axios';
@@ -86,7 +86,8 @@ class PathNew extends Component{
pathName:"",
description:"",
point:"",
- flag_name:true
+ flag_name:true,
+ bottonloading:false
}
}
@@ -124,6 +125,9 @@ class PathNew extends Component{
return;
}
if (this.isEditPage == true) {
+ this.setState({
+ bottonloading:true
+ })
let pathId = this.props.match.params.pathId;
const editUrl = `/paths/${pathId}.json`
@@ -135,11 +139,21 @@ class PathNew extends Component{
// console.log(response.data.subject_id);
if (response.data.subject_id) {
this.props.history.push(`/paths/${response.data.subject_id}`)
- }
+ }else{
+ this.setState({
+ bottonloading:false
+ })
+ }
}).catch((error)=>{
console.log(error);
+ this.setState({
+ bottonloading:false
+ })
})
} else {
+ this.setState({
+ bottonloading:true
+ })
let url="/paths.json"
axios.post(url,{
name:pathName,
@@ -149,9 +163,16 @@ class PathNew extends Component{
// console.log(response.data.subject_id);
if (response.data.subject_id) {
this.props.history.push(`/paths/${response.data.subject_id}`)
- }
+ }else{
+ this.setState({
+ bottonloading:false
+ })
+ }
}).catch((error)=>{
console.log(error);
+ this.setState({
+ bottonloading:false
+ })
})
}
@@ -267,7 +288,7 @@ class PathNew extends Component{
-
提交
+
提交
{this.isEditPage ?
取消
diff --git a/public/react/src/modules/paths/ShixunPathCard.js b/public/react/src/modules/paths/ShixunPathCard.js
index 166c6415a..d919b1a6b 100644
--- a/public/react/src/modules/paths/ShixunPathCard.js
+++ b/public/react/src/modules/paths/ShixunPathCard.js
@@ -1,5 +1,5 @@
import React, { Component } from 'react';
-import {getImageUrl} from 'educoder';
+import {getImageUrl , setImagesUrl } from 'educoder';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Tooltip } from 'antd';
import axios from 'axios';
@@ -22,51 +22,49 @@ class ShixunPathCard extends Component{
pathList && pathList.map((item,key)=>{
return(
-
-
- {
- item.tag_name === null ? "" :
-
-
{item.tag_name}
- {/*
*/}
-
- }
-
+
+ {/* item.tag_name === null ? "" :
+
+
{item.tag_name}
+
+
*/}
{
item.excellent === false ? "" :
-
- 开放课程
-
+
+ 开放课程
+
}
-
+ {/*
非试用内容,需要授权
-
-
+
*/}
+
+
{/*target="_blank"*/}
-
+
-
-
- {item.name}
+
+
+ {item.name}
{/*target="_blank"*/}
-
+
-
- {item.stages_count}
-
+ {/* */}
+ {/* */}
+ 章节: {item.stages_count}
+ {/* */}
{/**/}
{/* {item.shixuns_count} */}
{/* */}
-
-
- {item.members_count}
-
+ {/* */}
+ {/* */}
+ 学习人数: {item.members_count}
+ {/* */}
diff --git a/public/react/src/modules/paths/ShixunPathSearch.js b/public/react/src/modules/paths/ShixunPathSearch.js
index c0060a528..8323cd607 100644
--- a/public/react/src/modules/paths/ShixunPathSearch.js
+++ b/public/react/src/modules/paths/ShixunPathSearch.js
@@ -7,9 +7,6 @@ import Pagination from '@icedesign/base/lib/pagination';
import '@icedesign/base/lib/pagination/style.js';
import './ShixunPaths.css';
-
-const Search = Input.Search;
-
class ShixunPathSearch extends Component{
constructor(props) {
super(props)
@@ -122,9 +119,10 @@ class ShixunPathSearch extends Component{
{this.state.updata===undefined?"":
}
-
-
-
+
+
+
+
0 ? "" : "active"}>this.changeSelect(null)}>全部
{
sortList && sortList.map((item,key)=>{
@@ -133,18 +131,18 @@ class ShixunPathSearch extends Component{
)
})
}
-
+
-
+
{/*
this.changeStatus("publish_time")}>全部 */}
{/*
this.changeStatus("mine")}>我的 */}
-
this.changeStatus("updated_at")}>最新
-
this.changeStatus("myshixun_count")}>最热
+
this.changeStatus("updated_at")}>最新
+
this.changeStatus("myshixun_count")}>最热
{/*
*/}
{/*/!*
diff --git a/public/react/src/modules/projectPackages/MDEditors.js b/public/react/src/modules/projectPackages/MDEditors.js
index 2c11e0895..61e26db23 100644
--- a/public/react/src/modules/projectPackages/MDEditors.js
+++ b/public/react/src/modules/projectPackages/MDEditors.js
@@ -166,7 +166,7 @@ function create_editorMD(id, width, high, placeholder, imageUrl, callback, initV
});
$("#" + _id + " [type=\"inline\"]").bind("click", function () {
- _editorName.cm.replaceSelection("$$$$");
+ _editorName.cm.replaceSelection("`$$$$`");
var __Cursor = _editorName.cm.getDoc().getCursor();
_editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 2);
_editorName.cm.focus();
diff --git a/public/react/src/modules/projectPackages/packageconcnet.css b/public/react/src/modules/projectPackages/packageconcnet.css
index f7ee4cc06..22dd4ebc8 100644
--- a/public/react/src/modules/projectPackages/packageconcnet.css
+++ b/public/react/src/modules/projectPackages/packageconcnet.css
@@ -295,11 +295,11 @@
.topsj{
position: absolute;
- top: -6px;
+ top: -3px;
}
.bottomsj{
position: absolute;
- bottom: -6px;
+ bottom: -5px;
}
.touchSelect .ant-spin-dot-spin{
margin-top: 30% !important;
diff --git a/public/react/src/modules/test/ShareTest.js b/public/react/src/modules/test/ShareTest.js
index 293396109..c715f8910 100644
--- a/public/react/src/modules/test/ShareTest.js
+++ b/public/react/src/modules/test/ShareTest.js
@@ -11,7 +11,7 @@ if(window.wx) {
title: ' title', // 分享标题
desc: 'hello world', // 分享描述
link: 'https://www.educoder.net', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
- imgUrl: 'https://pre-newweb.educoder.net/images/educoder/headNavLogo.png', // 分享图标
+ imgUrl: 'https://test-newweb.educoder.net/images/educoder/headNavLogo.png', // 分享图标
success: function () {
// 设置成功
}
diff --git a/public/react/src/modules/topic_bank/Topic_bank.js b/public/react/src/modules/topic_bank/Topic_bank.js
new file mode 100644
index 000000000..a4bbc9dd7
--- /dev/null
+++ b/public/react/src/modules/topic_bank/Topic_bank.js
@@ -0,0 +1,51 @@
+import React, { Component } from 'react';
+
+import { Redirect } from 'react-router';
+
+import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
+
+import Loading from '../../Loading'
+
+import Loadable from 'react-loadable';
+import { TPMIndexHOC } from '../tpm/TPMIndexHOC'
+import { SnackbarHOC } from 'educoder'
+
+
+const PackageIndex = Loadable({
+ loader: () => import('../user/usersInfo/InfosTopics'),
+ loading: Loading,
+})
+
+
+class Topic_bank extends Component {
+ constructor(props) {
+ super(props)
+ }
+
+ componentDidMount(){
+ }
+
+ render() {
+ return (
+
+
+
+ ( )
+ }
+ >
+
+ ( )
+ }
+ >
+
+
+
+ );
+ }
+}
+
+export default SnackbarHOC() (TPMIndexHOC (Topic_bank)) ;
\ No newline at end of file
diff --git a/public/react/src/modules/tpm/NewFooter.js b/public/react/src/modules/tpm/NewFooter.js
index a85b35ccf..be1a1ed29 100644
--- a/public/react/src/modules/tpm/NewFooter.js
+++ b/public/react/src/modules/tpm/NewFooter.js
@@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { Redirect } from 'react-router';
+import { Link } from 'react-router-dom';
import { getImageUrl, toPath } from 'educoder'
import PropTypes from 'prop-types';
@@ -29,12 +30,12 @@ class NewFooter extends Component {
*/}
diff --git a/public/react/src/modules/tpm/TPMIndex.js b/public/react/src/modules/tpm/TPMIndex.js
index 00a88cd21..67779af8d 100644
--- a/public/react/src/modules/tpm/TPMIndex.js
+++ b/public/react/src/modules/tpm/TPMIndex.js
@@ -190,6 +190,8 @@ class TPMIndex extends Component {
identity: response.data.identity,
propaedeutics:response.data.propaedeutics,
status: response.data.shixun_status,
+ secret_repository: response.data.secret_repository,
+
});
}
}).catch((error) => {
@@ -285,6 +287,10 @@ class TPMIndex extends Component {
(props) => (
)
}>
+
( )
+ }>
( ( )
}>
+
( )
+ }>
+
{/*
*/}
{
// return false
return this.state.coursedata&&this.state.coursedata.course_identity === 0
}
- // 课堂管理等
+ //超管、运维0-1
isClassManagement = () => {
- // return this.state.coursedata&&this.state.coursedata.course_identity >= 0 &&
return this.state.coursedata&&this.state.coursedata.course_identity < 2
}
- //老师等
+ //超管、运维、课堂管理0-2
isAdminOrCreator = () => {
- // return this.state.coursedata&&this.state.coursedata.course_identity >= 0 &&
return this.state.coursedata&&this.state.coursedata.course_identity < 3
}
- // 助教等
+ //超管、运维、课堂管理、老师0-3
isAdminOrTeacher = () => {
- // return this.state.coursedata&&this.state.coursedata.course_identity >= 0 &&
return this.state.coursedata&&this.state.coursedata.course_identity < 4
}
- // 老师、管理员等
+ // 超管、运维、课堂管理、老师、助教0-4
isAdmin = () => {
- // return false
- // return this.state.coursedata&&this.state.coursedata.course_identity >= 0 &&
return this.state.coursedata&&this.state.coursedata.course_identity < 5
}
- // 学生
+ // 学生5
isStudent = () => {
- // return true
- // return this.state.coursedata&&this.state.coursedata.course_identity >= 0 &&
return this.state.coursedata&&this.state.coursedata.course_identity === 5
}
+ // 超管、运维、课堂管理、老师、助教、学生0-5
isAdminOrStudent = () => {
- // return this.state.coursedata&&this.state.coursedata.course_identity >= 0 &&
return this.state.coursedata&&this.state.coursedata.course_identity <= 5
}
- // 非课堂成员
+ // 游客未登录/非课堂成员6>
isNotMember = () => {
- // return this.state.coursedata&&this.state.coursedata.course_identity >= 0 &&
return this.state.coursedata&&this.state.coursedata.course_identity >= 6
}
-
+ //课堂是否已结束
isCourseEnd = () => {
return this.state.current_user ? this.state.current_user.course_is_end : false
}
diff --git a/public/react/src/modules/tpm/TPMRepository.js b/public/react/src/modules/tpm/TPMRepository.js
index e9bfbf51d..0f8e31258 100644
--- a/public/react/src/modules/tpm/TPMRepository.js
+++ b/public/react/src/modules/tpm/TPMRepository.js
@@ -11,6 +11,8 @@ import Repository from './shixunchild/Repository/Repository'
import TPMRightSection from './component/TPMRightSection'
import TPMNav from './component/TPMNav'
+// import RepositoryChooseModal from './component/modal/RepositoryChooseModal'
+
class TPMRepository extends Component {
constructor(props) {
super(props)
@@ -34,6 +36,7 @@ class TPMRepository extends Component {
shixun={shixun}
{...this.props}
>
+ {/* */}
{ loadingContent ?
:
{
this.fetchRepo()
@@ -72,7 +78,8 @@ class TPMRepositoryComponent extends Component {
let id = this.props.match.params.shixunId;
let url = `/shixuns/${id}/file_content.json`;
axios.post(url, {
- path: path
+ path: path,
+ secret_repository: this.props.secret_repository_tab
}).then((response) => {
trace_collapse('repository res: ', response)
@@ -138,7 +145,7 @@ class TPMRepositoryComponent extends Component {
const path = urlNewPathArray.join('/')
let id = this.props.match.params.shixunId;
- let url = `/shixuns/${id}/repository.json`;
+ let url = `/shixuns/${id}/${this.props.secret_repository_tab ? 'secret_repository' : 'repository'}.json`;
// this.props.setLoadingContent(true)
axios.post(url, {
path: path ? path : ''
diff --git a/public/react/src/modules/tpm/TPMsettings/TPMsettings.js b/public/react/src/modules/tpm/TPMsettings/TPMsettings.js
index f1184e7b7..b2767887d 100644
--- a/public/react/src/modules/tpm/TPMsettings/TPMsettings.js
+++ b/public/react/src/modules/tpm/TPMsettings/TPMsettings.js
@@ -220,6 +220,7 @@ export default class TPMsettings extends Component {
can_copy: undefined,
task_pass: undefined,
test_set_permission: undefined,
+ code_edit_permission: undefined,
hide_code: undefined,
code_hidden: undefined,
forbid_copy: undefined,
@@ -352,9 +353,13 @@ export default class TPMsettings extends Component {
task_pass: response.data.shixun.task_pass,
test_set_permission: response.data.shixun.test_set_permission,
hide_code: response.data.shixun.hide_code,
+ code_edit_permission: response.data.shixun.code_edit_permission,
code_hidden: response.data.shixun.code_hidden,
+ is_secret_repository: response.data.shixun.is_secret_repository,
+ init_is_secret_repository: response.data.shixun.is_secret_repository,
forbid_copy: response.data.shixun.forbid_copy,
vnc: response.data.shixun.vnc,
+ vnc_evaluate: response.data.shixun.vnc_evaluate,
name: response.data.shixun.name,
scope_partment: response.data.shixun.scope_partment,
description: response.data.shixun.description,
@@ -438,20 +443,52 @@ export default class TPMsettings extends Component {
SelectshixunCommand=(e)=>{
// console.log( e.target.value)
- this.setState({
- webssh: e.target.value,
- });
- if(e.target.value===2){
- this.setState({
- SelectTheCommandtype: true,
- multi_webssh:false
- });
- }else{
- this.setState({
- SelectTheCommandtype: false,
- multi_webssh:false
- });
- }
+ const webssh = e.target.value
+ if (webssh == 2) {
+ this.setState({
+ webssh: webssh,
+ SelectTheCommandtype: true,
+ multi_webssh:false
+ });
+ } else {
+ if (this.state.init_is_secret_repository && !this.state.vnc && this.state.is_secret_repository == true) {
+ this.confirmDeleteSecretRepo({
+ onOk: () => {
+ this.setState({
+ webssh: webssh,
+ SelectTheCommandtype: false,
+ multi_webssh:false
+ });
+ }
+ })
+ } else {
+ if (!this.state.vnc) {
+ this.setState({
+ is_secret_repository: false,
+ })
+ }
+ this.setState({
+ webssh: webssh,
+ SelectTheCommandtype: false,
+ multi_webssh:false
+ });
+ }
+ }
+
+ // this.setState({
+ // webssh: webssh,
+ // });
+ // if(webssh===2){
+ // this.setState({
+ // SelectTheCommandtype: true,
+ // multi_webssh:false
+ // });
+ // }else{
+ // this.setState({
+ // SelectTheCommandtype: false,
+ // multi_webssh:false
+ // });
+ // }
}
SelectOpenpublic=(e)=>{
@@ -511,7 +548,11 @@ export default class TPMsettings extends Component {
});
}
-
+ code_edit_permission = (e) => {
+ this.setState({
+ code_edit_permission: e.target.checked
+ })
+ }
code_hidden=(e)=>{
let sum = ""
if (e.target.checked === false) {
@@ -524,6 +565,35 @@ export default class TPMsettings extends Component {
});
}
+ confirmDeleteSecretRepo = ({title, onOk}) => {
+ confirm({
+ title: title ||
+
已创建的私密版本库及其内容,将在“保存”时被删除。
+
是否确认取消勾选?
+
,
+ okText: '确定',
+ cancelText: '取消',
+ onOk: () => {
+ this.setState({ is_secret_repository: false })
+ onOk && onOk()
+ },
+ onCancel() {
+ },
+ });
+ }
+ is_secret_repository = (e) => {
+ const checked = e.target.checked
+ if (!checked) {
+ if (this.state.init_is_secret_repository) {
+ this.confirmDeleteSecretRepo({
+ })
+ } else {
+ this.setState({ is_secret_repository: false })
+ }
+ } else {
+ this.setState({ is_secret_repository: true })
+ }
+ }
forbid_copy = (e) => {
let sum = ""
if (e.target.checked === false) {
@@ -535,6 +605,12 @@ export default class TPMsettings extends Component {
forbid_copy: sum,
});
}
+ shixun_vnc_evaluate=(e) => {
+ this.setState({
+ vnc_evaluate: e.target.checked,
+ });
+
+ }
shixun_vnc=(e)=>{
// let sum = ""
@@ -543,10 +619,34 @@ export default class TPMsettings extends Component {
// } else if (e.target.checked === true) {
// sum = 1
// }
- this.setState({
- vnc: e.target.checked,
- });
-
+ const vnc = e.target.checked;
+ if (!vnc) {
+ if (this.state.init_is_secret_repository && this.state.webssh != 2 && this.state.is_secret_repository == true) {
+ this.confirmDeleteSecretRepo({
+ onOk: () => {
+ this.setState({
+ vnc: e.target.checked,
+ vnc_evaluate: false,
+ });
+ }
+ })
+ } else {
+ if (this.state.webssh != 2) {
+ this.setState({
+ is_secret_repository: false
+ })
+ }
+ this.setState({
+ vnc: e.target.checked,
+ vnc_evaluate: false,
+ });
+ }
+ } else {
+ this.setState({
+ vnc: e.target.checked,
+ vnc_evaluate: false,
+ });
+ }
}
shixunsname = (e) => {
// let {shixunsstatus}=this.state;
@@ -765,16 +865,17 @@ export default class TPMsettings extends Component {
// });
// }
submit_edit_shixun = () => {
-
+ if (this.saving == true) return;
+ this.saving = true;
if(this.state.status===-1){
this.props.showSnackbar("该实训已被删除,保存失败!");
return
}
let {
- name, choice_main_type, choice_small_type, choice_standard_scripts, scope_partment, choice_standard_scriptssum,
+ name, choice_main_type, choice_small_type, choice_standard_scripts, scope_partment, choice_standard_scriptssum, vnc_evaluate,
evaluate_script, webssh, use_scope, trainee, can_copy, task_pass, test_set_permission, hide_code, code_hidden, forbid_copy, vnc,multi_webssh,
- opening_time,shixunmemoMDvalue,shixun_service_configlist
+ opening_time,shixunmemoMDvalue,shixun_service_configlist, is_secret_repository, code_edit_permission
} = this.state;
let newshixun_service_configlist = shixun_service_configlist.map(v => {
@@ -878,13 +979,16 @@ export default class TPMsettings extends Component {
let Url = `/shixuns/` + id + `.json`;
let data = {
shixun:{
+
name: name,
webssh: webssh,
use_scope: use_scope,
can_copy: can_copy,
vnc: vnc===null?undefined:vnc,
+ vnc_evaluate: vnc_evaluate===null?undefined:vnc_evaluate,
test_set_permission: test_set_permission,
code_hidden: code_hidden,
+ code_edit_permission: code_edit_permission,
trainee: trainee,
task_pass: task_pass,
hide_code: hide_code,
@@ -897,6 +1001,7 @@ export default class TPMsettings extends Component {
description: description_editormd,
evaluate_script: evaluate_script_editormd,
},
+ is_secret_repository: is_secret_repository,
main_type: choice_main_type,
small_type: choice_small_type,
scope_partment: scope_partment,
@@ -905,6 +1010,7 @@ export default class TPMsettings extends Component {
axios.put(Url, data).then((response) => {
// console.log(response)
+ this.saving = false;
if(response.status){
if (response.data.status === -1) {
this.props.showSnackbar(response.data.message);
@@ -916,6 +1022,7 @@ export default class TPMsettings extends Component {
}).catch((error) => {
console.log(error)
+ this.saving = false;
})
@@ -1451,6 +1558,7 @@ export default class TPMsettings extends Component {
name,
settingsData,
webssh,
+ is_secret_repository,
use_scope,
shixunsID,
can_copy,
@@ -1462,8 +1570,10 @@ export default class TPMsettings extends Component {
test_set_permission,
hide_code,
forbid_copy,
+ code_edit_permission,
code_hidden,
vnc,
+ vnc_evaluate,
scopetype,
scope_partment,
departmentslist,
@@ -1643,8 +1753,6 @@ export default class TPMsettings extends Component {
// onMouseEnter={operateauthority?this.bigopen:""}
onSelect={operateauthority?this.bigopens:""}
// open={opers}
-
- showSearch
optionFilterProp="children"
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
@@ -2174,6 +2282,15 @@ export default class TPMsettings extends Component {
+ {!code_hidden && !hide_code &&
+ 代码开放修改:
+
+
+ 勾选则学员可以修改版本库目录中的任意文件内容
+
+
}
+
隐藏代码窗口:
@@ -2191,6 +2308,15 @@ export default class TPMsettings extends Component {
+ { (vnc || webssh == 2) &&
+ 私密版本库:
+
+
+ 勾选则启用私密版本库,学员页面不能查看该版本库目录
+
+
}
+
禁用复制粘贴:
@@ -2222,12 +2348,21 @@ export default class TPMsettings extends Component {
{this.props.identity<3?
VNC图形化:
-
-
- 勾选则给学员的实践任务提供Ubuntu系统图形化实践窗口,否则不提供
-
+
+
+ 勾选则给学员的实践任务提供Ubuntu系统图形化实践窗口,否则不提供
+
+
:""}
+ {this.props.identity<3 && vnc ?
+ VNC图形化评测:
+
+
+ 勾选则在学员的VNC图形化页面中,开启评测功能
+
:""}
+
+
{this.props.identity<3?
@@ -2237,10 +2372,10 @@ export default class TPMsettings extends Component {
return(
-
+
{item.name}
{/*this.Deselectlittle(item.mirror_repository_id)}> */}
-
+
CPU(核):
diff --git a/public/react/src/modules/tpm/challengesnew/TPMMDEditor.js b/public/react/src/modules/tpm/challengesnew/TPMMDEditor.js
index 10cddfd31..ba4f1bc21 100644
--- a/public/react/src/modules/tpm/challengesnew/TPMMDEditor.js
+++ b/public/react/src/modules/tpm/challengesnew/TPMMDEditor.js
@@ -62,7 +62,7 @@ window.md_rec_data = md_rec_data;
function md_elocalStorage(editor,mdu,id){
if (window.sessionStorage){
var oc = window.sessionStorage.getItem('content'+mdu);
- if(oc !== null ){
+ if(oc !== null && oc != editor.getValue()){
console.log("#e_tips_"+id)
$("#e_tips_"+id).data('editor', editor);
var h = '您上次有已保存的数据,是否
恢复 ? /
不恢复 ';
@@ -169,7 +169,7 @@ function create_editorMD(id, width, high, placeholder, imageUrl, callback, initV
});
$("#" + _id + " [type=\"inline\"]").bind("click", function () {
- _editorName.cm.replaceSelection("$$$$");
+ _editorName.cm.replaceSelection("`$$$$`");
var __Cursor = _editorName.cm.getDoc().getCursor();
_editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 2);
_editorName.cm.focus();
diff --git a/public/react/src/modules/tpm/challengesnew/TPMchallengesnew.js b/public/react/src/modules/tpm/challengesnew/TPMchallengesnew.js
index cc3d335bb..2905699e7 100644
--- a/public/react/src/modules/tpm/challengesnew/TPMchallengesnew.js
+++ b/public/react/src/modules/tpm/challengesnew/TPMchallengesnew.js
@@ -408,13 +408,13 @@ export default class TPMchallengesnew extends Component {
} = this.state;
let options;
- options = optionsums.map((d, k) => {
- return (
-
{d}
- )
- })
-
- console.log(this.state.shixunExec_timeType )
+ if(optionsums!=undefined){
+ options = optionsums.map((d, k) => {
+ return (
+
{d}
+ )
+ })
+ }
return (
@@ -565,7 +565,7 @@ export default class TPMchallengesnew extends Component {
{
- shixunsskillvaluelist.length === 0 ? "" : shixunsskillvaluelist.map((itme, key) => {
+ shixunsskillvaluelist===undefined?"":shixunsskillvaluelist.length === 0 ? "" : shixunsskillvaluelist.map((itme, key) => {
return (
{itme}
this.delshixunsskilllist(key)}>×
diff --git a/public/react/src/modules/tpm/challengesnew/TPMevaluation.js b/public/react/src/modules/tpm/challengesnew/TPMevaluation.js
index 491751b6a..dd074fc0d 100644
--- a/public/react/src/modules/tpm/challengesnew/TPMevaluation.js
+++ b/public/react/src/modules/tpm/challengesnew/TPMevaluation.js
@@ -240,6 +240,7 @@ export default class TPMevaluation extends Component {
this.setState({
evaluationlist:newevaluationlist
})
+ console.log(newevaluationlist)
}
@@ -539,6 +540,14 @@ export default class TPMevaluation extends Component {
this.setevaluationlist(newevaluationlist);
}
+ // 修改测试集的匹配规则
+ changeEvaluationRule=(e,key)=>{
+ let {evaluationlist}=this.state;
+ let newevaluationlist=evaluationlist;
+ newevaluationlist[key].match_rule=e.target.value
+ this.setevaluationlist(newevaluationlist);
+ }
+
evaluationoninputvalue=(e,key,type)=>{
$.fn.autoHeight = function(){
function autoHeight(elem){
@@ -1159,6 +1168,13 @@ export default class TPMevaluation extends Component {
autoHeight="true"
onInput={(e)=>this.evaluationoninputvalue(e,key,"yq")}
>
+
+ 匹配规则:
+ this.changeEvaluationRule(e,key)}>
+ 完全匹配
+ 末尾匹配
+
+
)
})}
diff --git a/public/react/src/modules/tpm/component/TPMNav.js b/public/react/src/modules/tpm/component/TPMNav.js
index 1683c59a0..ce74be13b 100644
--- a/public/react/src/modules/tpm/component/TPMNav.js
+++ b/public/react/src/modules/tpm/component/TPMNav.js
@@ -5,7 +5,7 @@ import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class TPMNav extends Component {
render() {
- const { user, match, shixun } = this.props;
+ const { user, match, shixun, secret_repository } = this.props;
let isAdminOrCreator = false;
if (user) {
isAdminOrCreator = user.admin || user.manager
@@ -30,7 +30,10 @@ class TPMNav extends Component {
4||this.props.identity===undefined ? "none" : 'block'}}
- className={`${match.url.indexOf('repository') != -1 ? 'active' : ''} fl mr40`}>版本库
+ className={`${match.url.indexOf('/repository') != -1 ? 'active' : ''} fl mr40`}>版本库
+ {secret_repository && 4||this.props.identity===undefined ? "none" : 'block'}}
+ className={`${match.url.indexOf('secret_repository') != -1 ? 'active' : ''} fl mr40`}>私密版本库}
合作者
diff --git a/public/react/src/modules/tpm/component/TPMRightSection.js b/public/react/src/modules/tpm/component/TPMRightSection.js
index fd96454c4..5fb9fadc4 100644
--- a/public/react/src/modules/tpm/component/TPMRightSection.js
+++ b/public/react/src/modules/tpm/component/TPMRightSection.js
@@ -13,192 +13,193 @@ import {Icon,Tooltip} from 'antd';
// import "antd/dist/antd.css";
class TPMRightSection extends Component {
- constructor(props) {
- super(props)
- this.state = {
-
- TPMRightSection:false,
- clickNewsubscripttype:false
- }
- }
-
- // componentDidMount() {
- // let id=this.props.match.params.shixunId;
- //
- // let shixunsDetailsURL=`/shixuns/`+id+`/show_right.json`;
- //
- // axios.get(shixunsDetailsURL).then((response)=> {
- // if(response.status===200){
- // this.setState({
- // TPMRightSectionData: response.data
- // });
- // }
- // }).catch((error)=>{
- // console.log(error)
- // });
- // }
-
- // shouldComponentUpdate(nextProps, nextState) {
- // return nextProps.TPMRightSectionData !== this.state.TPMRightSectionData
- // }
- clickNewsubscript=(val)=>{
- if(val===0){
- this.setState({
- TPMRightSection:true,
- clickNewsubscripttype:true
- })
- }else{
- this.setState({
- TPMRightSection:false,
- clickNewsubscripttype:false
- })
- }
-
- }
- render() {
- let {TPMRightSection,clickNewsubscripttype}=this.state;
- let {TPMRightSectionData}=this.props
-
- return (
-
- {
- TPMRightSectionData===undefined?"":
-
-
-
创建者
-
-
-
-
-
-
-
{TPMRightSectionData===undefined?"":TPMRightSectionData.creator===undefined?"":TPMRightSectionData.creator.name}
-
-
发布 {TPMRightSectionData.user_shixuns_count}
- {/*
粉丝 {TPMRightSectionData.fans_count} */}
- {/*
取消关注 */}
-
-
-
-
-
- {
- TPMRightSectionData === undefined ? "" :TPMRightSectionData.tags===undefined?"": TPMRightSectionData.tags.length === 0 ? "" :
-
-
技能标签 {TPMRightSectionData.tags.length}
-
-
- { TPMRightSectionData.tags.map((item,key)=>{
- return(
- {item.tag_name}
- )})
- }
-
-
-
-
-
15&&clickNewsubscripttype===false?"newsubscript mb9 color-grey-9":"newsubscript mb9 color-grey-9 none"}
-
- data-tip-down="显示全部"
- onClick={()=>this.clickNewsubscript(0)}>...
-
-
-
-
this.clickNewsubscript(1)}>
-
-
-
-
-
- }
-
-
-
-
相关实践课程
-
- {
- TPMRightSectionData===undefined?"":TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.map((i,k)=>{
-
- return(
-
-
-
-
-
-
-
-
-
{i.name}
-
-
-
- {i.stages_count}
-
-
- {/**/}
- {/* {i.score_count} */}
- {/* */}
-
-
- {i.members_count}
-
-
-
-
-
-
-
- )
- })
- }
-
-
-
-
-
推荐实训
-
- {
- TPMRightSectionData===undefined?"":TPMRightSectionData.recommands===undefined?"":TPMRightSectionData.recommands.map((item,key)=>{
- return(
-
- )
- })
- }
-
-
-
-
- }
-
- )
+ constructor(props) {
+ super(props)
+ this.state = {
+
+ TPMRightSection:false,
+ clickNewsubscripttype:false
+ }
+ }
+
+ // componentDidMount() {
+ // let id=this.props.match.params.shixunId;
+ //
+ // let shixunsDetailsURL=`/shixuns/`+id+`/show_right.json`;
+ //
+ // axios.get(shixunsDetailsURL).then((response)=> {
+ // if(response.status===200){
+ // this.setState({
+ // TPMRightSectionData: response.data
+ // });
+ // }
+ // }).catch((error)=>{
+ // console.log(error)
+ // });
+ // }
+
+ // shouldComponentUpdate(nextProps, nextState) {
+ // return nextProps.TPMRightSectionData !== this.state.TPMRightSectionData
+ // }
+ clickNewsubscript=(val)=>{
+ if(val===0){
+ this.setState({
+ TPMRightSection:true,
+ clickNewsubscripttype:true
+ })
+ }else{
+ this.setState({
+ TPMRightSection:false,
+ clickNewsubscripttype:false
+ })
+ }
+
+ }
+ render() {
+ let {TPMRightSection,clickNewsubscripttype}=this.state;
+ let {TPMRightSectionData}=this.props
+
+ return (
+
+ {
+ TPMRightSectionData===undefined?"":
+
+
+
创建者
+
+
+
+
+
+
+
{TPMRightSectionData===undefined?"":TPMRightSectionData.creator===undefined?"":TPMRightSectionData.creator.name}
+
+
发布 {TPMRightSectionData.user_shixuns_count}
+ {/*
粉丝 {TPMRightSectionData.fans_count} */}
+ {/*
取消关注 */}
+
+
+
+
+
+ {
+ TPMRightSectionData === undefined ? "" :TPMRightSectionData.tags===undefined?"": TPMRightSectionData.tags.length === 0 ? "" :
+
+
技能标签 {TPMRightSectionData.tags.length}
+
+
+ { TPMRightSectionData.tags.map((item,key)=>{
+ return(
+ {item.tag_name}
+ )})
+ }
+
+
+
+
+
15&&clickNewsubscripttype===false?"newsubscript mb9 color-grey-9":"newsubscript mb9 color-grey-9 none"}
+
+ data-tip-down="显示全部"
+ onClick={()=>this.clickNewsubscript(0)}>...
+
+
+
+
this.clickNewsubscript(1)}>
+
+
+
+
+
+ }
+
+
+
+
所属课程
+
+ {
+ TPMRightSectionData===undefined?"":TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.map((i,k)=>{
+
+ return(
+
+
+
+
+
+
+
+
+
{i.name}
+
+
+
+ {i.stages_count}
+
+
+ {/**/}
+ {/* {i.score_count} */}
+ {/* */}
+
+
+ {i.members_count}
+
+
+
+
+
+
+
+ )
+ })
+ }
+
+
+
+ {TPMRightSectionData === undefined?"":TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.length === 0 ? "" :
+
+
推荐实训
+
+ {
+ TPMRightSectionData===undefined?"":TPMRightSectionData.recommands===undefined?"":TPMRightSectionData.recommands.map((item,key)=>{
+ return(
+
+ )
+ })
+ }
+
+
}
+
+
+ }
+
+ )
-
- }
+
+ }
}
export default TPMRightSection;
diff --git a/public/react/src/modules/tpm/component/modal/RepositoryChooseModal.js b/public/react/src/modules/tpm/component/modal/RepositoryChooseModal.js
new file mode 100644
index 000000000..4b72ae2bd
--- /dev/null
+++ b/public/react/src/modules/tpm/component/modal/RepositoryChooseModal.js
@@ -0,0 +1,153 @@
+// import React, { useState, useEffect, memo } from 'react';
+// import axios from 'axios'
+// import { Modal, Input } from 'antd';
+
+// function RepositoryChooseModal(props) {
+// const [trees, setTrees] = useState([])
+// const [path, setPath] = useState('')
+// const [pathArray, setPathArray] = useState([{val: "根目录/", path: ""}])
+// const [modalVisible, setModalVisible] = useState(true)
+
+// useEffect(() => {
+// repository('')
+// }, [])
+// function onOk() {
+
+// }
+// function onCancel() {
+
+// }
+// /**
+// 点nav 会传入key
+// 点item 会传入 newPath
+
+// item => name, type type tree/leaf
+// */
+// const repository=(item, key, newPath)=>{
+// let newPathArray = [] //
+// //
+// if (key) {
+// for(var i=0; i<=key; i++){
+// newPathArray.push(pathArray[i])
+// }
+// } else if (item) {
+// newPathArray = pathArray.slice(0)
+// newPathArray.push({val: item.name, path: pathArray[pathArray.length - 1] + "/" + item.name})
+// }
+
+// const path = item || key ? newPathArray[newPathArray.length - 1] : ''
+
+// let id = props.match.params.shixunId;
+// let url ="/shixuns/"+id+"/repository.json";
+// axios.post(url,{
+// path: path
+// }).then((response) => {
+// if (response.data.status === 403||response.data.status === 401||response.data.status === 500) {
+
+// }else{
+// setTrees(response.data.trees)
+// setPath(path)
+// pathArray(newPathArray)
+// }
+
+// }).catch((error) => {
+// console.log(error)
+// });
+// }
+// const savegetfilepath=(value)=>{
+// const state = {}
+// let {selectpath,saveshixunfilepath,pathtype} = state
+
+// if(pathtype===1){
+// let newselectpath;
+
+// if(saveshixunfilepath==="shixunfilepathplay"){
+// newselectpath=value
+// }else{
+// const type = selectpath.split(';');
+// let types=false;
+// for(var i=0; i{
+
+// }
+// function sendgetfilepath() {
+
+// }
+// return (
+//
+//
+//
+//
+// {/*文件导航*/}
+// {
+// pathArray.length===0?"":pathArray.map((item,key)=>{
+// return(
+// goblakepath(item.path,key,item)}>{item.val}
+// )
+// })
+// }
+// {/*文件*/}
+// {trees === undefined || trees === null ? "" : trees.map((item, key) => {
+// return(
+//
+//
+//
+
+// )
+// })}
+
+//
+//
+// 选中的文件路径:
+// saveselectpath(e)}
+// value={path}/>
+//
+
+//
onOk()}>确定
+//
onCancel()}>取消
+//
+//
+//
+// )
+
+// }
+
+// export default RepositoryChooseModal
\ No newline at end of file
diff --git a/public/react/src/modules/tpm/newshixuns/Newshixuns.js b/public/react/src/modules/tpm/newshixuns/Newshixuns.js
index d7b2b7d26..c2c464618 100644
--- a/public/react/src/modules/tpm/newshixuns/Newshixuns.js
+++ b/public/react/src/modules/tpm/newshixuns/Newshixuns.js
@@ -233,7 +233,8 @@ class Newshixuns extends Component {
systemenvironmenttype:false,
testcoderunmodetype:false,
attachmentidstype:false,
- datalisttype:false
+ datalisttype:false,
+ bottonloading:false
}
}
@@ -494,6 +495,9 @@ class Newshixuns extends Component {
} else {
newmulti_webssh = ""
}
+ this.setState({
+ bottonloading:true
+ })
axios.post(Url, {
name: name,
can_copy: can_copy,
@@ -513,9 +517,16 @@ class Newshixuns extends Component {
if (response.status === 200) {
window.location.href = "/shixuns/" + response.data.shixun_identifier + "/challenges";
// window.open("/shixuns/"+response.data.shixun_identifier+"/challenges");
- }
+ }else{
+ this.setState({
+ bottonloading:false
+ })
+ }
}).catch((error) => {
console.log(error)
+ this.setState({
+ bottonloading:false
+ })
})
}
@@ -759,19 +770,21 @@ class Newshixuns extends Component {
// 附件相关 START
handleChange = (info) => {
- let {fileList}=this.state;
+ if(info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
+ let {fileList} = this.state;
- if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
- console.log("handleChange1");
- // if(fileList.length===0){
+ if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
+ console.log("handleChange1");
+ // if(fileList.length===0){
let fileLists = info.fileList;
this.setState({
// fileList:appendFileSizeToUploadFileAll(fileList),
- fileList:fileLists,
- deleteisnot:false});
- // }
- }
-
+ fileList: fileLists,
+ deleteisnot: false
+ });
+ // }
+ }
+ }
}
onAttachmentRemove = (file) => {
if(!file.percent || file.percent == 100){
@@ -1319,7 +1332,9 @@ class Newshixuns extends Component {
diff --git a/public/react/src/modules/tpm/newshixuns/css/Newshixuns.css b/public/react/src/modules/tpm/newshixuns/css/Newshixuns.css
index da0924b94..e241dcf0d 100644
--- a/public/react/src/modules/tpm/newshixuns/css/Newshixuns.css
+++ b/public/react/src/modules/tpm/newshixuns/css/Newshixuns.css
@@ -384,4 +384,14 @@ a.white-btn.use_scope-btn:hover{
.ml82{
margin-left: 82px;
+}
+
+.ant-btn-primary.active, .ant-btn-primary:active {
+ color: #fff;
+ background-color: #096dd9;
+ border-color: #096dd9;
+}
+
+.ant-btn:hover, .ant-btn:focus, .ant-btn:active, .ant-btn.active{
+ background-color: #4CACFF;
}
\ No newline at end of file
diff --git a/public/react/src/modules/tpm/shixunchild/Repository/Repository.js b/public/react/src/modules/tpm/shixunchild/Repository/Repository.js
index a62151126..58379b1e8 100644
--- a/public/react/src/modules/tpm/shixunchild/Repository/Repository.js
+++ b/public/react/src/modules/tpm/shixunchild/Repository/Repository.js
@@ -15,7 +15,7 @@ import { trace, trace_collapse ,getImageUrl, toPath} from "educoder";
import RepositoryDirectories from './RepositoryDirectories'
import { ActionBtn , NoneData } from 'educoder'
-
+import RepositoryCombinePath from './RepositoryCombinePath'
const $ = window.$;
// 点击按钮复制功能
@@ -85,10 +85,13 @@ class Repository extends Component {
className=" guideBtn" >Git使用指南
{
this.props.current_user && (this.props.current_user.admin ==true || (TPMRightSectionData && TPMRightSectionData.creator && TPMRightSectionData.creator.login == this.props.current_user.login)) ?
- +添加文件 :""
+ !this.props.secret_repository_tab &&
+ +添加文件
+ :""
}
+
网址克隆:
+ {this.props.secret_repository_tab &&
+
+ }
+
@@ -181,7 +188,7 @@ class Repository extends Component {
{commits===undefined?"":commits[0].time}
:{commits===undefined?"":commits[0].title}
-
提交记录
diff --git a/public/react/src/modules/tpm/shixunchild/Repository/RepositoryCodeEditor.js b/public/react/src/modules/tpm/shixunchild/Repository/RepositoryCodeEditor.js
index bd7c8355b..51f6e35f2 100644
--- a/public/react/src/modules/tpm/shixunchild/Repository/RepositoryCodeEditor.js
+++ b/public/react/src/modules/tpm/shixunchild/Repository/RepositoryCodeEditor.js
@@ -121,6 +121,7 @@ class RepositoryCodeEditor extends Component {
const path = pathArray.join('/')
this.setState({ codeSaving: true })
axios.post(url, {
+ secret_repository: this.props.secret_repository_tab,
content: this.extend_editor.getValue(),
// type: forTest === true ? 1 : 0,
path: path
diff --git a/public/react/src/modules/tpm/shixunchild/Repository/RepositoryCombinePath.js b/public/react/src/modules/tpm/shixunchild/Repository/RepositoryCombinePath.js
new file mode 100644
index 000000000..aba008e20
--- /dev/null
+++ b/public/react/src/modules/tpm/shixunchild/Repository/RepositoryCombinePath.js
@@ -0,0 +1,82 @@
+import React, { Component } from 'react';
+
+import { Redirect } from 'react-router';
+
+import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
+
+import PropTypes from 'prop-types';
+
+import classNames from 'classnames';
+
+import axios from 'axios';
+
+import { trace_collapse, WordsBtn } from 'educoder'
+
+import { message, Input } from 'antd';
+
+
+const $ = window.$;
+
+
+class RepositoryCombinePath extends Component {
+ constructor(props) {
+ super(props)
+ this.state = {
+ value: this.props.secret_dir_path || '',
+ isEdit: false,
+ }
+ }
+
+ onSave = () => {
+ const { shixunId, pathArray } = this.props;
+ const url = `/shixuns/${shixunId}/set_secret_dir.json`
+
+ this.setState({ codeSaving: true })
+ axios.post(url, {
+ secret_dir_path: this.state.value
+ }
+ ).then((response) => {
+ if (response.data) {
+ message.success('保存成功');
+ this.setState({isEdit: false})
+ }
+ })
+ }
+ onChange = (e) => {
+ const { value } = e.target;
+ this.setState({ value })
+ }
+ onEdit = () => {
+ this.setState({isEdit: true}, () => {
+ window.$('.combinePathEditRow input')[0].focus()
+ });
+ }
+ render() {
+ const { fileContent, match, saveCode } = this.props;
+ const { isEdit, value } = this.state;
+ return (
+
+
+
+ 第一版本库合并路径:
+
+ {!isEdit && 修改 }
+ {isEdit && 保存 }
+
+
+
+ );
+ }
+}
+export default RepositoryCombinePath;
diff --git a/public/react/src/modules/tpm/shixunchild/Repository/TPMRepositoryCommits.js b/public/react/src/modules/tpm/shixunchild/Repository/TPMRepositoryCommits.js
index d0499a76c..663c5fcf3 100644
--- a/public/react/src/modules/tpm/shixunchild/Repository/TPMRepositoryCommits.js
+++ b/public/react/src/modules/tpm/shixunchild/Repository/TPMRepositoryCommits.js
@@ -34,7 +34,9 @@ class TPMRepositoryCommits extends Component {
let id = this.props.match.params.shixunId;
let collaborators=`/shixuns/`+id+`/commits.json`;
- axios.post(collaborators).then((response)=> {
+ axios.post(collaborators, {
+ secret_repository: this.props.secret_repository_tab
+ }).then((response)=> {
if(response.status===200){
this.setState({
diff --git a/public/react/src/modules/tpm/shixuns/shixunCss/shixunCard.css b/public/react/src/modules/tpm/shixuns/shixunCss/shixunCard.css
index 4470aaec1..1ec00a26e 100644
--- a/public/react/src/modules/tpm/shixuns/shixunCss/shixunCard.css
+++ b/public/react/src/modules/tpm/shixuns/shixunCss/shixunCard.css
@@ -6,9 +6,6 @@
margin-left: 32%;
}
-.square-Item{
- /*min-height: 324px;*/
-}
.square-img{
min-height: 210px;
}
diff --git a/public/react/src/modules/user/account/AccountBasicEdit.js b/public/react/src/modules/user/account/AccountBasicEdit.js
index bff4422ec..1f87454a5 100644
--- a/public/react/src/modules/user/account/AccountBasicEdit.js
+++ b/public/react/src/modules/user/account/AccountBasicEdit.js
@@ -421,7 +421,11 @@ class AccountBasic extends Component {
identity
}=this.state;
const { getFieldDecorator } = this.props.form;
- let{basicInfo}=this.props
+ let{ basicInfo }=this.props
+
+ // 已职业认证的账户不能修改职业,学校/单位,院系/部门,产品还未确定,先默认为false--可以更改
+ // basicInfo && basicInfo.professional_certification == "certified"
+ const professionalFlag = false;
// form合并了
const propsWithoutForm = Object.assign({}, this.props)
@@ -607,7 +611,7 @@ class AccountBasic extends Component {
message: '请先选择职业',
}],
})(
-
+
教师
学生
专业人士
@@ -628,7 +632,7 @@ class AccountBasic extends Component {
message: '请先输入学号',
}],
})(
-
+
)}
}
@@ -646,7 +650,7 @@ class AccountBasic extends Component {
message: '请先选择职称',
}],
})(
-
+
教授
副教授
讲师
@@ -669,7 +673,7 @@ class AccountBasic extends Component {
message: '请先选择职称',
}],
})(
-
+
企业管理者
部门管理者
高级工程师
@@ -703,7 +707,7 @@ class AccountBasic extends Component {
}],
})(
-
+
{
filterSchoolList && filterSchoolList.map((item,key)=>{
return({item.name} )
@@ -741,7 +745,7 @@ class AccountBasic extends Component {
// }
}],
})(
-
+
{
filterDepartments && filterDepartments.map((item,key)=>{
return(
diff --git a/public/react/src/modules/user/modal/RealNameCertificationModal.js b/public/react/src/modules/user/modal/RealNameCertificationModal.js
index 1d17b7c20..412cb58c7 100644
--- a/public/react/src/modules/user/modal/RealNameCertificationModal.js
+++ b/public/react/src/modules/user/modal/RealNameCertificationModal.js
@@ -2,7 +2,7 @@ import React, { Component } from "react";
import { message, Icon, Input, Form, Upload} from "antd";
import axios from 'axios'
import ModalWrapper from "../../courses/common/ModalWrapper"
-import { City, getUploadActionUrl, getImageUrl, ImageLayerHook } from 'educoder'
+import { City, getUploadActionUrl, getImageUrl, ImageLayerHook, getUploadActionUrlOfAuth } from 'educoder'
import '../account/common.css'
import authImg from '../../../images/account/auth.png'
@@ -125,14 +125,14 @@ class RealNameCertificationModal extends Component{
const { moduleName } = this.props
const { getFieldDecorator } = this.props.form;
let {certification}=this.props;
-
+ // /api/users/accounts/${this.props.current_user.login}/auth_attachment.json
const uploadProps2 = {
name: 'image',
data:{type:certification == 1 ? "real_name" : "professional"},
multiple: true,
showUploadList: false,
// https://newweb.educoder.net
- action: this.props.current_user ? `/api/users/accounts/${this.props.current_user.login}/auth_attachment.json` : '',
+ action: this.props.current_user ? `${getUploadActionUrlOfAuth(this.props.current_user.login)}` : '',
className: 'idPic-uploader',
onChange: this.handleChange2,
};
diff --git a/public/react/src/modules/user/usersInfo/Infos.js b/public/react/src/modules/user/usersInfo/Infos.js
index 242e25464..3a2632061 100644
--- a/public/react/src/modules/user/usersInfo/Infos.js
+++ b/public/react/src/modules/user/usersInfo/Infos.js
@@ -43,7 +43,10 @@ const InfosVideo = Loadable({
loader: () => import('./video/InfosVideo'),
loading:Loading,
})
-
+const InfosTopics=Loadable({
+ loader: () => import('./InfosTopics'),
+ loading:Loading,
+})
const $ = window.$;
class Infos extends Component{
@@ -258,13 +261,10 @@ class Infos extends Component{
{/* --------------------------------------------------------------------- */}
-
-
- {/* 众包 */}
- {/* http://localhost:3007/courses/1309/homework/9300/setting */}
- ( )
+ (props) => ( )
}
>
@@ -297,7 +297,15 @@ class Infos extends Component{
}
>
- {/* 项目 */}
+ {/* 众包 */}
+ {/* http://localhost:3007/courses/1309/homework/9300/setting */}
+ ( )
+ }
+ >
+
+ {/* 视频 */}
( )
@@ -305,6 +313,7 @@ class Infos extends Component{
>
+
( )
diff --git a/public/react/src/modules/user/usersInfo/InfosBanner.js b/public/react/src/modules/user/usersInfo/InfosBanner.js
index 3aa329ec9..5e79a9b2b 100644
--- a/public/react/src/modules/user/usersInfo/InfosBanner.js
+++ b/public/react/src/modules/user/usersInfo/InfosBanner.js
@@ -27,6 +27,14 @@ class InfosBanner extends Component{
let {pathname}=this.props.location;
moduleName=pathname.split("/")[3];
+ let user_id=this.props.current_user&&this.props.current_user.user_id;
+ let user_type=this.props.current_user&&this.props.current_user.user_identity;
+ let targetuserid=this.props.data&&this.props.data.id;
+
+ // console.log(is_current)
+ // console.log(current_user)
+ // console.log(current_user.is_teacher)
+ // console.log(current_user.admin)
return(
@@ -115,6 +123,14 @@ class InfosBanner extends Component{
to={`/users/${username}/videos`}>视频
}
+ {/*自己的主页且不是学生显示题库按钮*/}
+ {(is_current && current_user&& current_user.is_teacher )
+ &&
+ this.setState({moduleName: 'topics'})}
+ to={`/users/${username}/topics/personal`}>题库
+ }
+
diff --git a/public/react/src/modules/user/usersInfo/InfosCourse.js b/public/react/src/modules/user/usersInfo/InfosCourse.js
index bc401d4d7..f90b35d09 100644
--- a/public/react/src/modules/user/usersInfo/InfosCourse.js
+++ b/public/react/src/modules/user/usersInfo/InfosCourse.js
@@ -103,7 +103,7 @@ class InfosCourse extends Component{
} = this.state;
let is_current=this.props.is_current;
- console.log(this.props.current_user&&this.props.current_user.user_identity==="学生")
+ // console.log(this.props.current_user&&this.props.current_user.user_identity==="学生")
return(
}
-
+
共参与{totalCount}个{category?category=="manage"?"发布":"学习":"课堂"}
时间最新
+ {/* 289 */}
{
page == 1 && is_current && !category &&
this.props.current_user && this.props.current_user.user_identity != "学生" ?
: ""
@@ -135,7 +136,7 @@ class InfosCourse extends Component{
{
data && data.courses && data.courses.map((item,key)=>{
return(
-
this.turnToCourses(`${item.first_category_url}`,item.can_visited)} style={{"cursor": "pointer"}}>
+
this.turnToCourses(`${item.first_category_url}`,item.can_visited)} style={{"cursor": "pointer",height:"289px"}}>
{
item.is_public == 1 &&
diff --git a/public/react/src/modules/user/usersInfo/InfosPath.js b/public/react/src/modules/user/usersInfo/InfosPath.js
index 554d8380f..67809b6ef 100644
--- a/public/react/src/modules/user/usersInfo/InfosPath.js
+++ b/public/react/src/modules/user/usersInfo/InfosPath.js
@@ -147,6 +147,7 @@ class InfosPath extends Component{
时间最新
+ {/* 295 */}
{
page == 1 && is_current && !category &&
this.props.current_user && this.props.current_user.user_identity != "学生" ?
:""
diff --git a/public/react/src/modules/user/usersInfo/InfosProject.js b/public/react/src/modules/user/usersInfo/InfosProject.js
index 5aea5117b..030af5fb6 100644
--- a/public/react/src/modules/user/usersInfo/InfosProject.js
+++ b/public/react/src/modules/user/usersInfo/InfosProject.js
@@ -120,6 +120,7 @@ class InfosProject extends Component{
时间最新
+ {/* 289 */}
{
page == 1 && is_current && this.props.current_user && !category && this.props.current_user.user_identity != "学生" ?
:""
@@ -130,7 +131,7 @@ class InfosProject extends Component{
{
data && data.projects && data.projects.map((item,key)=>{
return(
-
this.turnToCourses(`/projects/${item.id}`,item.can_visited)} style={{"cursor": "pointer"}}>
+
this.turnToCourses(`/projects/${item.id}`,item.can_visited)} style={{"cursor": "pointer","height":"289px"}}>
{
item.is_public==1 &&
diff --git a/public/react/src/modules/user/usersInfo/InfosShixun.js b/public/react/src/modules/user/usersInfo/InfosShixun.js
index 4799626a9..61a9ce8cb 100644
--- a/public/react/src/modules/user/usersInfo/InfosShixun.js
+++ b/public/react/src/modules/user/usersInfo/InfosShixun.js
@@ -153,6 +153,7 @@ class InfosShixun extends Component{
+ {/* 298 */}
{
page == 1 && is_current && !category && this.props.current_user && this.props.current_user.user_identity != "学生" ?
:""
@@ -163,7 +164,7 @@ class InfosShixun extends Component{
{
data && data.shixuns && data.shixuns.map((item,key)=>{
return(
-
this.turnToCourses(`/shixuns/${item.identifier}/challenges`)}>
+
this.turnToCourses(`/shixuns/${item.identifier}/challenges`)}>
{
item.tag &&
{item.tag}
}
diff --git a/public/react/src/modules/user/usersInfo/InfosTopics.js b/public/react/src/modules/user/usersInfo/InfosTopics.js
new file mode 100644
index 000000000..8da80aad3
--- /dev/null
+++ b/public/react/src/modules/user/usersInfo/InfosTopics.js
@@ -0,0 +1,593 @@
+import React, { Component } from 'react';
+import { SnackbarHOC } from 'educoder';
+import {BrowserRouter as Router,Route,Switch,Link} from 'react-router-dom';
+import {Tooltip,Menu,Pagination,Spin, Dropdown,Checkbox} from 'antd';
+import axios from 'axios';
+import {getImageUrl,WordsBtn} from 'educoder';
+import moment from 'moment';
+import Modals from '../../modals/Modals';
+import SendTopics from '../../modals/SendTopics'
+import NoneData from '../../courses/coursesPublic/NoneData';
+import "./usersInfo.css";
+import Withoutpermission from './Withoutpermission.png';
+
+
+
+class InfosTopics extends Component{
+ constructor(props){
+ super(props);
+ this.state={
+ isSpin:false,
+ category:"normal",
+ course_list_id:undefined,
+ sort_by:"updated_at",
+ sort_direction:"desc",
+ page:1,
+ data:undefined,
+ checkBoxValues:[],
+ per_page:15,
+ isshowprofes:false
+ }
+ }
+
+ componentDidMount(){
+ // let types=this.props.match.params.topicstype;
+ // let professional_certification=this.props.current_user&&this.props.current_user.professional_certification;
+ //
+ // if(professional_certification===false&&types==="publicly"){
+ // this.setState({
+ // isshowprofes:true
+ // })
+ // }else{
+ // this.updataslist()
+ // }
+ this.updataslist()
+ }
+ // componentDidUpdate(prevProps) {
+ //
+ // if(prevProps.current_user!=this.props.current_user){
+ // let types=this.props.match.params.topicstype;
+ // let professional_certification=this.props.current_user&&this.props.current_user.professional_certification;
+ //
+ // console.log(professional_certification)
+ // if(professional_certification===false&&types==="publicly"){
+ // this.setState({
+ // isshowprofes:true
+ // })
+ // }else{
+ // this.updataslist()
+ // }
+ // }
+ // }
+ updataslist=()=>{
+ let types=this.props.match.params.topicstype;
+ let { category,course_list_id,sort_by,sort_direction,page}=this.state;
+ this.searchAlldata(types,category,course_list_id,sort_by,sort_direction,page)
+ }
+ searchAlldata=(type,category,course_list_id,sort_by,sort_direction,page)=>{
+
+ this.setState({
+ isSpin:true
+ })
+
+ let {per_page}=this.state;
+ let url=`/users/question_banks.json`;
+
+ axios.get(encodeURI(url),{params:{
+ type,
+ object_type:category,
+ course_list_id,
+ sort_by,
+ sort_direction,
+ page,
+ per_page
+ }
+ }).then((response) => {
+ if(response){
+ if(response.status){
+ if(response.data.status == -2){
+ this.setState({
+ isshowprofes:true,
+ isSpin:false
+ })
+ }else if(response.data.status === 403||response.data.status === 401||response.data.status === 500){
+ this.setState({
+ isSpin:false
+ })
+ }else{
+ this.setState({
+ data:response.data,
+ checkBoxValues:[],
+ isSpin:false
+ })
+ }
+ }
+ }
+
+ }).catch((error) => {
+ this.setState({
+ isSpin:false
+ })
+ });
+
+ }
+
+ searchCategory=(type)=>{
+ this.setState({
+ category:type,
+ course_list_id:undefined,
+ })
+
+ let types=this.props.match.params.topicstype;
+ let { category,course_list_id,sort_by,sort_direction,page}=this.state;
+ this.searchAlldata(types,type,undefined,sort_by,sort_direction,page)
+ }
+
+ searchCourselistid=(id)=>{
+ this.setState({
+ course_list_id:id
+ })
+
+ let types=this.props.match.params.topicstype;
+ let { category,course_list_id,sort_by,sort_direction,page}=this.state;
+ this.searchAlldata(types,category,id,sort_by,sort_direction,page)
+ }
+
+ onCheckBoxChange=(checkedValues)=>{
+ if(checkedValues.length>15){
+ this.props.showNotification("选择条数不能大于15条")
+ }else{
+ this.setState({
+ checkBoxValues:checkedValues
+ })
+ }
+
+ }
+
+ updatedlist=(updatedtype)=>{
+ let types=this.props.match.params.topicstype;
+ let { category,course_list_id,sort_by,sort_direction,page}=this.state;
+ if(updatedtype===sort_by){
+ if(sort_direction==="desc"){
+ this.setState({
+ sort_direction:"asc",
+ sort_by:updatedtype
+ })
+ this.searchAlldata(types,category,course_list_id,updatedtype,"asc",page)
+ }else{
+ this.setState({
+ sort_direction:"desc",
+ sort_by:updatedtype
+ })
+ this.searchAlldata(types,category,course_list_id,updatedtype,"desc",page)
+ }
+ }else{
+ this.setState({
+ sort_direction:"desc",
+ sort_by:updatedtype
+ })
+ this.searchAlldata(types,category,course_list_id,updatedtype,"desc",page)
+ }
+ }
+
+
+ changePage=(pageNumber)=>{
+ let types=this.props.match.params.topicstype;
+ let { category,course_list_id,sort_by,sort_direction,page}=this.state;
+ this.searchAlldata(types,category,course_list_id,sort_by,sort_direction,pageNumber)
+ this.setState({
+ page:pageNumber,
+ checkBoxValues:[]
+ })
+ }
+
+ deletecheckBoxValues=()=>{
+ let {checkBoxValues}=this.state;
+
+ if(checkBoxValues.length===0){
+ this.props.showNotification("请选择题库")
+ }else{
+ this.setState({
+ Modalstype:true,
+ Modalstopval:"是否确认删除?",
+ ModalCancel:this.topicscancelmodel,
+ ModalSave:this.topicssavedelete,
+ })
+ }
+ }
+
+ topicssavedelete=()=>{
+ let {checkBoxValues,category}=this.state;
+ const url = `/question_banks/multi_delete.json`;
+ axios.delete(url, { data: {
+ object_id: checkBoxValues,
+ object_type:category
+ }})
+ .then((response) => {
+ if(response.data.status===0){
+ this.updataslist()
+ this.props.showNotification(response.data.message)
+ }else{
+ this.props.showNotification(response.data.message)
+ }
+ })
+ .catch(function (error) {
+ console.log(error);
+ });
+
+ this.topicscancelmodel()
+ }
+
+
+ topicscancelmodel=()=>{
+ this.setState({
+ Modalstype:false,
+ Loadtype:false,
+ visible:false,
+ Modalstopval:"",
+ ModalCancel:"",
+ ModalSave:"",
+ checkBoxValues:[],
+ checkedtype:false
+ })
+
+ }
+ openTopics=(id)=>{
+ this.setState({
+ Modalstype:true,
+ Modalstopval:"公开后不能重设为私有",
+ ModalsBottomval:"是否确认设为公开?",
+ ModalCancel:this.topicscancelmodel,
+ ModalSave:()=>this.topicssaveonOpen(id),
+ })
+ }
+
+ topicssaveonOpen=(id)=>{
+
+ let {category}=this.state;
+ const url = `/question_banks/multi_public.json`;
+ axios.post(url,{
+ object_id:[id],
+ object_type:category
+ }).then((response) => {
+ if(response.data.status===0){
+ this.updataslist()
+ this.props.showNotification(response.data.message)
+ }else{
+ this.props.showNotification(response.data.message)
+ }
+ }).catch(function (error) {
+ console.log(error);
+ });
+
+ this.topicscancelmodel()
+ }
+
+
+ sendTopics=()=>{
+ let {checkBoxValues}=this.state;
+ if(checkBoxValues.length===0){
+ this.props.showNotification("请选择题库")
+ }else{
+ this.setState({
+ visible:true
+ })
+ }
+
+ }
+ render(){
+ let{
+ category,
+ course_list_id,
+ isSpin,
+ data,
+ page,
+ sort_direction,
+ sort_by,
+ checkBoxValues,
+ Modalstype,
+ visible,
+ isshowprofes
+ } = this.state;
+ let {
+ is_current,
+ current_user,
+ }=this.props;
+
+ let categorylist=[
+ {val:"普通作业",type:"normal"},
+ {val:"分组作业",type:"group"},
+ {val:"毕设选题",type:"gtopic"},
+ {val:"毕设任务",type:"gtask"},
+ {val:"试卷",type:"exercise"},
+ {val:"问卷",type:"poll"},
+ ]
+
+ let types=this.props.match.params.topicstype;
+ let username=this.props.match.params.username;
+
+
+ //types===publicly 公共
+ //types===personal 私有
+ let user_id=this.props.current_user&&this.props.current_user.user_id;
+ let user_type=this.props.current_user&&this.props.current_user.user_identity;
+ let targetuserid=this.props.data&&this.props.data.id;
+
+
+
+ // console.log(is_current)
+ // console.log(current_user)
+ // console.log(current_user.is_teacher)
+ // console.log(current_user.admin)
+ const menu = (
+
+ this.updatedlist("updated_at")}>
+ 最近更新
+
+ this.updatedlist("name")}>
+ 题目名称
+
+ {types==="publicly"?this.updatedlist("contributor")}>
+ 贡献者
+ :""}
+
+ );
+
+ // console.log(this.props)
+ return(
+
+ {/*提示*/}
+
+ {Modalstype&&Modalstype===true?
:""}
+
+ {/*发送至弹窗*/}
+ {
+ visible&&visible===true?
+
this.updataslist()}
+ topicscancelmodel={()=>this.topicscancelmodel()}
+ />:""
+ }
+
+
+
+
+ {types==="publicly"?
:
}
+
+ {isshowprofes===false?
+
+
+
+ {categorylist.map((item,key)=>{
+ return(
+ this.searchCategory(item.type)}>{item.val}
+ )
+ })}
+
+
+
+ {data&&data.count===undefined?"":data&&data.count===0?"":
+
this.searchCourselistid(undefined)}>全部
+
+ {data===undefined?"":data.course_list===undefined||data.course_list.length===0?"":data.course_list.map((item,key)=>{
+ return(
+
this.searchCourselistid(item.id)}>{item.name}
+ )
+ })}
+
+
}
+
+
:
+
+
+
+
通过职业认证的教师才能访问公共题库
+
+
+
+
}
+
+
+
+
+
+
+ {isshowprofes===false?data&&data.count===undefined?"":data&&data.count===0?"":
+
+ 共 {data&&data.count===undefined?0:data&&data.count} 个
+ 已选择 {checkBoxValues.length} 个 (不支持跨页勾选)
+
+
+
+
+
+ {sort_by==="updated_at"?'最近更新':sort_by==="name"?'题目名称':sort_by==="contributor"?"贡献者":""}
+ this.updatedlist(sort_by)}>
+
+
+
+
+
+ {user_type!="学生"?this.sendTopics()}>发送 :""}
+ {types==="personal"?((is_current && current_user && current_user.is_teacher ) || current_user && current_user.admin)
+ &&this.deletecheckBoxValues()}>删除 :""}
+
+
:""}
+
+
+ {isshowprofes===true?"":data===undefined? :data.question_banks===undefined||data.question_banks.length===0? :
+
+ {data.question_banks.map((item,key)=>{
+ return(
+
+ )})}
+
+ }
+
+ {
+ isshowprofes===true?"":data&&data.count >15 &&
+
+ }
+
+
+
+ )
+ }
+}
+export default InfosTopics;
\ No newline at end of file
diff --git a/public/react/src/modules/user/usersInfo/Withoutpermission.png b/public/react/src/modules/user/usersInfo/Withoutpermission.png
new file mode 100755
index 000000000..791a0bc6e
Binary files /dev/null and b/public/react/src/modules/user/usersInfo/Withoutpermission.png differ
diff --git a/public/react/src/modules/user/usersInfo/banks/BanksIndex.js b/public/react/src/modules/user/usersInfo/banks/BanksIndex.js
index e6c39d578..121119a0d 100644
--- a/public/react/src/modules/user/usersInfo/banks/BanksIndex.js
+++ b/public/react/src/modules/user/usersInfo/banks/BanksIndex.js
@@ -72,16 +72,50 @@ class BanksIndex extends Component{
constructor(props){
super(props);
this.state={
- crumbData:undefined
+ crumbData:undefined,
+ publicly:undefined
}
}
- initPublic = (crumbData) =>{
+ componentDidMount = () =>{
+ // let pathname = this.props.location.pathname;
+ // this.setState({
+ // publicly:pathname.indexOf("/publicly") > -1
+ // })
+
+ }
+
+ initPublic = (crumbData,data) =>{
+ if(data && data.status && data.status == -2){
+ this.props.history.push(`/topicbank/publicly`);
+ }
this.setState({
crumbData
})
}
+ componentDidUpdate(prevProps) {
+ // if(prevProps.current_user!=this.props.current_user){
+ // let { publicly }=this.state;
+ // if( this.props.checkIfLogin()) {
+ // if (this.props.current_user && this.props.current_user.professional_certification == false && publicly){
+ // this.props.history.push(`/topicbank/${this.props.current_user.login}/publicly`);
+ // }
+ // } else {
+ // this.props.showLoginDialog()
+ // }
+ // }
+ // let { publicly }=this.state;
+ // if(this.props.current_user && this.props.current_user.professional_certification == false && publicly){
+ // if( this.props.checkIfLogin()) {
+ // this.props.history.push(`/topicbank/${this.props.current_user.login}/publicly`);
+ // } else {
+ // this.props.showLoginDialog()
+ // }
+ // // console.log(`/topicbank/${this.props.current_user.login}/publicly`);
+ // }
+ }
+
render(){
let { crumbData }=this.state
const common = {
@@ -93,7 +127,7 @@ class BanksIndex extends Component{
{
crumbData &&
- 题库
+ { crumbData && crumbData.is_public == true ? '公共' : '我的' }题库
{
crumbData.crumbArray && crumbData.crumbArray.map((item,key)=>{
return(
@@ -105,23 +139,26 @@ class BanksIndex extends Component{
}
{
- crumbData &&
-
- {crumbData && crumbData.title}
- {crumbData.is_public == true ? '公开':'私有'}
-
- }
+ crumbData &&
+ {crumbData && crumbData.title}
+ { crumbData.is_public == true ?
+ 公开
+ :
+ 私有
+ }
+
+
}
{/*毕设任务编辑*/}
- {
return ( )
}
}>
- {
return (
- {
return (
- {
return (
- {
return ( )
@@ -156,40 +194,46 @@ class BanksIndex extends Component{
{/*题库问卷编辑详情*/}
- ( )
}
>
- {
return ( )
}
}>
-
- {/*毕设任务题库详情*/}
- {
+ return ( )
+ }
+ }>
+ {/*毕设任务题库详情*/}
+ ( )
}
>
{/*毕设内容题库详情*/}
- ( )
}
>
{/*分组作业题库详情*/}
- ( )
}
>
{/* 普通作业题库详情*/}
- ( )
}
diff --git a/public/react/src/modules/user/usersInfo/banks/BanksTabIndex.js b/public/react/src/modules/user/usersInfo/banks/BanksTabIndex.js
new file mode 100644
index 000000000..e38bb5de8
--- /dev/null
+++ b/public/react/src/modules/user/usersInfo/banks/BanksTabIndex.js
@@ -0,0 +1,90 @@
+import React, { Component } from 'react';
+
+import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
+
+
+import Loadable from 'react-loadable';
+import Loading from '../../../../Loading';
+
+import BanksMenu from './banksMenu'
+// 毕设选题
+const GtopicBanks = Loadable({
+ loader: () => import('./GtopicBanks'),
+ loading: Loading,
+})
+// 问卷内容
+const PollBanks = Loadable({
+ loader: () => import('./PollBanksContent'),
+ loading: Loading,
+})
+// 试卷详情
+const ExerciseBanksDetail = Loadable({
+ loader: () => import('./ExerciseBanksDetail'),
+ loading: Loading,
+});
+
+class BanksTabIndex extends Component{
+ constructor(props){
+ super(props);
+ this.state={
+ banksMenu:undefined
+ }
+ }
+
+ initPublic = (crumbData,menuData,data) =>{
+ this.setState({
+ banksMenu:menuData
+ })
+ this.props.initPublic(crumbData,data);
+ }
+
+ render(){
+ let{
+ banksMenu
+ }=this.state
+
+ const common={
+ initPublic:this.initPublic,
+ };
+ console.log("BanksTabIndex");
+ console.log(banksMenu);
+ console.log(this.props);
+ return(
+
+ {
+ banksMenu &&
+
+ }
+
+ {
+ return ( )
+ }
+ }>
+
+ {
+ return ( )
+ }
+ }>
+ {
+ return ( )
+ }
+ }>
+
+
+
+ )
+ }
+}
+export default (BanksTabIndex);
\ No newline at end of file
diff --git a/public/react/src/modules/user/usersInfo/banks/ExerciseBanksDetail.js b/public/react/src/modules/user/usersInfo/banks/ExerciseBanksDetail.js
new file mode 100644
index 000000000..00fb830db
--- /dev/null
+++ b/public/react/src/modules/user/usersInfo/banks/ExerciseBanksDetail.js
@@ -0,0 +1,56 @@
+import React, { Component } from 'react';
+import axios from 'axios'
+
+import ExerciseDisplay from '../../../courses/exercise/ExerciseDisplay'
+
+class ExerciseBanksDetail extends Component{
+ constructor(props){
+ super(props);
+ this.state={
+
+ }
+ }
+
+ componentDidMount = () =>{
+
+ }
+ detailFetchCallback = (result) => {
+ let Id=this.props.match.params.Id;
+
+ const crumbData={
+ title: result.data.exercise && result.data.exercise.name,
+ is_public: result.data.exercise && result.data.exercise.is_public,
+ crumbArray:[
+ {content:'详情'},
+ ]
+ }
+ const menuData={
+ tab:'0',//tab选中的index
+ menuArray:[//tab以及tab路由
+ {to:`/banks/exercise/${Id}/${this.props.match.params.type}?tab=0`,content:'内容详情'}
+ ],
+ category:'exercise',//
+ tos: `/banks/exercise/${Id}/edit/${this.props.match.params.type}`,
+ id: Id,
+ is_public: result.data.exercise && result.data.exercise.is_public,
+ type:this.props.match.params.type,
+ authorize:result && result.data && result.data.authorize,
+ }
+ this.props.initPublic(crumbData,menuData,result.data);
+ }
+
+ render(){
+ let { pollDetail } = this.state
+
+ return(
+
+
+
+
+ )
+ }
+}
+export default ExerciseBanksDetail
diff --git a/public/react/src/modules/user/usersInfo/banks/ExerciseBanksEdit.js b/public/react/src/modules/user/usersInfo/banks/ExerciseBanksEdit.js
new file mode 100644
index 000000000..94d093454
--- /dev/null
+++ b/public/react/src/modules/user/usersInfo/banks/ExerciseBanksEdit.js
@@ -0,0 +1,71 @@
+import React, { Component } from 'react';
+import axios from 'axios'
+import { ActionBtn } from 'educoder'
+
+
+import ExerciseNewCommon from '../../../courses/exercise/ExerciseNewCommon'
+
+class ExerciseBanksEdit extends Component {
+ constructor(props){
+ super(props);
+ this.state = {
+ isPublic: undefined,
+ // isGroup: false
+ }
+ }
+ componentDidMount = () =>{
+
+
+ }
+
+ initData = (responseData) =>{
+ const Id = this.props.match.params.Id;
+
+ const crumbData={
+ title:'编辑',
+ is_public: responseData && responseData.data && responseData.data.exercise.is_public,
+ crumbArray:[
+ {to:`/banks/exercise/${Id}/${this.props.match.params.type}`,content:'详情'},
+ {content:'编辑'}
+ ]
+ }
+ this.props.initPublic(crumbData,responseData.data);
+ }
+
+ render(){
+ let { Id, type } = this.props.match.params
+ const common = {
+ // onCancel:this.onCancel,
+ // isGroup: this.isGroup,
+ // doNew: this.doNew,
+ // doEdit: this.doEdit,
+ initData: this.initData
+ }
+ return(
+
+
+
this.exerciseNewCommonRef = ref}
+ isEdit={true}
+ shixunsUrl={`/exercise_banks/choose_shixun.json`}
+ exercise_url={'exercise_banks'}
+ exercise_url_questions={'exercise_bank_questions'}
+ bottomSection={
+ 完成
+ }
+ >
+
+ )
+ }
+}
+export default ExerciseBanksEdit;
\ No newline at end of file
diff --git a/public/react/src/modules/user/usersInfo/banks/GtaskBanksEdit.js b/public/react/src/modules/user/usersInfo/banks/GtaskBanksEdit.js
new file mode 100644
index 000000000..8209effa4
--- /dev/null
+++ b/public/react/src/modules/user/usersInfo/banks/GtaskBanksEdit.js
@@ -0,0 +1,99 @@
+import React, { Component } from 'react';
+import axios from 'axios'
+import NewGtaskForm from './NewGtaskForm';
+import NewWorkForm from "./HomeworkBanksEdit";
+
+class GtaskBanksEdit extends Component {
+ constructor(props){
+ super(props);
+ this.state = {
+ isPublic: undefined,
+ isGroup: false
+ }
+ }
+ componentDidMount = () =>{
+ let workId = this.props.match.params.workId;
+ this.initData(workId);
+ }
+
+ initData = (workId) =>{
+
+ let url = `/task_banks/${workId}.json`;
+ axios.get(url).then((result)=>{
+ if(result){
+ const crumbData={
+ title:'编辑',
+ is_public:result && result.data && result.data.is_public,
+ crumbArray:[
+ {to:`/banks/gtask/${workId}/${this.props.match.params.type}?tab=0`,content:'详情'},
+ {content:'编辑'}
+ ]
+ }
+ this.props.initPublic(crumbData,result.data);
+ result.data.isEdit = true;
+ this.setState({ data:result.data})
+ this.newWorkFormRef.initValue(result.data);
+ }
+ }).catch((error)=>{
+ console.log(error)
+ })
+ }
+
+
+ doNew = () => {
+ }
+ doEdit = (params) => {
+ const workId = this.props.match.params.workId
+ const newUrl = `/homework_banks/${workId}.json`
+
+ // const isGroup = this.props.isGroup()
+ axios.put(newUrl, params)
+ .then((response) => {
+ if (response.data.status == 0) {
+ this.props.showNotification('保存成功')
+ this.toWorkDetail()
+ }
+ })
+ .catch(function (error) {
+ console.log(error);
+ });
+ }
+ toWorkDetail = () => {
+ window.location.href=`/banks/gtask/${this.props.match.params.workId}/${this.props.match.params.type}?tab=0`;
+ this.props.initPublic(undefined);
+ }
+ onCancel = () => {
+ this.toWorkDetail()
+ }
+ isGroup = () => {
+ return this.state.isGroup;
+ }
+ render(){
+
+ const common = {
+ onCancel:this.onCancel,
+ isGroup: this.isGroup,
+ doNew: this.doNew,
+ doEdit: this.doEdit,
+ }
+ return(
+
+
+ this.newWorkFormRef = ref}
+ topicId={this.props.match.params.workId}
+ >
+
+ )
+ }
+}
+export default GtaskBanksEdit;
\ No newline at end of file
diff --git a/public/react/src/modules/user/usersInfo/banks/GtopicBanks.js b/public/react/src/modules/user/usersInfo/banks/GtopicBanks.js
new file mode 100644
index 000000000..be7922d90
--- /dev/null
+++ b/public/react/src/modules/user/usersInfo/banks/GtopicBanks.js
@@ -0,0 +1,37 @@
+import React, { Component } from 'react';
+
+
+class GtopicBanks extends Component{
+ constructor(props){
+ super(props);
+ }
+ componentDidMount = () =>{
+ let bankId = this.props.match.params.bankId
+ const crumbData={
+ title:'MySQL数据库编程开发实训(基础篇)111',
+ is_public:true,
+ crumbArray:[
+ {content:'详情'},
+ ]
+ }
+ const menuData={
+ tab:'0',//tab选中的index
+ menuArray:[//tab以及tab路由
+ {to:'/banks/gtopic/1',content:'内容详情'},
+ // {to:'/banks/gtopic/1/answer',content:'参考答案'},
+ ],
+ category:'topic',//毕设选题
+ id:bankId,
+ is_public:true,
+ }
+ this.props.initPublic(crumbData,menuData);
+ }
+ render(){
+ return(
+
+
+
+ )
+ }
+}
+export default GtopicBanks;
\ No newline at end of file
diff --git a/public/react/src/modules/user/usersInfo/banks/GtopicBanksEdit.js b/public/react/src/modules/user/usersInfo/banks/GtopicBanksEdit.js
new file mode 100644
index 000000000..a03a5b533
--- /dev/null
+++ b/public/react/src/modules/user/usersInfo/banks/GtopicBanksEdit.js
@@ -0,0 +1,88 @@
+import React, { Component } from 'react';
+import axios from 'axios'
+
+import GraduateTopicNewFrom from '../../../courses/graduation/topics/GraduateTopicNewFrom'
+
+class GtopicBanksEdit extends Component{
+ constructor(props){
+ super(props);
+ this.state = {
+ isPublic:undefined
+ }
+ }
+ componentDidMount = () =>{
+ let bankId = this.props.match.params.bankId;
+ this.initData(bankId);
+ }
+
+ initData = (bankId) =>{
+ let url = `/gtopic_banks/${bankId}/edit.json`;
+ axios.get(url).then((result)=>{
+ if(result){
+ const crumbData={
+ title:'编辑',
+ is_public:result && result.data.selected_data && result.data.selected_data.is_public,
+ crumbArray:[
+ {to:`/banks/gtopic/${bankId}/${this.props.match.params.type}?tab=0`,content:'详情'},
+ {content:'编辑'}
+ ]
+ }
+ this.props.initPublic(crumbData,result.data);
+
+ this.GraduateTopicNewFromRef.initValue(result);
+ }
+ }).catch((error)=>{
+ console.log(error)
+ })
+ }
+
+ // 编辑保存
+ editSave = (param,attachments,bankId) =>{
+ const url = `/gtopic_banks/${bankId}.json`;
+ let params = {
+ gtopic_bank:param,
+ attachment_ids:attachments
+ }
+ axios.put(url,params).then((result)=>{
+ if(result){
+ this.props.showNotification('保存成功!');
+ this.props.history.push(`/banks/gtopic/${bankId}/${this.props.match.params.type}?tab=0`);
+ }
+ }).catch((error)=>{
+ console.log(error);
+ })
+ }
+
+ // 取消
+ editCancel = () =>{
+ this.props.history.push(`/banks/gtopic/${this.props.match.params.bankId}/${this.props.match.params.type}?tab=0`);
+ this.props.initPublic(undefined);
+ }
+
+ render(){
+ let { bankId } = this.props.match.params
+ const common = {
+ editSave:this.editSave,
+ editCancel:this.editCancel
+ }
+ return(
+
+
+ this.GraduateTopicNewFromRef = ref}
+ topicId={bankId}
+ >
+
+ )
+ }
+}
+export default GtopicBanksEdit;
\ No newline at end of file
diff --git a/public/react/src/modules/user/usersInfo/banks/HomeworkBanksEdit.js b/public/react/src/modules/user/usersInfo/banks/HomeworkBanksEdit.js
new file mode 100644
index 000000000..33a981141
--- /dev/null
+++ b/public/react/src/modules/user/usersInfo/banks/HomeworkBanksEdit.js
@@ -0,0 +1,103 @@
+import React, { Component } from 'react';
+import axios from 'axios'
+
+
+import NewWorkForm from '../../../courses/busyWork/NewWorkForm'
+
+class HomeworkBanksEdit extends Component {
+ constructor(props){
+ super(props);
+ this.state = {
+ isPublic: undefined,
+ // isGroup: false
+ }
+ }
+ componentDidMount = () =>{
+ let workId = this.props.match.params.workId;
+
+ this.initData(workId);
+ }
+
+ initData = (workId) =>{
+ let url = `/homework_banks/${workId}.json`;
+ axios.get(url).then((result)=>{
+ if(result){
+ const crumbData={
+ title:'编辑',
+ is_public:result && result.data && result.data.is_public,
+ crumbArray:[
+ {to:`/banks/${this.getModuleName()}/${workId}/${this.props.match.params.type}?tab=0`,content:'详情'},
+ {content:'编辑'}
+ ]
+ }
+ this.props.initPublic(crumbData,result.data);
+ result.data.isEdit = true;
+ result.data.ref_attachments = result.data.reference_attachments
+ // this.setState({ isGroup: result.data.min_num || result.data.max_num })
+ this.newWorkFormRef.initValue(result.data);
+ }
+ }).catch((error)=>{
+ console.log(error)
+ })
+ }
+
+
+ doNew = () => {
+ }
+ doEdit = (params) => {
+ const workId = this.props.match.params.workId
+ const newUrl = `/homework_banks/${workId}.json`
+
+ // const isGroup = this.props.isGroup()
+ axios.put(newUrl, params)
+ .then((response) => {
+ if (response.data.status == 0) {
+ this.props.showNotification('保存成功')
+ this.toWorkDetail()
+ }
+ })
+ .catch(function (error) {
+ console.log(error);
+ });
+ }
+ getModuleName = () => {
+ return this.props.isGroup ? 'group' : 'normal'
+ }
+ toWorkDetail = () => {
+ this.props.history.push(`/banks/${this.getModuleName()}/${this.props.match.params.workId}/${this.props.match.params.type}?tab=0`)
+ this.props.initPublic(undefined);
+ }
+ onCancel = () => {
+ this.toWorkDetail()
+ }
+ isGroup = () => {
+ return this.props.isGroup;
+ }
+ render(){
+ let { bankId } = this.props.match.params
+ const common = {
+ onCancel:this.onCancel,
+ isGroup: this.isGroup,
+ doNew: this.doNew,
+ doEdit: this.doEdit,
+ }
+ return(
+
+
+ this.newWorkFormRef = ref}
+ >
+
+ )
+ }
+}
+export default HomeworkBanksEdit;
\ No newline at end of file
diff --git a/public/react/src/modules/user/usersInfo/banks/NewGtaskForm.js b/public/react/src/modules/user/usersInfo/banks/NewGtaskForm.js
new file mode 100644
index 000000000..8c0f6474c
--- /dev/null
+++ b/public/react/src/modules/user/usersInfo/banks/NewGtaskForm.js
@@ -0,0 +1,358 @@
+import React,{ Component } from "react";
+import { Input, InputNumber, Form, Button, Checkbox, Upload, Icon, message, Modal } from "antd";
+import axios from 'axios'
+import { WordsBtn, getUrl, ConditionToolTip, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll } from 'educoder'
+import TPMMDEditor from '../../../tpm/challengesnew/TPMMDEditor';
+const $ = window.$;
+const MAX_TITLE_LENGTH = 60;
+class NewGtaskForms extends Component{
+ constructor(props){
+ super(props);
+ this.contentMdRef = React.createRef();
+ this.state={
+ title_num:0,
+ description:"",
+ contentFileList: [],
+ }
+ }
+
+
+ initValue = (data) => {
+
+ if (data.isEdit===true) {
+ const contentFileList = data.attachments.map(item => {
+ return {
+ id: item.id,
+ uid: item.id,
+ name: appendFileSizeToUploadFile(item),
+ url: item.url,
+ filesize: item.filesize,
+ status: 'done'
+ }
+ })
+ this.setState({
+ ...data,
+ base_on_project: data.task_type===2?data.group_info.base_on_project:undefined,
+ title_num: parseInt(data.name.length),
+ min_num: data.task_type===2?data.group_info.min_number:undefined,
+ max_num: data.task_type===2?data.group_info.max_number:undefined,
+ contentFileList,
+ }, () => {
+ setTimeout(() => {
+ this.contentMdRef.current.setValue(data.description || '')
+ }, 2000)
+
+ this.props.form.setFieldsValue({
+ title: data.name,
+ description: data.description || '',
+ });
+
+ })
+ }
+
+
+ }
+
+
+ // 输入title
+ changeTitle=(e)=>{
+ console.log(e.target.value.length);
+ this.setState({
+ title_num: parseInt(e.target.value.length)
+ })
+ }
+ handleContentUploadChange = (info) => {
+ let contentFileList = info.fileList;
+ this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList) });
+ }
+ deleteAttachment = (file, stateName) => {
+ // 初次上传不能直接取uid
+ const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
+ axios.delete(url, {
+ })
+ .then((response) => {
+ if (response.data) {
+ const { status } = response.data;
+ if (status == 0) {
+ console.log('--- success')
+
+ this.setState((state) => {
+ const index = state[stateName].indexOf(file);
+ const newFileList = state[stateName].slice();
+ newFileList.splice(index, 1);
+ return {
+ [stateName]: newFileList,
+ };
+ });
+ }
+ }
+ })
+ .catch(function (error) {
+ console.log(error);
+ });
+ }
+
+ onAttachmentRemove = (file, stateName) => {
+ if(file.response!=undefined){
+ this.props.confirm({
+ content: '是否确认删除?',
+
+ onOk: () => {
+ this.deleteAttachment(file, stateName)
+ },
+ onCancel() {
+ console.log('Cancel');
+ },
+ });
+ return false;
+ }
+
+ }
+
+ handleSubmit = () => {
+
+ let {contentFileList,min_num,max_num,base_on_project}=this.state;
+ let {data}=this.props;
+ let task_type=data.task_type
+ let topicId=this.props.topicId
+ let attachment_ids = contentFileList.map(item => {
+ return item.response ? item.response.id : item.id
+ })
+ this.props.form.validateFields((err, values) => {
+
+ const mdContnet = this.contentMdRef.current.getValue().trim();
+
+ values.description = mdContnet;
+
+ if (!err) {
+ if (this.props.data.isEdit===true) {
+ let url="/task_banks/"+topicId+".json";
+ axios.put(url, {
+ gtask_bank: {
+ name: values.title,
+ description: values.description,
+ min_num:task_type===1?undefined:min_num,
+ max_num:task_type===1?undefined:max_num,
+ base_on_project: task_type===1?task_type:base_on_project===true?1:0
+ },
+ attachment_ids:attachment_ids
+ }
+ ).then((response) => {
+ if(response.data.status===0){
+ this.props.showNotification(response.data.message)
+ this.props.onCancel()
+ }else{
+ this.props.showNotification(response.data.message)
+ }
+ }).catch((error) => {
+ console.log(error)
+ })
+ }
+ } else {
+ $("html").animate({ scrollTop: $('html').scrollTop() - 100 })
+ }
+ })
+ }
+
+ max_num_change = (val) => {
+ if (val < 2) {
+ this.setState({
+ max_num: 2,
+ })
+ return;
+ }
+ const { min_num } = this.state;
+ this.setState({
+ max_num: val,
+ min_num: val <= min_num ? val - 1 : min_num
+ })
+ }
+
+ min_num_change = (val) => {
+ this.setState({ min_num: val })
+ }
+
+ base_on_project_change = () => {
+ this.setState({ base_on_project: !this.state.base_on_project })
+ }
+
+ render(){
+ const { getFieldDecorator } = this.props.form;
+ let{
+ title_value, contentFileList, answerFileList, max_num, min_num, base_on_project,
+ init_max_num, init_min_num,
+ title_num, course_name, category, has_commit, has_project,
+ isEdit
+ }=this.state
+ const uploadProps = {
+ width: 600,
+ fileList: contentFileList,
+ multiple: true,
+ // https://github.com/ant-design/ant-design/issues/15505
+ // showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
+ // showUploadList: false,
+ action: `${getUrl()}/api/attachments.json`,
+ onChange: this.handleContentUploadChange,
+ onRemove: (file) => this.onAttachmentRemove(file, 'contentFileList'),
+ beforeUpload: (file) => {
+ console.log('beforeUpload', file.name);
+ const isLt150M = file.size / 1024 / 1024 < 150;
+ if (!isLt150M) {
+ message.error('文件大小必须小于150MB!');
+ }
+ return isLt150M;
+ },
+ };
+
+
+ return(
+
+
+
+ {getFieldDecorator('title', {
+ rules: [{
+ required: true, message: '请输入标题'
+ }],
+ })(
+
+ )}
+
+
+
+
+
+ {
+ {getFieldDecorator('description', {
+ rules: [{
+ required: true, message: '请输入任务内容说明'
+ }],
+ })(
+
+ )}
+ }
+
+
+ 上传附件
+
+ (单个文件150M以内)
+
+ {this.props.data&&this.props.data.task_type===2?
+
+ {getFieldDecorator('personNum', {
+ rules: [{
+ required: false
+ // required: true, message: '请输入最小人数和最大人数'
+ }],
+ })(
+
+
+
+ {/* max={has_commit ? init_min_num : null } */}
+ 每组最小人数: 人
+
+
+
+ {/* min={has_commit ? init_max_num : (min_num == undefined ? 2 : min_num + 1) } */}
+
+ 每组最大人数: 人
+
+
学生提交作品时需要关联同组成员,组内成员作品共享
+
+
+
+ 基于项目(选中,则必须在本平台创建项目,项目管理员可以提交作品;不选中,无需在平台创建项目,任意小组成员均可以提交作品)
+
+
+
+ )}
+ :""
+ }
+
+
+
+
+
+ )
+ }
+}
+
+
+const NewGtaskForm = Form.create({ name: 'NewGtaskForm' })(NewGtaskForms);
+export default NewGtaskForm;
\ No newline at end of file
diff --git a/public/react/src/modules/user/usersInfo/banks/PollBanksContent.js b/public/react/src/modules/user/usersInfo/banks/PollBanksContent.js
new file mode 100644
index 000000000..be6bd5e2c
--- /dev/null
+++ b/public/react/src/modules/user/usersInfo/banks/PollBanksContent.js
@@ -0,0 +1,69 @@
+import React, { Component } from 'react';
+import axios from 'axios'
+
+import PollDetailTabThirdInfo from '../../../courses/poll/PollDetailTabThirdInfo'
+
+class PollBanksContent extends Component{
+ constructor(props){
+ super(props);
+ this.state={
+ pollDetail:undefined
+ }
+ }
+
+ componentDidMount = () =>{
+ console.log("PollBanksContent");
+ console.log(this.props)
+ let bankId=this.props.match.params.bankId;
+ let url = `/exercise_banks/${bankId}.json`
+ axios.get(url).then((result)=>{
+ if(result){
+ let pollDetail = {
+ poll:{
+ id: result.data.poll && result.data.poll.id ,
+ polls_description: result.data.poll && result.data.poll.description,
+ polls_name: result.data.poll && result.data.poll.name,
+ is_public:result.data.poll && result.data.poll.is_public
+ },
+ question_types:result.data.question_types,
+ questions:result.data.questions,
+ }
+ const crumbData={
+ title:result.data.poll && result.data.poll.name,
+ is_public:result.data.poll && result.data.poll.is_public,
+ crumbArray:[
+ {content:'详情'},
+ ]
+ }
+ const menuData={
+ tab:'0',//tab选中的index
+ menuArray:[//tab以及tab路由
+ {to:`/banks/poll/${bankId}/${this.props.match.params.type}`,content:'内容详情'}
+ ],
+ category:'poll',//毕设选题
+ tos:`/banks/poll/${bankId}/edit/${this.props.match.params.type}`,
+ id:bankId,
+ is_public:result.data.poll && result.data.poll.is_public,
+ type:this.props.match.params.type,
+ authorize:result && result.data && result.data.authorize,
+ }
+ this.props.initPublic(crumbData,menuData,result.data);
+ this.setState({
+ pollDetail
+ })
+ }
+ }).catch((error)=>{
+ console.log(error);
+ })
+ }
+
+ render(){
+ let { pollDetail } = this.state
+ return(
+
+ )
+ }
+}
+export default PollBanksContent
diff --git a/public/react/src/modules/user/usersInfo/banks/banksMenu.js b/public/react/src/modules/user/usersInfo/banks/banksMenu.js
new file mode 100644
index 000000000..1c753450b
--- /dev/null
+++ b/public/react/src/modules/user/usersInfo/banks/banksMenu.js
@@ -0,0 +1,196 @@
+import React, { Component } from 'react';
+
+import { Menu } from 'antd'
+import { Link } from 'react-router-dom'
+import { WordsBtn } from 'educoder'
+import "../usersInfo.css"
+import "../../../courses/css/Courses.css"
+import "../../../courses/css/busyWork.css"
+import SendTopics from '../../../modals/SendTopics';
+import Modals from '../../../modals/Modals';
+import axios from 'axios';
+class BanksMenu extends Component{
+ constructor(props){
+ super(props);
+ this.state={
+ visible:false,
+ tab:['0'],
+ }
+ }
+
+ //发送至相关
+ sendTopics=()=>{
+ this.setState({
+ visible:true
+ })
+ }
+ componentDidMount() {
+ debugger
+ try {
+ const query = this.props.location.search;
+ const type = query.split('?tab=');
+ if(type[1]===undefined){
+ this.setState({
+ tab:['0'],
+ });
+ }else{
+ if(type[1]==="0"){
+ this.setState({
+ tab:['0'],
+ });
+ }else if(type[1]==="1"){
+ this.setState({
+ tab:['1'],
+ });
+ }
+ }
+
+ }catch (e) {
+ this.setState({
+ tab:['0'],
+ });
+ }
+
+ }
+
+ topicscancelmodel=()=>{
+ this.setState({
+ Modalstype:false,
+ Loadtype:false,
+ visible:false,
+ Modalstopval:"",
+ ModalCancel:"",
+ ModalSave:"",
+ checkBoxValues:[],
+ checkedtype:false
+ })
+ }
+
+ //删除相关
+
+ deletecheckBoxValues=(id,type)=>{
+
+ this.setState({
+ Modalstype:true,
+ Modalstopval:"是否确认删除?",
+ ModalCancel:this.topicscancelmodel,
+ ModalSave:()=>this.topicssavedelete(id,type),
+ })
+
+ }
+
+ topicssavedelete=(id,type)=>{
+ console.log("删除了");
+ console.log(id);
+ console.log(type);
+ const url = `/question_banks/multi_delete.json`;
+ axios.delete(url, { data: {
+ object_id: [id],
+ object_type:type
+ }})
+ .then((response) => {
+ console.log(response);
+ if(response){
+ if(response.data){
+ if(response.data.status===0){
+ this.props.showNotification(response.data.message)
+ window.location.href=`/users/${this.props.current_user.login}/topics/personal`;
+ }else{
+ this.props.showNotification(response.data.message)
+ }
+ }
+ }
+
+ })
+ .catch(function (error) {
+ console.log(error);
+ });
+ this.topicscancelmodel()
+ }
+ changeTab=(e)=>{
+ this.setState({
+ tab:e.key
+ })
+ console.log(e.key);
+ // if(e.key === 0){
+ //
+ // }else{
+ //
+ //
+ // }
+ }
+ render(){
+ let { banksMenu} = this.props;
+ let {visible,tab}=this.state;
+ // console.log("问卷预览");
+ // console.log(visible);
+ let user_id=this.props.current_user&&this.props.current_user.user_id;
+ let user_type=this.props.current_user&&this.props.current_user.user_identity;
+ let targetuserid=this.props.data&&this.props.data.id;
+ // console.log("_____________________________");
+ // console.log(this.props);
+ // console.log("++++++++++++++++=");
+ // console.log("banksMenubanksMenubanksMenubanksMenu");
+ // console.log(banksMenu);
+ return(
+
+ {this.state.Modalstype&&this.state.Modalstype===true?
:""}
+ {/*发送至弹窗*/}
+ {
+ visible&&visible===true?
+
this.topicscancelmodel()}
+ />:""
+ }
+ {
+ banksMenu &&
+
+
+ {
+ banksMenu.menuArray && banksMenu.menuArray.map((item,key)=>{
+ console.log("BanksMenu");
+ console.log(item);
+ return(
+ {item.content}
+ )
+ })
+ }
+
+
+ }
+
+ {
+ banksMenu===undefined?
+
+ this.deletecheckBoxValues(banksMenu&&banksMenu.id,banksMenu&&banksMenu.category)}style="blue" className="ml20 font-16">删除
+ 编辑
+ this.sendTopics()} style="blue" className="ml20 font-16">发送
+
+ :banksMenu.authorize===true?
+
+ this.deletecheckBoxValues(banksMenu&&banksMenu.id,banksMenu&&banksMenu.category)}style="blue" className="ml20 font-16">删除
+ 编辑
+ this.sendTopics()} style="blue" className="ml20 font-16">发送
+
+ :
+
+ this.sendTopics()} style="blue" className="ml20 font-16">发送
+
+ }
+
+ )
+ }
+}
+export default BanksMenu;
\ No newline at end of file
diff --git a/public/react/src/modules/user/usersInfo/publicCreatNew.js b/public/react/src/modules/user/usersInfo/publicCreatNew.js
index 0b5cee7d2..25dd4a030 100644
--- a/public/react/src/modules/user/usersInfo/publicCreatNew.js
+++ b/public/react/src/modules/user/usersInfo/publicCreatNew.js
@@ -17,7 +17,7 @@ class publicCreateNew extends Component{
render() {
let {href,name,index}=this.props;
return (
-
+
diff --git a/public/react/src/modules/user/usersInfo/usersInfo.css b/public/react/src/modules/user/usersInfo/usersInfo.css
index ab5dd6136..a0cd3ba21 100644
--- a/public/react/src/modules/user/usersInfo/usersInfo.css
+++ b/public/react/src/modules/user/usersInfo/usersInfo.css
@@ -226,4 +226,207 @@
content: '';
left:0px;
background: #4CACFF;
+}
+/* 题库相关 */
+.breadcrumb{
+ height: 18px;
+ line-height: 18px;
+ margin:10px 0px 0px;
+}
+.breadcrumb .ant-breadcrumb a:hover{
+ color: #459BE5 !important;
+}
+.breadcrumb .ant-breadcrumb-separator{
+ margin:0px 2px!important;
+}
+.breadcrumb span.ant-breadcrumb-link{
+ cursor: default;
+}
+.bank_is_public{
+ background: #84B6EB;
+ float: left;
+ height: 24px;
+ line-height: 24px;
+ padding:0px 20px;
+ color: #fff;
+ font-size: 16px;
+ margin-left: 10px;
+ border-radius:20px;
+ margin-top:3px;
+}
+.breadcrumb.ant-breadcrumb > span:last-child a{
+ color: #05101A
+}
+.breadcrumb.ant-breadcrumb a{
+ color:#999;
+}
+.bank_is_private{
+ background: #56B998;
+ float: left;
+ height: 24px;
+ line-height: 24px;
+ padding:0px 20px;
+ color: #fff;
+ font-size: 16px;
+ margin-left: 10px;
+ border-radius:20px;
+ margin-top:3px;
+}
+.topicsbox{
+ width: 1200px;
+ /*min-height: 216px;*/
+ background: rgba(255,255,255,1);
+ padding: 0px 30px 0px 40px;
+}
+
+.topicstopfont{
+ width:64px;
+ height:16px;
+ font-size:16px;
+ font-weight:400;
+ color: #666 !important;
+}
+
+.topcschild{
+ width:1128px;
+ height:55px;
+ line-height: 54px;
+ border-bottom:1px solid rgba(235,235,235,1);
+}
+
+.topcsmid{
+ width:1128px;
+ height:55px;
+ line-height: 55px;
+}
+
+.topcsactive{
+ color: #4CACFF !important;
+ cursor: pointer;
+}
+
+.topicsmidfont{
+ max-width: 56px;
+ height: 55px;
+ font-size: 14px;
+ font-weight: 400;
+ cursor: pointer;
+ line-height: 55px;
+ color: #666;
+}
+
+.alltopisc{
+ width:230px;
+ height:20px;
+ font-size:14px;
+ font-weight:400;
+ color:rgba(153,153,153,1);
+ line-height:20px;
+}
+
+.alltopiscright{
+ /* width: 141px; */
+ height: 20px;
+ font-size: 14px;
+ font-weight: 400;
+ color: rgba(153,153,153,1);
+ line-height: 20px;
+}
+
+.topicsbtn{
+ padding: 0px 15px;
+ border-radius: 2px;
+ /*color: #4C4C4C;*/
+ cursor: pointer;
+ display: inline-block;
+ /*background-color: #4CACFF!important;*/
+ /*color: #fff!important;*/
+ border-radius: 11px;
+ border: 1px solid rgba(76,172,255,1);
+ color: rgba(76,172,255,1);
+}
+
+.pd1323{
+ padding: 10px 6px 25px 40px;
+ /*cursor: pointer;*/
+}
+.pd1323:hover {
+ box-shadow: 0px 2px 6px rgba(51,51,51,0.09);
+ opacity: 1;
+ border-radius: 2px;
+}
+.topicswidth600{
+ mac-width: 600px;
+ display: inline-block;
+}
+.topicswidth300{
+ width: 300px;
+ display: inline-block;
+}
+.topiscfilterbtn{
+ font-size: 14px;
+ color: #4CACFF !important;
+ border-radius: 5px;
+ border: 1px solid #4CACFF !important;
+ line-height: 23px !important;
+}
+
+.topscisright{
+ right: 0px;
+ top: 45px;
+ display: block;
+ position: absolute;
+}
+
+.topsics135{
+ max-width: 135px;
+ display: inline-block;
+}
+
+.professional_certificationbox{
+ height:431px;
+ background:rgba(255,255,255,1);
+}
+
+.pd115200{
+ padding: 55px 200px 0px 200px;
+}
+
+.topicsItemimg{
+ width:150px;
+}
+
+.topicsItemfont{
+ font-size: 18px;
+ font-family: PingFang-SC;
+ font-weight: 400;
+ color: rgba(51,51,51,1);
+ line-height: 35px;
+}
+
+.topicsItem{
+ max-width: 1068px;
+ max-height: 115px;
+ overflow-y: auto;
+}
+
+.mb45{
+ margin-bottom: 45px!important;
+}
+
+.topsicsmax550{
+ max-width: 550px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ position: absolute;
+ top: -16px;
+}
+
+.topsicrelative{
+ position: relative;
+}
+
+.topsicinline{
+ display: inline-block;
}
\ No newline at end of file
diff --git a/public/react/src/search/searchc.css b/public/react/src/search/searchc.css
index 99f35738a..15c34650b 100644
--- a/public/react/src/search/searchc.css
+++ b/public/react/src/search/searchc.css
@@ -12,7 +12,7 @@
height: 55px;
width:663px !important;
font-size: 18px;
- color: #681616 !important;
+ /*color: #681616 !important;*/
border-color: #E1EDF8 !important;
}
diff --git a/public/stylesheets/css/edu-common.css b/public/stylesheets/css/edu-common.css
index ad1d2598c..7997c8c8d 100644
--- a/public/stylesheets/css/edu-common.css
+++ b/public/stylesheets/css/edu-common.css
@@ -9,7 +9,6 @@ body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legen
table,input,textarea,select,button { font-family: "微软雅黑","宋体"; font-size:14px;line-height:1.9; background:#f5f5f5; color:#333;}
div,img,tr,td,table{ border:0;}
table,tr,td{border:0;}
-ol,ul,li{ list-style-type:none}
a:link,a:visited{text-decoration:none;color:#898989; }
a:hover {color:#FF7500;}
a:hover.fa{color:#FF7500;}
@@ -108,6 +107,7 @@ a:hover.link-color-grey03{color:#3498db!important;}
.font-18{ font-size: 18px!important;}
.font-20{ font-size: 20px!important;}
.font-22{ font-size: 22px!important;}
+.font-25{ font-size: 25px!important;}
.font-24{ font-size: 24px!important;}
.font-28{ font-size: 28px!important;}
.font-30{ font-size: 30px!important;}
@@ -121,7 +121,7 @@ a:hover.link-color-grey03{color:#3498db!important;}
.mr3{margin-right: 3px}.mr4{margin-right: 4px}.mr5{ margin-right: 5px;}.mr8{ margin-right: 8px;}.mr10{ margin-right: 10px;}.mr12{ margin-right:12px!important;}.mr15{ margin-right: 15px;}.mr18{ margin-right: 18px;}.mr20{ margin-right: 20px;}.mr25{ margin-right: 25px;}.mr30{ margin-right:30px;}.mr35{margin-right:35px;}.mr40{margin-right:40px;}.mr45{margin-right:45px;}.mr50{ margin-right: 50px;}.mr60{ margin-right:60px;}.mr350{ margin-right:350px;}.pt5{ padding-top:5px;}.pt10{ padding-top:10px;}.pt15{ padding-top:15px;}.pt20{ padding-top:20px;}.pt30{ padding-top:30px;}.pt40{ padding-top:40px;}.pt47{ padding-top:47px;}.pt100{padding-top:100px;}.pt130{padding-top:130px;}
.pt1{ padding-top:1px;}.pt5{ padding-top:5px;}.pt10{ padding-top:10px;}.pt15{ padding-top:15px;}.pt20{ padding-top:20px;}.pt30{ padding-top:30px;}.pt40{ padding-top:40px;}
-.pb5{ padding-bottom:5px;}.pb10{ padding-bottom:10px;}.pb15{ padding-bottom:15px;}.pb20{ padding-bottom:20px;}.pb30{ padding-bottom:30px;}.pb40{ padding-bottom:40px;}.pb47{ padding-bottom:47px;}.pb50{ padding-bottom:50px;}.pb155{ padding-bottom:155px;}
+.pb5{ padding-bottom:5px;}.pb10{ padding-bottom:10px;}.pb15{ padding-bottom:15px;}.pb20{ padding-bottom:20px;}.pb28{ padding-bottom:28px;}.pb30{ padding-bottom:30px;}.pb40{ padding-bottom:40px;}.pb47{ padding-bottom:47px;}.pb50{ padding-bottom:50px;}.pb155{ padding-bottom:155px;}
.pl2{ padding-left:2px;}.pl5{ padding-left:5px;}.pl8{ padding-left:8px;}.pl10{ padding-left:10px;}.pl15{ padding-left:15px;}.pl20{ padding-left:20px;}.pl28{ padding-left:28px;}.pl30{ padding-left:30px;}.pl33{padding-left: 33px}.pl40{ padding-left:40px;}.pl42{ padding-left:42px;}.pl45{ padding-left:45px;}.pl50{ padding-left:50px;}.pl100{ padding-left:100px;}.pl35{ padding-left:35px;}.pl50{padding-left:50px;}.pl70{padding-left:70px;}.pl80{padding-left:80px;}.pl92{padding-left:92px;}
.pr2{ paddding-right:2px;}.pr5{ padding-right:5px;}.pr10{ padding-right:10px;}.pr15{ padding-right:15px;}.pr20{ padding-right:20px!important;}.pr30{ padding-right:30px!important;}.pr42{ padding-right:42px;}.pr45{ padding-right:45px;}
diff --git a/public/stylesheets/educoder/edu-all.css b/public/stylesheets/educoder/edu-all.css
index 022030757..91ea604d4 100644
--- a/public/stylesheets/educoder/edu-all.css
+++ b/public/stylesheets/educoder/edu-all.css
@@ -3761,8 +3761,3 @@ a.singlepublishtwo{
/*width: auto !important;*/
/*max-width: 600px !important;*/
/*}*/
-.topicsItem{
- max-width: 1138px;
- max-height: 110px;
- overflow-y: auto;
-}
\ No newline at end of file
diff --git a/public/stylesheets/educoder/edu-main.css b/public/stylesheets/educoder/edu-main.css
index 62e052104..f283c1e95 100644
--- a/public/stylesheets/educoder/edu-main.css
+++ b/public/stylesheets/educoder/edu-main.css
@@ -82,6 +82,7 @@ a.edu-txt-w80,.edu-txt-w80{ width:80px; display: inline-block;text-align: center
.font-18{ font-size: 18px!important;}
.font-20{ font-size: 20px!important;}
.font-22{ font-size: 22px!important;}
+.font-25{ font-size: 25px!important;}
.font-24{ font-size: 24px!important;}
.font-26{ font-size: 26px!important;}
.font-28{ font-size: 28px!important;}
@@ -103,15 +104,17 @@ a.decoration{text-decoration: underline}
.mb0{margin-bottom: 0px!important;}.mb3{ margin-bottom: 3px;}.mb5{ margin-bottom: 5px;}.mb7{ margin-bottom: 7px;}.mb10{ margin-bottom: 10px;}.mb11{ margin-bottom: 11px;}.mb14{ margin-bottom: 14px;}.mb15{ margin-bottom: 15px;}.mb16{ margin-bottom: 16px;}.mb20{ margin-bottom: 20px!important;}.mb25{ margin-bottom: 25px;}.mb26{ margin-bottom: 26px;}.mb28{ margin-bottom: 28px;}.mb30{ margin-bottom: 30px!important;}.mb40{ margin-bottom: 40px!important;}.mb50{ margin-bottom: 50px!important;}.mb60{ margin-bottom: 60px!important;}.mb70{ margin-bottom: 70px!important;}.mb80{ margin-bottom: 80px!important;}.mb90{ margin-bottom: 90px!important;}.mb100{ margin-bottom: 100px!important;}.mb110{ margin-bottom: 110px;}
.ml-3{ margin-left: -3px;}.ml1{margin-left: 1px;}.ml2{margin-left: 2px;}.ml3{margin-left: 3px;}.ml4{margin-left: 4px;}.ml5{ margin-left: 5px;}.ml6{ margin-left: 6px;}.ml10{ margin-left: 10px;}.ml12{ margin-left:12px!important;}.ml13{ margin-left:13px!important;}.ml15{ margin-left: 15px;}.ml18{ margin-left: 18px;}.ml20{ margin-left: 20px;}.ml22{ margin-left: 22px;}.ml25{ margin-left: 25px;}.ml29{margin-left: 29px;}.ml30{ margin-left: 30px;}.ml33{ margin-left: 33px;}.ml35{ margin-left:35px;}.ml40{margin-left:40px;}.ml42{margin-left:42px;}.ml45{ margin-left: 45px;}.ml50{ margin-left: 50px;}.ml55{ margin-left: 55px;}.ml60{ margin-left: 60px;}.ml72{ margin-left: 72px;}.ml73{ margin-left: 73px;}.ml75{ margin-left: 75px;}.ml80{ margin-left: 80px;}.ml85{margin-left:85px;}.ml95{ margin-left: 95px;}.ml115{margin-left: 115px}.ml123{ margin-left: 123px;}.ml150{ margin-left: 150px;}.ml180{ margin-left: 180px;}.ml230{ margin-left: 230px;}.ml240{margin-left: 240px;}.ml250{ margin-left: 250px;}.ml290{ margin-left: 290px;}
.mr3{margin-right: 3px}.mr4{margin-right: 4px}.mr5{ margin-right: 5px;}.mr8{ margin-right: 8px;}.mr10{ margin-right: 10px;}.mr12{ margin-right:12px!important;}.mr15{ margin-right: 15px;}.mr18{ margin-right: 18px;}.mr20{ margin-right: 20px;}.mr24{ margin-right: 24px;}.mr25{ margin-right: 25px;}.mr30{ margin-right:30px;}.mr35{margin-right:35px;}.mr40{margin-right:40px;}.mr45{margin-right:45px;}.mr50{ margin-right: 50px;}.mr60{ margin-right:60px;}.mr70{ margin-right: 70px;}.mr75{ margin-right: 75px;}.mr80{ margin-right:80px;}.mr90{ margin-right:90px;}.mr100{ margin-right: 100px;}.mr110{ margin-right:110px;}.mr350{ margin-right:350px;}
+.ml61{
+ margin-left: 61px;
+}
.pt1{ padding-top:1px;}.pt3{ padding-top:3px!important;}.pt5{ padding-top:5px!important;}.pt10{ padding-top:10px;}.pt15{ padding-top:15px;}.pt17{ padding-top:17px;}.pt20{ padding-top:20px!important;}.pt25{ padding-top:25px;}.pt30{ padding-top:30px;}.pt35{ padding-top:35px;}.pt37{ padding-top:37px;}.pt40{ padding-top:40px;}.pt47{ padding-top:47px;}.pt49{ padding-top:49px;}.pt50{ padding-top:50px;}.pt60{ padding-top:60px;}.pt70{ padding-top:70px;}.pt80{ padding-top:80px;}.pt90{ padding-top:90px;}.pt100{padding-top:100px;}.pt110{ padding-top:110px;}.pt120{ padding-top:120px;}.pt130{padding-top:130px;}
-.pb3{ padding-bottom:3px!important;}.pb5{ padding-bottom:5px!important;}.pb10{ padding-bottom:10px;}.pb15{ padding-bottom:15px;}.pb20{ padding-bottom:20px;}.pb25{ padding-bottom:20px;}.pb25{ padding-bottom:20px;}.pb30{ padding-bottom:30px;}.pb35{ padding-bottom:35px;}.pb40{ padding-bottom:40px;}.pb47{ padding-bottom:47px;}.pb50{ padding-bottom:50px;}.pb60{ padding-bottom:60px;}.pb70{ padding-bottom:70px;}.pb80{ padding-bottom:80px;}.pb90{ padding-bottom:90px;}.pb100{ padding-bottom:100px;}.pb110{ padding-bottom:110px;}.pb155{ padding-bottom:155px;}
+.pb3{ padding-bottom:3px!important;}.pb5{ padding-bottom:5px!important;}.pb10{ padding-bottom:10px;}.pb15{ padding-bottom:15px;}.pb20{ padding-bottom:20px;}.pb25{ padding-bottom:20px;}.pb25{ padding-bottom:20px;}.pb28{ padding-bottom:28px;}.pb30{ padding-bottom:30px;}.pb35{ padding-bottom:35px;}.pb40{ padding-bottom:40px;}.pb47{ padding-bottom:47px;}.pb50{ padding-bottom:50px;}.pb60{ padding-bottom:60px;}.pb70{ padding-bottom:70px;}.pb80{ padding-bottom:80px;}.pb90{ padding-bottom:90px;}.pb100{ padding-bottom:100px;}.pb110{ padding-bottom:110px;}.pb155{ padding-bottom:155px;}
.pr2{ paddding-right:2px;}.pr5{ padding-right:5px;}.pr10{ padding-right:10px;}.pr15{ padding-right:15px;}.pr20{ padding-right:20px!important;}.pr30{ padding-right:30px!important;}.pr35{ padding-right:35px!important;}.pr42{ padding-right:42px;}.pr45{ padding-right:45px;}.pr48{ padding-right:48px;}.pr57{ padding-right:57px;}.pr60{ padding-right:60px;}.pr70{ padding-right:70px;}.pr72{ padding-right:72px;}.pr75{ padding-right:75px;}.pr88{ padding-right:88px;}
.pl0{ padding-left:0px!important;}.pl2{ padding-left:2px;}.pl5{ padding-left:5px;}.pl7{ padding-left:7px;}.pl8{ padding-left:8px;}.pl10{ padding-left:10px;}.pl15{ padding-left:15px;}.pl20{ padding-left:20px;}.pl22{ padding-left:22px;}.pl25{ padding-left:25px;}.pl28{ padding-left:28px;}.pl30{ padding-left:30px !important;}.pl33{padding-left: 33px}.pl35{ padding-left:35px;}.pl40{ padding-left:40px;}.pl42{ padding-left:42px;}.pl45{ padding-left:45px;}.pl50{ padding-left:50px;}.pl60{ padding-left:60px;}.pl70{padding-left:70px;}.pl75{padding-left:75px;}.pl80{padding-left:80px;}.pl88{ padding-left:88px;}.pl92{padding-left:92px;}.pl100{ padding-left:100px;}
.pr2{ paddding-right:2px;}.pr5{ padding-right:5px;}.pr7{ padding-right:7px;}.pr10{ padding-right:10px;}.pr15{ padding-right:15px;}.pr20{ padding-right:20px!important;}.pr25{ padding-right:25px!important;}.pr30{ padding-right:30px!important;}.pr40{ padding-right:40px;}.pr42{ padding-right:42px;}.pr45{ padding-right:45px;}.pr60{padding-right:60px;}.pr75{padding-right:75px;}
-
.padding5-10{padding:5px 10px;box-sizing: border-box}
.padding5-20{padding:5px 20px;box-sizing: border-box}
.padding10{padding: 10px;box-sizing: border-box}
@@ -170,7 +173,7 @@ input::-ms-clear{display:none;}
.newContainer{ min-height:100%; height: auto !important; height: 100%; /*IE6不识别min-height*/position: relative;}
.educontent{width: 1200px;margin:0px auto;box-sizing: border-box}/*中间部分宽度固定为1200*/
.newMain{ margin: 0 auto; padding-bottom: 235px; min-width:1200px;}/*padding-bottom根据底部的高度而定*/
-.newMain{ padding-bottom: 120px !important; }
+.newMain{ padding-bottom: 124px !important; }
/*高度*/
.height-100{height: 100%;}
@@ -579,9 +582,10 @@ a.user_greybg_btn{background-color:#747A7F;color: #fff;}
/*md编辑器恢复被覆盖样式*/
-.new_li li{ list-style-type: disc!important; }
-.new_li ol li{ list-style-type: decimal!important; }
+.new_li .markdown-body ul > li,.markdown-body ul > li{ list-style-type: disc!important;margin-bottom: 0!important; }
+.new_li .markdown-body ol > li,.markdown-body ol > li{ list-style-type: decimal!important; }
.new_li li{ margin-bottom: 0!important; }
+.markdown-body p{font-size: 16px!important;}
/*搜索框*/
#pollingPanel{position: relative;width: 248px;height: 32px;}
diff --git a/spec/controllers/course_stages_controller_spec.rb b/spec/controllers/course_stages_controller_spec.rb
new file mode 100644
index 000000000..e63a2b6a4
--- /dev/null
+++ b/spec/controllers/course_stages_controller_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe CourseStagesController, type: :controller do
+
+end
diff --git a/spec/helpers/course_stages_helper_spec.rb b/spec/helpers/course_stages_helper_spec.rb
new file mode 100644
index 000000000..bb1081a10
--- /dev/null
+++ b/spec/helpers/course_stages_helper_spec.rb
@@ -0,0 +1,15 @@
+require 'rails_helper'
+
+# Specs in this file have access to a helper object that includes
+# the CourseStagesHelper. For example:
+#
+# describe CourseStagesHelper do
+# describe "string concat" do
+# it "concats two strings with spaces" do
+# expect(helper.concat_strings("this","that")).to eq("this that")
+# end
+# end
+# end
+RSpec.describe CourseStagesHelper, type: :helper do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/models/course_stage_shixun_spec.rb b/spec/models/course_stage_shixun_spec.rb
new file mode 100644
index 000000000..716af2b97
--- /dev/null
+++ b/spec/models/course_stage_shixun_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe CourseStageShixun, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/models/course_stage_spec.rb b/spec/models/course_stage_spec.rb
new file mode 100644
index 000000000..06c711908
--- /dev/null
+++ b/spec/models/course_stage_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe CourseStage, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/models/shixun_secret_repository_spec.rb b/spec/models/shixun_secret_repository_spec.rb
new file mode 100644
index 000000000..ed06914ae
--- /dev/null
+++ b/spec/models/shixun_secret_repository_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe ShixunSecretRepository, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/models/shixun_work_comment_spec.rb b/spec/models/shixun_work_comment_spec.rb
new file mode 100644
index 000000000..0c40ede68
--- /dev/null
+++ b/spec/models/shixun_work_comment_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe ShixunWorkComment, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/vendor/assets/dragula/dragula.css b/vendor/assets/dragula/dragula.css
new file mode 100755
index 000000000..b18c16e7e
--- /dev/null
+++ b/vendor/assets/dragula/dragula.css
@@ -0,0 +1,22 @@
+.gu-mirror {
+ position: fixed !important;
+ margin: 0 !important;
+ z-index: 9999 !important;
+ opacity: 0.8;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
+ filter: alpha(opacity=80);
+}
+.gu-hide {
+ display: none !important;
+}
+.gu-unselectable {
+ -webkit-user-select: none !important;
+ -moz-user-select: none !important;
+ -ms-user-select: none !important;
+ user-select: none !important;
+}
+.gu-transit {
+ opacity: 0.2;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
+ filter: alpha(opacity=20);
+}
diff --git a/vendor/assets/dragula/dragula.js b/vendor/assets/dragula/dragula.js
new file mode 100755
index 000000000..67b93812e
--- /dev/null
+++ b/vendor/assets/dragula/dragula.js
@@ -0,0 +1,908 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.dragula = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o
0 ? revert : o.revertOnSpill;
+ var item = _copy || _item;
+ var parent = getParent(item);
+ var initial = isInitialPlacement(parent);
+ if (initial === false && reverts) {
+ if (_copy) {
+ if (parent) {
+ parent.removeChild(_copy);
+ }
+ } else {
+ _source.insertBefore(item, _initialSibling);
+ }
+ }
+ if (initial || reverts) {
+ drake.emit('cancel', item, _source, _source);
+ } else {
+ drake.emit('drop', item, parent, _source, _currentSibling);
+ }
+ cleanup();
+ }
+
+ function cleanup () {
+ var item = _copy || _item;
+ ungrab();
+ removeMirrorImage();
+ if (item) {
+ classes.rm(item, 'gu-transit');
+ }
+ if (_renderTimer) {
+ clearTimeout(_renderTimer);
+ }
+ drake.dragging = false;
+ if (_lastDropTarget) {
+ drake.emit('out', item, _lastDropTarget, _source);
+ }
+ drake.emit('dragend', item);
+ _source = _item = _copy = _initialSibling = _currentSibling = _renderTimer = _lastDropTarget = null;
+ }
+
+ function isInitialPlacement (target, s) {
+ var sibling;
+ if (s !== void 0) {
+ sibling = s;
+ } else if (_mirror) {
+ sibling = _currentSibling;
+ } else {
+ sibling = nextEl(_copy || _item);
+ }
+ return target === _source && sibling === _initialSibling;
+ }
+
+ function findDropTarget (elementBehindCursor, clientX, clientY) {
+ var target = elementBehindCursor;
+ while (target && !accepted()) {
+ target = getParent(target);
+ }
+ return target;
+
+ function accepted () {
+ var droppable = isContainer(target);
+ if (droppable === false) {
+ return false;
+ }
+
+ var immediate = getImmediateChild(target, elementBehindCursor);
+ var reference = getReference(target, immediate, clientX, clientY);
+ var initial = isInitialPlacement(target, reference);
+ if (initial) {
+ return true; // should always be able to drop it right back where it was
+ }
+ return o.accepts(_item, target, _source, reference);
+ }
+ }
+
+ function drag (e) {
+ if (!_mirror) {
+ return;
+ }
+ e.preventDefault();
+
+ var clientX = getCoord('clientX', e);
+ var clientY = getCoord('clientY', e);
+ var x = clientX - _offsetX;
+ var y = clientY - _offsetY;
+
+ _mirror.style.left = x + 'px';
+ _mirror.style.top = y + 'px';
+
+ var item = _copy || _item;
+ var elementBehindCursor = getElementBehindPoint(_mirror, clientX, clientY);
+ var dropTarget = findDropTarget(elementBehindCursor, clientX, clientY);
+ var changed = dropTarget !== null && dropTarget !== _lastDropTarget;
+ if (changed || dropTarget === null) {
+ out();
+ _lastDropTarget = dropTarget;
+ over();
+ }
+ var parent = getParent(item);
+ if (dropTarget === _source && _copy && !o.copySortSource) {
+ if (parent) {
+ parent.removeChild(item);
+ }
+ return;
+ }
+ var reference;
+ var immediate = getImmediateChild(dropTarget, elementBehindCursor);
+ if (immediate !== null) {
+ reference = getReference(dropTarget, immediate, clientX, clientY);
+ } else if (o.revertOnSpill === true && !_copy) {
+ reference = _initialSibling;
+ dropTarget = _source;
+ } else {
+ if (_copy && parent) {
+ parent.removeChild(item);
+ }
+ return;
+ }
+ if (
+ (reference === null && changed) ||
+ reference !== item &&
+ reference !== nextEl(item)
+ ) {
+ _currentSibling = reference;
+ dropTarget.insertBefore(item, reference);
+ drake.emit('shadow', item, dropTarget, _source);
+ }
+ function moved (type) { drake.emit(type, item, _lastDropTarget, _source); }
+ function over () { if (changed) { moved('over'); } }
+ function out () { if (_lastDropTarget) { moved('out'); } }
+ }
+
+ function spillOver (el) {
+ classes.rm(el, 'gu-hide');
+ }
+
+ function spillOut (el) {
+ if (drake.dragging) { classes.add(el, 'gu-hide'); }
+ }
+
+ function renderMirrorImage () {
+ if (_mirror) {
+ return;
+ }
+ var rect = _item.getBoundingClientRect();
+ _mirror = _item.cloneNode(true);
+ _mirror.style.width = getRectWidth(rect) + 'px';
+ _mirror.style.height = getRectHeight(rect) + 'px';
+ classes.rm(_mirror, 'gu-transit');
+ classes.add(_mirror, 'gu-mirror');
+ o.mirrorContainer.appendChild(_mirror);
+ touchy(documentElement, 'add', 'mousemove', drag);
+ classes.add(o.mirrorContainer, 'gu-unselectable');
+ drake.emit('cloned', _mirror, _item, 'mirror');
+ }
+
+ function removeMirrorImage () {
+ if (_mirror) {
+ classes.rm(o.mirrorContainer, 'gu-unselectable');
+ touchy(documentElement, 'remove', 'mousemove', drag);
+ getParent(_mirror).removeChild(_mirror);
+ _mirror = null;
+ }
+ }
+
+ function getImmediateChild (dropTarget, target) {
+ var immediate = target;
+ while (immediate !== dropTarget && getParent(immediate) !== dropTarget) {
+ immediate = getParent(immediate);
+ }
+ if (immediate === documentElement) {
+ return null;
+ }
+ return immediate;
+ }
+
+ function getReference (dropTarget, target, x, y) {
+ var horizontal = o.direction === 'horizontal';
+ var reference = target !== dropTarget ? inside() : outside();
+ return reference;
+
+ function outside () { // slower, but able to figure out any position
+ var len = dropTarget.children.length;
+ var i;
+ var el;
+ var rect;
+ for (i = 0; i < len; i++) {
+ el = dropTarget.children[i];
+ rect = el.getBoundingClientRect();
+ if (horizontal && (rect.left + rect.width / 2) > x) { return el; }
+ if (!horizontal && (rect.top + rect.height / 2) > y) { return el; }
+ }
+ return null;
+ }
+
+ function inside () { // faster, but only available if dropped inside a child element
+ var rect = target.getBoundingClientRect();
+ if (horizontal) {
+ return resolve(x > rect.left + getRectWidth(rect) / 2);
+ }
+ return resolve(y > rect.top + getRectHeight(rect) / 2);
+ }
+
+ function resolve (after) {
+ return after ? nextEl(target) : target;
+ }
+ }
+
+ function isCopy (item, container) {
+ return typeof o.copy === 'boolean' ? o.copy : o.copy(item, container);
+ }
+}
+
+function touchy (el, op, type, fn) {
+ var touch = {
+ mouseup: 'touchend',
+ mousedown: 'touchstart',
+ mousemove: 'touchmove'
+ };
+ var pointers = {
+ mouseup: 'pointerup',
+ mousedown: 'pointerdown',
+ mousemove: 'pointermove'
+ };
+ var microsoft = {
+ mouseup: 'MSPointerUp',
+ mousedown: 'MSPointerDown',
+ mousemove: 'MSPointerMove'
+ };
+ if (global.navigator.pointerEnabled) {
+ crossvent[op](el, pointers[type], fn);
+ } else if (global.navigator.msPointerEnabled) {
+ crossvent[op](el, microsoft[type], fn);
+ } else {
+ crossvent[op](el, touch[type], fn);
+ crossvent[op](el, type, fn);
+ }
+}
+
+function whichMouseButton (e) {
+ if (e.touches !== void 0) { return e.touches.length; }
+ if (e.which !== void 0 && e.which !== 0) { return e.which; } // see https://github.com/bevacqua/dragula/issues/261
+ if (e.buttons !== void 0) { return e.buttons; }
+ var button = e.button;
+ if (button !== void 0) { // see https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/event.js#L573-L575
+ return button & 1 ? 1 : button & 2 ? 3 : (button & 4 ? 2 : 0);
+ }
+}
+
+function getOffset (el) {
+ var rect = el.getBoundingClientRect();
+ return {
+ left: rect.left + getScroll('scrollLeft', 'pageXOffset'),
+ top: rect.top + getScroll('scrollTop', 'pageYOffset')
+ };
+}
+
+function getScroll (scrollProp, offsetProp) {
+ if (typeof global[offsetProp] !== 'undefined') {
+ return global[offsetProp];
+ }
+ if (documentElement.clientHeight) {
+ return documentElement[scrollProp];
+ }
+ return doc.body[scrollProp];
+}
+
+function getElementBehindPoint (point, x, y) {
+ var p = point || {};
+ var state = p.className;
+ var el;
+ p.className += ' gu-hide';
+ el = doc.elementFromPoint(x, y);
+ p.className = state;
+ return el;
+}
+
+function never () { return false; }
+function always () { return true; }
+function getRectWidth (rect) { return rect.width || (rect.right - rect.left); }
+function getRectHeight (rect) { return rect.height || (rect.bottom - rect.top); }
+function getParent (el) { return el.parentNode === doc ? null : el.parentNode; }
+function isInput (el) { return el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT' || isEditable(el); }
+function isEditable (el) {
+ if (!el) { return false; } // no parents were editable
+ if (el.contentEditable === 'false') { return false; } // stop the lookup
+ if (el.contentEditable === 'true') { return true; } // found a contentEditable element in the chain
+ return isEditable(getParent(el)); // contentEditable is set to 'inherit'
+}
+
+function nextEl (el) {
+ return el.nextElementSibling || manually();
+ function manually () {
+ var sibling = el;
+ do {
+ sibling = sibling.nextSibling;
+ } while (sibling && sibling.nodeType !== 1);
+ return sibling;
+ }
+}
+
+function getEventHost (e) {
+ // on touchend event, we have to use `e.changedTouches`
+ // see http://stackoverflow.com/questions/7192563/touchend-event-properties
+ // see https://github.com/bevacqua/dragula/issues/34
+ if (e.targetTouches && e.targetTouches.length) {
+ return e.targetTouches[0];
+ }
+ if (e.changedTouches && e.changedTouches.length) {
+ return e.changedTouches[0];
+ }
+ return e;
+}
+
+function getCoord (coord, e) {
+ var host = getEventHost(e);
+ var missMap = {
+ pageX: 'clientX', // IE8
+ pageY: 'clientY' // IE8
+ };
+ if (coord in missMap && !(coord in host) && missMap[coord] in host) {
+ coord = missMap[coord];
+ }
+ return host[coord];
+}
+
+module.exports = dragula;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"./classes":1,"contra/emitter":5,"crossvent":6}],3:[function(require,module,exports){
+module.exports = function atoa (a, n) { return Array.prototype.slice.call(a, n); }
+
+},{}],4:[function(require,module,exports){
+'use strict';
+
+var ticky = require('ticky');
+
+module.exports = function debounce (fn, args, ctx) {
+ if (!fn) { return; }
+ ticky(function run () {
+ fn.apply(ctx || null, args || []);
+ });
+};
+
+},{"ticky":9}],5:[function(require,module,exports){
+'use strict';
+
+var atoa = require('atoa');
+var debounce = require('./debounce');
+
+module.exports = function emitter (thing, options) {
+ var opts = options || {};
+ var evt = {};
+ if (thing === undefined) { thing = {}; }
+ thing.on = function (type, fn) {
+ if (!evt[type]) {
+ evt[type] = [fn];
+ } else {
+ evt[type].push(fn);
+ }
+ return thing;
+ };
+ thing.once = function (type, fn) {
+ fn._once = true; // thing.off(fn) still works!
+ thing.on(type, fn);
+ return thing;
+ };
+ thing.off = function (type, fn) {
+ var c = arguments.length;
+ if (c === 1) {
+ delete evt[type];
+ } else if (c === 0) {
+ evt = {};
+ } else {
+ var et = evt[type];
+ if (!et) { return thing; }
+ et.splice(et.indexOf(fn), 1);
+ }
+ return thing;
+ };
+ thing.emit = function () {
+ var args = atoa(arguments);
+ return thing.emitterSnapshot(args.shift()).apply(this, args);
+ };
+ thing.emitterSnapshot = function (type) {
+ var et = (evt[type] || []).slice(0);
+ return function () {
+ var args = atoa(arguments);
+ var ctx = this || thing;
+ if (type === 'error' && opts.throws !== false && !et.length) { throw args.length === 1 ? args[0] : args; }
+ et.forEach(function emitter (listen) {
+ if (opts.async) { debounce(listen, args, ctx); } else { listen.apply(ctx, args); }
+ if (listen._once) { thing.off(type, listen); }
+ });
+ return thing;
+ };
+ };
+ return thing;
+};
+
+},{"./debounce":4,"atoa":3}],6:[function(require,module,exports){
+(function (global){
+'use strict';
+
+var customEvent = require('custom-event');
+var eventmap = require('./eventmap');
+var doc = global.document;
+var addEvent = addEventEasy;
+var removeEvent = removeEventEasy;
+var hardCache = [];
+
+if (!global.addEventListener) {
+ addEvent = addEventHard;
+ removeEvent = removeEventHard;
+}
+
+module.exports = {
+ add: addEvent,
+ remove: removeEvent,
+ fabricate: fabricateEvent
+};
+
+function addEventEasy (el, type, fn, capturing) {
+ return el.addEventListener(type, fn, capturing);
+}
+
+function addEventHard (el, type, fn) {
+ return el.attachEvent('on' + type, wrap(el, type, fn));
+}
+
+function removeEventEasy (el, type, fn, capturing) {
+ return el.removeEventListener(type, fn, capturing);
+}
+
+function removeEventHard (el, type, fn) {
+ var listener = unwrap(el, type, fn);
+ if (listener) {
+ return el.detachEvent('on' + type, listener);
+ }
+}
+
+function fabricateEvent (el, type, model) {
+ var e = eventmap.indexOf(type) === -1 ? makeCustomEvent() : makeClassicEvent();
+ if (el.dispatchEvent) {
+ el.dispatchEvent(e);
+ } else {
+ el.fireEvent('on' + type, e);
+ }
+ function makeClassicEvent () {
+ var e;
+ if (doc.createEvent) {
+ e = doc.createEvent('Event');
+ e.initEvent(type, true, true);
+ } else if (doc.createEventObject) {
+ e = doc.createEventObject();
+ }
+ return e;
+ }
+ function makeCustomEvent () {
+ return new customEvent(type, { detail: model });
+ }
+}
+
+function wrapperFactory (el, type, fn) {
+ return function wrapper (originalEvent) {
+ var e = originalEvent || global.event;
+ e.target = e.target || e.srcElement;
+ e.preventDefault = e.preventDefault || function preventDefault () { e.returnValue = false; };
+ e.stopPropagation = e.stopPropagation || function stopPropagation () { e.cancelBubble = true; };
+ e.which = e.which || e.keyCode;
+ fn.call(el, e);
+ };
+}
+
+function wrap (el, type, fn) {
+ var wrapper = unwrap(el, type, fn) || wrapperFactory(el, type, fn);
+ hardCache.push({
+ wrapper: wrapper,
+ element: el,
+ type: type,
+ fn: fn
+ });
+ return wrapper;
+}
+
+function unwrap (el, type, fn) {
+ var i = find(el, type, fn);
+ if (i) {
+ var wrapper = hardCache[i].wrapper;
+ hardCache.splice(i, 1); // free up a tad of memory
+ return wrapper;
+ }
+}
+
+function find (el, type, fn) {
+ var i, item;
+ for (i = 0; i < hardCache.length; i++) {
+ item = hardCache[i];
+ if (item.element === el && item.type === type && item.fn === fn) {
+ return i;
+ }
+ }
+}
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"./eventmap":7,"custom-event":8}],7:[function(require,module,exports){
+(function (global){
+'use strict';
+
+var eventmap = [];
+var eventname = '';
+var ron = /^on/;
+
+for (eventname in global) {
+ if (ron.test(eventname)) {
+ eventmap.push(eventname.slice(2));
+ }
+}
+
+module.exports = eventmap;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{}],8:[function(require,module,exports){
+(function (global){
+
+var NativeCustomEvent = global.CustomEvent;
+
+function useNative () {
+ try {
+ var p = new NativeCustomEvent('cat', { detail: { foo: 'bar' } });
+ return 'cat' === p.type && 'bar' === p.detail.foo;
+ } catch (e) {
+ }
+ return false;
+}
+
+/**
+ * Cross-browser `CustomEvent` constructor.
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent.CustomEvent
+ *
+ * @public
+ */
+
+module.exports = useNative() ? NativeCustomEvent :
+
+// IE >= 9
+'function' === typeof document.createEvent ? function CustomEvent (type, params) {
+ var e = document.createEvent('CustomEvent');
+ if (params) {
+ e.initCustomEvent(type, params.bubbles, params.cancelable, params.detail);
+ } else {
+ e.initCustomEvent(type, false, false, void 0);
+ }
+ return e;
+} :
+
+// IE <= 8
+function CustomEvent (type, params) {
+ var e = document.createEventObject();
+ e.type = type;
+ if (params) {
+ e.bubbles = Boolean(params.bubbles);
+ e.cancelable = Boolean(params.cancelable);
+ e.detail = params.detail;
+ } else {
+ e.bubbles = false;
+ e.cancelable = false;
+ e.detail = void 0;
+ }
+ return e;
+}
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{}],9:[function(require,module,exports){
+var si = typeof setImmediate === 'function', tick;
+if (si) {
+ tick = function (fn) { setImmediate(fn); };
+} else {
+ tick = function (fn) { setTimeout(fn, 0); };
+}
+
+module.exports = tick;
+},{}]},{},[2])(2)
+});
+//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJjbGFzc2VzLmpzIiwiZHJhZ3VsYS5qcyIsIm5vZGVfbW9kdWxlcy9hdG9hL2F0b2EuanMiLCJub2RlX21vZHVsZXMvY29udHJhL2RlYm91bmNlLmpzIiwibm9kZV9tb2R1bGVzL2NvbnRyYS9lbWl0dGVyLmpzIiwibm9kZV9tb2R1bGVzL2Nyb3NzdmVudC9zcmMvY3Jvc3N2ZW50LmpzIiwibm9kZV9tb2R1bGVzL2Nyb3NzdmVudC9zcmMvZXZlbnRtYXAuanMiLCJub2RlX21vZHVsZXMvY3VzdG9tLWV2ZW50L2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL3RpY2t5L3RpY2t5LWJyb3dzZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDakNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7OztBQ2htQkE7QUFDQTs7QUNEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUN0REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQ3JHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQ2JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDaERBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgY2FjaGUgPSB7fTtcbnZhciBzdGFydCA9ICcoPzpefFxcXFxzKSc7XG52YXIgZW5kID0gJyg/OlxcXFxzfCQpJztcblxuZnVuY3Rpb24gbG9va3VwQ2xhc3MgKGNsYXNzTmFtZSkge1xuICB2YXIgY2FjaGVkID0gY2FjaGVbY2xhc3NOYW1lXTtcbiAgaWYgKGNhY2hlZCkge1xuICAgIGNhY2hlZC5sYXN0SW5kZXggPSAwO1xuICB9IGVsc2Uge1xuICAgIGNhY2hlW2NsYXNzTmFtZV0gPSBjYWNoZWQgPSBuZXcgUmVnRXhwKHN0YXJ0ICsgY2xhc3NOYW1lICsgZW5kLCAnZycpO1xuICB9XG4gIHJldHVybiBjYWNoZWQ7XG59XG5cbmZ1bmN0aW9uIGFkZENsYXNzIChlbCwgY2xhc3NOYW1lKSB7XG4gIHZhciBjdXJyZW50ID0gZWwuY2xhc3NOYW1lO1xuICBpZiAoIWN1cnJlbnQubGVuZ3RoKSB7XG4gICAgZWwuY2xhc3NOYW1lID0gY2xhc3NOYW1lO1xuICB9IGVsc2UgaWYgKCFsb29rdXBDbGFzcyhjbGFzc05hbWUpLnRlc3QoY3VycmVudCkpIHtcbiAgICBlbC5jbGFzc05hbWUgKz0gJyAnICsgY2xhc3NOYW1lO1xuICB9XG59XG5cbmZ1bmN0aW9uIHJtQ2xhc3MgKGVsLCBjbGFzc05hbWUpIHtcbiAgZWwuY2xhc3NOYW1lID0gZWwuY2xhc3NOYW1lLnJlcGxhY2UobG9va3VwQ2xhc3MoY2xhc3NOYW1lKSwgJyAnKS50cmltKCk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBhZGQ6IGFkZENsYXNzLFxuICBybTogcm1DbGFzc1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGVtaXR0ZXIgPSByZXF1aXJlKCdjb250cmEvZW1pdHRlcicpO1xudmFyIGNyb3NzdmVudCA9IHJlcXVpcmUoJ2Nyb3NzdmVudCcpO1xudmFyIGNsYXNzZXMgPSByZXF1aXJlKCcuL2NsYXNzZXMnKTtcbnZhciBkb2MgPSBkb2N1bWVudDtcbnZhciBkb2N1bWVudEVsZW1lbnQgPSBkb2MuZG9jdW1lbnRFbGVtZW50O1xuXG5mdW5jdGlvbiBkcmFndWxhIChpbml0aWFsQ29udGFpbmVycywgb3B0aW9ucykge1xuICB2YXIgbGVuID0gYXJndW1lbnRzLmxlbmd0aDtcbiAgaWYgKGxlbiA9PT0gMSAmJiBBcnJheS5pc0FycmF5KGluaXRpYWxDb250YWluZXJzKSA9PT0gZmFsc2UpIHtcbiAgICBvcHRpb25zID0gaW5pdGlhbENvbnRhaW5lcnM7XG4gICAgaW5pdGlhbENvbnRhaW5lcnMgPSBbXTtcbiAgfVxuICB2YXIgX21pcnJvcjsgLy8gbWlycm9yIGltYWdlXG4gIHZhciBfc291cmNlOyAvLyBzb3VyY2UgY29udGFpbmVyXG4gIHZhciBfaXRlbTsgLy8gaXRlbSBiZWluZyBkcmFnZ2VkXG4gIHZhciBfb2Zmc2V0WDsgLy8gcmVmZXJlbmNlIHhcbiAgdmFyIF9vZmZzZXRZOyAvLyByZWZlcmVuY2UgeVxuICB2YXIgX21vdmVYOyAvLyByZWZlcmVuY2UgbW92ZSB4XG4gIHZhciBfbW92ZVk7IC8vIHJlZmVyZW5jZSBtb3ZlIHlcbiAgdmFyIF9pbml0aWFsU2libGluZzsgLy8gcmVmZXJlbmNlIHNpYmxpbmcgd2hlbiBncmFiYmVkXG4gIHZhciBfY3VycmVudFNpYmxpbmc7IC8vIHJlZmVyZW5jZSBzaWJsaW5nIG5vd1xuICB2YXIgX2NvcHk7IC8vIGl0ZW0gdXNlZCBmb3IgY29weWluZ1xuICB2YXIgX3JlbmRlclRpbWVyOyAvLyB0aW1lciBmb3Igc2V0VGltZW91dCByZW5kZXJNaXJyb3JJbWFnZVxuICB2YXIgX2xhc3REcm9wVGFyZ2V0ID0gbnVsbDsgLy8gbGFzdCBjb250YWluZXIgaXRlbSB3YXMgb3ZlclxuICB2YXIgX2dyYWJiZWQ7IC8vIGhvbGRzIG1vdXNlZG93biBjb250ZXh0IHVudGlsIGZpcnN0IG1vdXNlbW92ZVxuXG4gIHZhciBvID0gb3B0aW9ucyB8fCB7fTtcbiAgaWYgKG8ubW92ZXMgPT09IHZvaWQgMCkgeyBvLm1vdmVzID0gYWx3YXlzOyB9XG4gIGlmIChvLmFjY2VwdHMgPT09IHZvaWQgMCkgeyBvLmFjY2VwdHMgPSBhbHdheXM7IH1cbiAgaWYgKG8uaW52YWxpZCA9PT0gdm9pZCAwKSB7IG8uaW52YWxpZCA9IGludmFsaWRUYXJnZXQ7IH1cbiAgaWYgKG8uY29udGFpbmVycyA9PT0gdm9pZCAwKSB7IG8uY29udGFpbmVycyA9IGluaXRpYWxDb250YWluZXJzIHx8IFtdOyB9XG4gIGlmIChvLmlzQ29udGFpbmVyID09PSB2b2lkIDApIHsgby5pc0NvbnRhaW5lciA9IG5ldmVyOyB9XG4gIGlmIChvLmNvcHkgPT09IHZvaWQgMCkgeyBvLmNvcHkgPSBmYWxzZTsgfVxuICBpZiAoby5jb3B5U29ydFNvdXJjZSA9PT0gdm9pZCAwKSB7IG8uY29weVNvcnRTb3VyY2UgPSBmYWxzZTsgfVxuICBpZiAoby5yZXZlcnRPblNwaWxsID09PSB2b2lkIDApIHsgby5yZXZlcnRPblNwaWxsID0gZmFsc2U7IH1cbiAgaWYgKG8ucmVtb3ZlT25TcGlsbCA9PT0gdm9pZCAwKSB7IG8ucmVtb3ZlT25TcGlsbCA9IGZhbHNlOyB9XG4gIGlmIChvLmRpcmVjdGlvbiA9PT0gdm9pZCAwKSB7IG8uZGlyZWN0aW9uID0gJ3ZlcnRpY2FsJzsgfVxuICBpZiAoby5pZ25vcmVJbnB1dFRleHRTZWxlY3Rpb24gPT09IHZvaWQgMCkgeyBvLmlnbm9yZUlucHV0VGV4dFNlbGVjdGlvbiA9IHRydWU7IH1cbiAgaWYgKG8ubWlycm9yQ29udGFpbmVyID09PSB2b2lkIDApIHsgby5taXJyb3JDb250YWluZXIgPSBkb2MuYm9keTsgfVxuXG4gIHZhciBkcmFrZSA9IGVtaXR0ZXIoe1xuICAgIGNvbnRhaW5lcnM6IG8uY29udGFpbmVycyxcbiAgICBzdGFydDogbWFudWFsU3RhcnQsXG4gICAgZW5kOiBlbmQsXG4gICAgY2FuY2VsOiBjYW5jZWwsXG4gICAgcmVtb3ZlOiByZW1vdmUsXG4gICAgZGVzdHJveTogZGVzdHJveSxcbiAgICBjYW5Nb3ZlOiBjYW5Nb3ZlLFxuICAgIGRyYWdnaW5nOiBmYWxzZVxuICB9KTtcblxuICBpZiAoby5yZW1vdmVPblNwaWxsID09PSB0cnVlKSB7XG4gICAgZHJha2Uub24oJ292ZXInLCBzcGlsbE92ZXIpLm9uKCdvdXQnLCBzcGlsbE91dCk7XG4gIH1cblxuICBldmVudHMoKTtcblxuICByZXR1cm4gZHJha2U7XG5cbiAgZnVuY3Rpb24gaXNDb250YWluZXIgKGVsKSB7XG4gICAgcmV0dXJuIGRyYWtlLmNvbnRhaW5lcnMuaW5kZXhPZihlbCkgIT09IC0xIHx8IG8uaXNDb250YWluZXIoZWwpO1xuICB9XG5cbiAgZnVuY3Rpb24gZXZlbnRzIChyZW1vdmUpIHtcbiAgICB2YXIgb3AgPSByZW1vdmUgPyAncmVtb3ZlJyA6ICdhZGQnO1xuICAgIHRvdWNoeShkb2N1bWVudEVsZW1lbnQsIG9wLCAnbW91c2Vkb3duJywgZ3JhYik7XG4gICAgdG91Y2h5KGRvY3VtZW50RWxlbWVudCwgb3AsICdtb3VzZXVwJywgcmVsZWFzZSk7XG4gIH1cblxuICBmdW5jdGlvbiBldmVudHVhbE1vdmVtZW50cyAocmVtb3ZlKSB7XG4gICAgdmFyIG9wID0gcmVtb3ZlID8gJ3JlbW92ZScgOiAnYWRkJztcbiAgICB0b3VjaHkoZG9jdW1lbnRFbGVtZW50LCBvcCwgJ21vdXNlbW92ZScsIHN0YXJ0QmVjYXVzZU1vdXNlTW92ZWQpO1xuICB9XG5cbiAgZnVuY3Rpb24gbW92ZW1lbnRzIChyZW1vdmUpIHtcbiAgICB2YXIgb3AgPSByZW1vdmUgPyAncmVtb3ZlJyA6ICdhZGQnO1xuICAgIGNyb3NzdmVudFtvcF0oZG9jdW1lbnRFbGVtZW50LCAnc2VsZWN0c3RhcnQnLCBwcmV2ZW50R3JhYmJlZCk7IC8vIElFOFxuICAgIGNyb3NzdmVudFtvcF0oZG9jdW1lbnRFbGVtZW50LCAnY2xpY2snLCBwcmV2ZW50R3JhYmJlZCk7XG4gIH1cblxuICBmdW5jdGlvbiBkZXN0cm95ICgpIHtcbiAgICBldmVudHModHJ1ZSk7XG4gICAgcmVsZWFzZSh7fSk7XG4gIH1cblxuICBmdW5jdGlvbiBwcmV2ZW50R3JhYmJlZCAoZSkge1xuICAgIGlmIChfZ3JhYmJlZCkge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGdyYWIgKGUpIHtcbiAgICBfbW92ZVggPSBlLmNsaWVudFg7XG4gICAgX21vdmVZID0gZS5jbGllbnRZO1xuXG4gICAgdmFyIGlnbm9yZSA9IHdoaWNoTW91c2VCdXR0b24oZSkgIT09IDEgfHwgZS5tZXRhS2V5IHx8IGUuY3RybEtleTtcbiAgICBpZiAoaWdub3JlKSB7XG4gICAgICByZXR1cm47IC8vIHdlIG9ubHkgY2FyZSBhYm91dCBob25lc3QtdG8tZ29kIGxlZnQgY2xpY2tzIGFuZCB0b3VjaCBldmVudHNcbiAgICB9XG4gICAgdmFyIGl0ZW0gPSBlLnRhcmdldDtcbiAgICB2YXIgY29udGV4dCA9IGNhblN0YXJ0KGl0ZW0pO1xuICAgIGlmICghY29udGV4dCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBfZ3JhYmJlZCA9IGNvbnRleHQ7XG4gICAgZXZlbnR1YWxNb3ZlbWVudHMoKTtcbiAgICBpZiAoZS50eXBlID09PSAnbW91c2Vkb3duJykge1xuICAgICAgaWYgKGlzSW5wdXQoaXRlbSkpIHsgLy8gc2VlIGFsc286IGh0dHBzOi8vZ2l0aHViLmNvbS9iZXZhY3F1YS9kcmFndWxhL2lzc3Vlcy8yMDhcbiAgICAgICAgaXRlbS5mb2N1cygpOyAvLyBmaXhlcyBodHRwczovL2dpdGh1Yi5jb20vYmV2YWNxdWEvZHJhZ3VsYS9pc3N1ZXMvMTc2XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7IC8vIGZpeGVzIGh0dHBzOi8vZ2l0aHViLmNvbS9iZXZhY3F1YS9kcmFndWxhL2lzc3Vlcy8xNTVcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBzdGFydEJlY2F1c2VNb3VzZU1vdmVkIChlKSB7XG4gICAgaWYgKCFfZ3JhYmJlZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAod2hpY2hNb3VzZUJ1dHRvbihlKSA9PT0gMCkge1xuICAgICAgcmVsZWFzZSh7fSk7XG4gICAgICByZXR1cm47IC8vIHdoZW4gdGV4dCBpcyBzZWxlY3RlZCBvbiBhbiBpbnB1dCBhbmQgdGhlbiBkcmFnZ2VkLCBtb3VzZXVwIGRvZXNuJ3QgZmlyZS4gdGhpcyBpcyBvdXIgb25seSBob3BlXG4gICAgfVxuICAgIC8vIHRydXRoeSBjaGVjayBmaXhlcyAjMjM5LCBlcXVhbGl0eSBmaXhlcyAjMjA3XG4gICAgaWYgKGUuY2xpZW50WCAhPT0gdm9pZCAwICYmIGUuY2xpZW50WCA9PT0gX21vdmVYICYmIGUuY2xpZW50WSAhPT0gdm9pZCAwICYmIGUuY2xpZW50WSA9PT0gX21vdmVZKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChvLmlnbm9yZUlucHV0VGV4dFNlbGVjdGlvbikge1xuICAgICAgdmFyIGNsaWVudFggPSBnZXRDb29yZCgnY2xpZW50WCcsIGUpO1xuICAgICAgdmFyIGNsaWVudFkgPSBnZXRDb29yZCgnY2xpZW50WScsIGUpO1xuICAgICAgdmFyIGVsZW1lbnRCZWhpbmRDdXJzb3IgPSBkb2MuZWxlbWVudEZyb21Qb2ludChjbGllbnRYLCBjbGllbnRZKTtcbiAgICAgIGlmIChpc0lucHV0KGVsZW1lbnRCZWhpbmRDdXJzb3IpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgZ3JhYmJlZCA9IF9ncmFiYmVkOyAvLyBjYWxsIHRvIGVuZCgpIHVuc2V0cyBfZ3JhYmJlZFxuICAgIGV2ZW50dWFsTW92ZW1lbnRzKHRydWUpO1xuICAgIG1vdmVtZW50cygpO1xuICAgIGVuZCgpO1xuICAgIHN0YXJ0KGdyYWJiZWQpO1xuXG4gICAgdmFyIG9mZnNldCA9IGdldE9mZnNldChfaXRlbSk7XG4gICAgX29mZnNldFggPSBnZXRDb29yZCgncGFnZVgnLCBlKSAtIG9mZnNldC5sZWZ0O1xuICAgIF9vZmZzZXRZID0gZ2V0Q29vcmQoJ3BhZ2VZJywgZSkgLSBvZmZzZXQudG9wO1xuXG4gICAgY2xhc3Nlcy5hZGQoX2NvcHkgfHwgX2l0ZW0sICdndS10cmFuc2l0Jyk7XG4gICAgcmVuZGVyTWlycm9ySW1hZ2UoKTtcbiAgICBkcmFnKGUpO1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuU3RhcnQgKGl0ZW0pIHtcbiAgICBpZiAoZHJha2UuZHJhZ2dpbmcgJiYgX21pcnJvcikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoaXNDb250YWluZXIoaXRlbSkpIHtcbiAgICAgIHJldHVybjsgLy8gZG9uJ3QgZHJhZyBjb250YWluZXIgaXRzZWxmXG4gICAgfVxuICAgIHZhciBoYW5kbGUgPSBpdGVtO1xuICAgIHdoaWxlIChnZXRQYXJlbnQoaXRlbSkgJiYgaXNDb250YWluZXIoZ2V0UGFyZW50KGl0ZW0pKSA9PT0gZmFsc2UpIHtcbiAgICAgIGlmIChvLmludmFsaWQoaXRlbSwgaGFuZGxlKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpdGVtID0gZ2V0UGFyZW50KGl0ZW0pOyAvLyBkcmFnIHRhcmdldCBzaG91bGQgYmUgYSB0b3AgZWxlbWVudFxuICAgICAgaWYgKCFpdGVtKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG4gICAgdmFyIHNvdXJjZSA9IGdldFBhcmVudChpdGVtKTtcbiAgICBpZiAoIXNvdXJjZSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoby5pbnZhbGlkKGl0ZW0sIGhhbmRsZSkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgbW92YWJsZSA9IG8ubW92ZXMoaXRlbSwgc291cmNlLCBoYW5kbGUsIG5leHRFbChpdGVtKSk7XG4gICAgaWYgKCFtb3ZhYmxlKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGl0ZW06IGl0ZW0sXG4gICAgICBzb3VyY2U6IHNvdXJjZVxuICAgIH07XG4gIH1cblxuICBmdW5jdGlvbiBjYW5Nb3ZlIChpdGVtKSB7XG4gICAgcmV0dXJuICEhY2FuU3RhcnQoaXRlbSk7XG4gIH1cblxuICBmdW5jdGlvbiBtYW51YWxTdGFydCAoaXRlbSkge1xuICAgIHZhciBjb250ZXh0ID0gY2FuU3RhcnQoaXRlbSk7XG4gICAgaWYgKGNvbnRleHQpIHtcbiAgICAgIHN0YXJ0KGNvbnRleHQpO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHN0YXJ0IChjb250ZXh0KSB7XG4gICAgaWYgKGlzQ29weShjb250ZXh0Lml0ZW0sIGNvbnRleHQuc291cmNlKSkge1xuICAgICAgX2NvcHkgPSBjb250ZXh0Lml0ZW0uY2xvbmVOb2RlKHRydWUpO1xuICAgICAgZHJha2UuZW1pdCgnY2xvbmVkJywgX2NvcHksIGNvbnRleHQuaXRlbSwgJ2NvcHknKTtcbiAgICB9XG5cbiAgICBfc291cmNlID0gY29udGV4dC5zb3VyY2U7XG4gICAgX2l0ZW0gPSBjb250ZXh0Lml0ZW07XG4gICAgX2luaXRpYWxTaWJsaW5nID0gX2N1cnJlbnRTaWJsaW5nID0gbmV4dEVsKGNvbnRleHQuaXRlbSk7XG5cbiAgICBkcmFrZS5kcmFnZ2luZyA9IHRydWU7XG4gICAgZHJha2UuZW1pdCgnZHJhZycsIF9pdGVtLCBfc291cmNlKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGludmFsaWRUYXJnZXQgKCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGVuZCAoKSB7XG4gICAgaWYgKCFkcmFrZS5kcmFnZ2luZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB2YXIgaXRlbSA9IF9jb3B5IHx8IF9pdGVtO1xuICAgIGRyb3AoaXRlbSwgZ2V0UGFyZW50KGl0ZW0pKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHVuZ3JhYiAoKSB7XG4gICAgX2dyYWJiZWQgPSBmYWxzZTtcbiAgICBldmVudHVhbE1vdmVtZW50cyh0cnVlKTtcbiAgICBtb3ZlbWVudHModHJ1ZSk7XG4gIH1cblxuICBmdW5jdGlvbiByZWxlYXNlIChlKSB7XG4gICAgdW5ncmFiKCk7XG5cbiAgICBpZiAoIWRyYWtlLmRyYWdnaW5nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHZhciBpdGVtID0gX2NvcHkgfHwgX2l0ZW07XG4gICAgdmFyIGNsaWVudFggPSBnZXRDb29yZCgnY2xpZW50WCcsIGUpO1xuICAgIHZhciBjbGllbnRZID0gZ2V0Q29vcmQoJ2NsaWVudFknLCBlKTtcbiAgICB2YXIgZWxlbWVudEJlaGluZEN1cnNvciA9IGdldEVsZW1lbnRCZWhpbmRQb2ludChfbWlycm9yLCBjbGllbnRYLCBjbGllbnRZKTtcbiAgICB2YXIgZHJvcFRhcmdldCA9IGZpbmREcm9wVGFyZ2V0KGVsZW1lbnRCZWhpbmRDdXJzb3IsIGNsaWVudFgsIGNsaWVudFkpO1xuICAgIGlmIChkcm9wVGFyZ2V0ICYmICgoX2NvcHkgJiYgby5jb3B5U29ydFNvdXJjZSkgfHwgKCFfY29weSB8fCBkcm9wVGFyZ2V0ICE9PSBfc291cmNlKSkpIHtcbiAgICAgIGRyb3AoaXRlbSwgZHJvcFRhcmdldCk7XG4gICAgfSBlbHNlIGlmIChvLnJlbW92ZU9uU3BpbGwpIHtcbiAgICAgIHJlbW92ZSgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjYW5jZWwoKTtcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBkcm9wIChpdGVtLCB0YXJnZXQpIHtcbiAgICB2YXIgcGFyZW50ID0gZ2V0UGFyZW50KGl0ZW0pO1xuICAgIGlmIChfY29weSAmJiBvLmNvcHlTb3J0U291cmNlICYmIHRhcmdldCA9PT0gX3NvdXJjZSkge1xuICAgICAgcGFyZW50LnJlbW92ZUNoaWxkKF9pdGVtKTtcbiAgICB9XG4gICAgaWYgKGlzSW5pdGlhbFBsYWNlbWVudCh0YXJnZXQpKSB7XG4gICAgICBkcmFrZS5lbWl0KCdjYW5jZWwnLCBpdGVtLCBfc291cmNlLCBfc291cmNlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgZHJha2UuZW1pdCgnZHJvcCcsIGl0ZW0sIHRhcmdldCwgX3NvdXJjZSwgX2N1cnJlbnRTaWJsaW5nKTtcbiAgICB9XG4gICAgY2xlYW51cCgpO1xuICB9XG5cbiAgZnVuY3Rpb24gcmVtb3ZlICgpIHtcbiAgICBpZiAoIWRyYWtlLmRyYWdnaW5nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHZhciBpdGVtID0gX2NvcHkgfHwgX2l0ZW07XG4gICAgdmFyIHBhcmVudCA9IGdldFBhcmVudChpdGVtKTtcbiAgICBpZiAocGFyZW50KSB7XG4gICAgICBwYXJlbnQucmVtb3ZlQ2hpbGQoaXRlbSk7XG4gICAgfVxuICAgIGRyYWtlLmVtaXQoX2NvcHkgPyAnY2FuY2VsJyA6ICdyZW1vdmUnLCBpdGVtLCBwYXJlbnQsIF9zb3VyY2UpO1xuICAgIGNsZWFudXAoKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNhbmNlbCAocmV2ZXJ0KSB7XG4gICAgaWYgKCFkcmFrZS5kcmFnZ2luZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB2YXIgcmV2ZXJ0cyA9IGFyZ3VtZW50cy5sZW5ndGggPiAwID8gcmV2ZXJ0IDogby5yZXZlcnRPblNwaWxsO1xuICAgIHZhciBpdGVtID0gX2NvcHkgfHwgX2l0ZW07XG4gICAgdmFyIHBhcmVudCA9IGdldFBhcmVudChpdGVtKTtcbiAgICB2YXIgaW5pdGlhbCA9IGlzSW5pdGlhbFBsYWNlbWVudChwYXJlbnQpO1xuICAgIGlmIChpbml0aWFsID09PSBmYWxzZSAmJiByZXZlcnRzKSB7XG4gICAgICBpZiAoX2NvcHkpIHtcbiAgICAgICAgaWYgKHBhcmVudCkge1xuICAgICAgICAgIHBhcmVudC5yZW1vdmVDaGlsZChfY29weSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIF9zb3VyY2UuaW5zZXJ0QmVmb3JlKGl0ZW0sIF9pbml0aWFsU2libGluZyk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChpbml0aWFsIHx8IHJldmVydHMpIHtcbiAgICAgIGRyYWtlLmVtaXQoJ2NhbmNlbCcsIGl0ZW0sIF9zb3VyY2UsIF9zb3VyY2UpO1xuICAgIH0gZWxzZSB7XG4gICAgICBkcmFrZS5lbWl0KCdkcm9wJywgaXRlbSwgcGFyZW50LCBfc291cmNlLCBfY3VycmVudFNpYmxpbmcpO1xuICAgIH1cbiAgICBjbGVhbnVwKCk7XG4gIH1cblxuICBmdW5jdGlvbiBjbGVhbnVwICgpIHtcbiAgICB2YXIgaXRlbSA9IF9jb3B5IHx8IF9pdGVtO1xuICAgIHVuZ3JhYigpO1xuICAgIHJlbW92ZU1pcnJvckltYWdlKCk7XG4gICAgaWYgKGl0ZW0pIHtcbiAgICAgIGNsYXNzZXMucm0oaXRlbSwgJ2d1LXRyYW5zaXQnKTtcbiAgICB9XG4gICAgaWYgKF9yZW5kZXJUaW1lcikge1xuICAgICAgY2xlYXJUaW1lb3V0KF9yZW5kZXJUaW1lcik7XG4gICAgfVxuICAgIGRyYWtlLmRyYWdnaW5nID0gZmFsc2U7XG4gICAgaWYgKF9sYXN0RHJvcFRhcmdldCkge1xuICAgICAgZHJha2UuZW1pdCgnb3V0JywgaXRlbSwgX2xhc3REcm9wVGFyZ2V0LCBfc291cmNlKTtcbiAgICB9XG4gICAgZHJha2UuZW1pdCgnZHJhZ2VuZCcsIGl0ZW0pO1xuICAgIF9zb3VyY2UgPSBfaXRlbSA9IF9jb3B5ID0gX2luaXRpYWxTaWJsaW5nID0gX2N1cnJlbnRTaWJsaW5nID0gX3JlbmRlclRpbWVyID0gX2xhc3REcm9wVGFyZ2V0ID0gbnVsbDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGlzSW5pdGlhbFBsYWNlbWVudCAodGFyZ2V0LCBzKSB7XG4gICAgdmFyIHNpYmxpbmc7XG4gICAgaWYgKHMgIT09IHZvaWQgMCkge1xuICAgICAgc2libGluZyA9IHM7XG4gICAgfSBlbHNlIGlmIChfbWlycm9yKSB7XG4gICAgICBzaWJsaW5nID0gX2N1cnJlbnRTaWJsaW5nO1xuICAgIH0gZWxzZSB7XG4gICAgICBzaWJsaW5nID0gbmV4dEVsKF9jb3B5IHx8IF9pdGVtKTtcbiAgICB9XG4gICAgcmV0dXJuIHRhcmdldCA9PT0gX3NvdXJjZSAmJiBzaWJsaW5nID09PSBfaW5pdGlhbFNpYmxpbmc7XG4gIH1cblxuICBmdW5jdGlvbiBmaW5kRHJvcFRhcmdldCAoZWxlbWVudEJlaGluZEN1cnNvciwgY2xpZW50WCwgY2xpZW50WSkge1xuICAgIHZhciB0YXJnZXQgPSBlbGVtZW50QmVoaW5kQ3Vyc29yO1xuICAgIHdoaWxlICh0YXJnZXQgJiYgIWFjY2VwdGVkKCkpIHtcbiAgICAgIHRhcmdldCA9IGdldFBhcmVudCh0YXJnZXQpO1xuICAgIH1cbiAgICByZXR1cm4gdGFyZ2V0O1xuXG4gICAgZnVuY3Rpb24gYWNjZXB0ZWQgKCkge1xuICAgICAgdmFyIGRyb3BwYWJsZSA9IGlzQ29udGFpbmVyKHRhcmdldCk7XG4gICAgICBpZiAoZHJvcHBhYmxlID09PSBmYWxzZSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIHZhciBpbW1lZGlhdGUgPSBnZXRJbW1lZGlhdGVDaGlsZCh0YXJnZXQsIGVsZW1lbnRCZWhpbmRDdXJzb3IpO1xuICAgICAgdmFyIHJlZmVyZW5jZSA9IGdldFJlZmVyZW5jZSh0YXJnZXQsIGltbWVkaWF0ZSwgY2xpZW50WCwgY2xpZW50WSk7XG4gICAgICB2YXIgaW5pdGlhbCA9IGlzSW5pdGlhbFBsYWNlbWVudCh0YXJnZXQsIHJlZmVyZW5jZSk7XG4gICAgICBpZiAoaW5pdGlhbCkge1xuICAgICAgICByZXR1cm4gdHJ1ZTsgLy8gc2hvdWxkIGFsd2F5cyBiZSBhYmxlIHRvIGRyb3AgaXQgcmlnaHQgYmFjayB3aGVyZSBpdCB3YXNcbiAgICAgIH1cbiAgICAgIHJldHVybiBvLmFjY2VwdHMoX2l0ZW0sIHRhcmdldCwgX3NvdXJjZSwgcmVmZXJlbmNlKTtcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBkcmFnIChlKSB7XG4gICAgaWYgKCFfbWlycm9yKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGUucHJldmVudERlZmF1bHQoKTtcblxuICAgIHZhciBjbGllbnRYID0gZ2V0Q29vcmQoJ2NsaWVudFgnLCBlKTtcbiAgICB2YXIgY2xpZW50WSA9IGdldENvb3JkKCdjbGllbnRZJywgZSk7XG4gICAgdmFyIHggPSBjbGllbnRYIC0gX29mZnNldFg7XG4gICAgdmFyIHkgPSBjbGllbnRZIC0gX29mZnNldFk7XG5cbiAgICBfbWlycm9yLnN0eWxlLmxlZnQgPSB4ICsgJ3B4JztcbiAgICBfbWlycm9yLnN0eWxlLnRvcCA9IHkgKyAncHgnO1xuXG4gICAgdmFyIGl0ZW0gPSBfY29weSB8fCBfaXRlbTtcbiAgICB2YXIgZWxlbWVudEJlaGluZEN1cnNvciA9IGdldEVsZW1lbnRCZWhpbmRQb2ludChfbWlycm9yLCBjbGllbnRYLCBjbGllbnRZKTtcbiAgICB2YXIgZHJvcFRhcmdldCA9IGZpbmREcm9wVGFyZ2V0KGVsZW1lbnRCZWhpbmRDdXJzb3IsIGNsaWVudFgsIGNsaWVudFkpO1xuICAgIHZhciBjaGFuZ2VkID0gZHJvcFRhcmdldCAhPT0gbnVsbCAmJiBkcm9wVGFyZ2V0ICE9PSBfbGFzdERyb3BUYXJnZXQ7XG4gICAgaWYgKGNoYW5nZWQgfHwgZHJvcFRhcmdldCA9PT0gbnVsbCkge1xuICAgICAgb3V0KCk7XG4gICAgICBfbGFzdERyb3BUYXJnZXQgPSBkcm9wVGFyZ2V0O1xuICAgICAgb3ZlcigpO1xuICAgIH1cbiAgICB2YXIgcGFyZW50ID0gZ2V0UGFyZW50KGl0ZW0pO1xuICAgIGlmIChkcm9wVGFyZ2V0ID09PSBfc291cmNlICYmIF9jb3B5ICYmICFvLmNvcHlTb3J0U291cmNlKSB7XG4gICAgICBpZiAocGFyZW50KSB7XG4gICAgICAgIHBhcmVudC5yZW1vdmVDaGlsZChpdGVtKTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdmFyIHJlZmVyZW5jZTtcbiAgICB2YXIgaW1tZWRpYXRlID0gZ2V0SW1tZWRpYXRlQ2hpbGQoZHJvcFRhcmdldCwgZWxlbWVudEJlaGluZEN1cnNvcik7XG4gICAgaWYgKGltbWVkaWF0ZSAhPT0gbnVsbCkge1xuICAgICAgcmVmZXJlbmNlID0gZ2V0UmVmZXJlbmNlKGRyb3BUYXJnZXQsIGltbWVkaWF0ZSwgY2xpZW50WCwgY2xpZW50WSk7XG4gICAgfSBlbHNlIGlmIChvLnJldmVydE9uU3BpbGwgPT09IHRydWUgJiYgIV9jb3B5KSB7XG4gICAgICByZWZlcmVuY2UgPSBfaW5pdGlhbFNpYmxpbmc7XG4gICAgICBkcm9wVGFyZ2V0ID0gX3NvdXJjZTtcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKF9jb3B5ICYmIHBhcmVudCkge1xuICAgICAgICBwYXJlbnQucmVtb3ZlQ2hpbGQoaXRlbSk7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChcbiAgICAgIChyZWZlcmVuY2UgPT09IG51bGwgJiYgY2hhbmdlZCkgfHxcbiAgICAgIHJlZmVyZW5jZSAhPT0gaXRlbSAmJlxuICAgICAgcmVmZXJlbmNlICE9PSBuZXh0RWwoaXRlbSlcbiAgICApIHtcbiAgICAgIF9jdXJyZW50U2libGluZyA9IHJlZmVyZW5jZTtcbiAgICAgIGRyb3BUYXJnZXQuaW5zZXJ0QmVmb3JlKGl0ZW0sIHJlZmVyZW5jZSk7XG4gICAgICBkcmFrZS5lbWl0KCdzaGFkb3cnLCBpdGVtLCBkcm9wVGFyZ2V0LCBfc291cmNlKTtcbiAgICB9XG4gICAgZnVuY3Rpb24gbW92ZWQgKHR5cGUpIHsgZHJha2UuZW1pdCh0eXBlLCBpdGVtLCBfbGFzdERyb3BUYXJnZXQsIF9zb3VyY2UpOyB9XG4gICAgZnVuY3Rpb24gb3ZlciAoKSB7IGlmIChjaGFuZ2VkKSB7IG1vdmVkKCdvdmVyJyk7IH0gfVxuICAgIGZ1bmN0aW9uIG91dCAoKSB7IGlmIChfbGFzdERyb3BUYXJnZXQpIHsgbW92ZWQoJ291dCcpOyB9IH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHNwaWxsT3ZlciAoZWwpIHtcbiAgICBjbGFzc2VzLnJtKGVsLCAnZ3UtaGlkZScpO1xuICB9XG5cbiAgZnVuY3Rpb24gc3BpbGxPdXQgKGVsKSB7XG4gICAgaWYgKGRyYWtlLmRyYWdnaW5nKSB7IGNsYXNzZXMuYWRkKGVsLCAnZ3UtaGlkZScpOyB9XG4gIH1cblxuICBmdW5jdGlvbiByZW5kZXJNaXJyb3JJbWFnZSAoKSB7XG4gICAgaWYgKF9taXJyb3IpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdmFyIHJlY3QgPSBfaXRlbS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICBfbWlycm9yID0gX2l0ZW0uY2xvbmVOb2RlKHRydWUpO1xuICAgIF9taXJyb3Iuc3R5bGUud2lkdGggPSBnZXRSZWN0V2lkdGgocmVjdCkgKyAncHgnO1xuICAgIF9taXJyb3Iuc3R5bGUuaGVpZ2h0ID0gZ2V0UmVjdEhlaWdodChyZWN0KSArICdweCc7XG4gICAgY2xhc3Nlcy5ybShfbWlycm9yLCAnZ3UtdHJhbnNpdCcpO1xuICAgIGNsYXNzZXMuYWRkKF9taXJyb3IsICdndS1taXJyb3InKTtcbiAgICBvLm1pcnJvckNvbnRhaW5lci5hcHBlbmRDaGlsZChfbWlycm9yKTtcbiAgICB0b3VjaHkoZG9jdW1lbnRFbGVtZW50LCAnYWRkJywgJ21vdXNlbW92ZScsIGRyYWcpO1xuICAgIGNsYXNzZXMuYWRkKG8ubWlycm9yQ29udGFpbmVyLCAnZ3UtdW5zZWxlY3RhYmxlJyk7XG4gICAgZHJha2UuZW1pdCgnY2xvbmVkJywgX21pcnJvciwgX2l0ZW0sICdtaXJyb3InKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlbW92ZU1pcnJvckltYWdlICgpIHtcbiAgICBpZiAoX21pcnJvcikge1xuICAgICAgY2xhc3Nlcy5ybShvLm1pcnJvckNvbnRhaW5lciwgJ2d1LXVuc2VsZWN0YWJsZScpO1xuICAgICAgdG91Y2h5KGRvY3VtZW50RWxlbWVudCwgJ3JlbW92ZScsICdtb3VzZW1vdmUnLCBkcmFnKTtcbiAgICAgIGdldFBhcmVudChfbWlycm9yKS5yZW1vdmVDaGlsZChfbWlycm9yKTtcbiAgICAgIF9taXJyb3IgPSBudWxsO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGdldEltbWVkaWF0ZUNoaWxkIChkcm9wVGFyZ2V0LCB0YXJnZXQpIHtcbiAgICB2YXIgaW1tZWRpYXRlID0gdGFyZ2V0O1xuICAgIHdoaWxlIChpbW1lZGlhdGUgIT09IGRyb3BUYXJnZXQgJiYgZ2V0UGFyZW50KGltbWVkaWF0ZSkgIT09IGRyb3BUYXJnZXQpIHtcbiAgICAgIGltbWVkaWF0ZSA9IGdldFBhcmVudChpbW1lZGlhdGUpO1xuICAgIH1cbiAgICBpZiAoaW1tZWRpYXRlID09PSBkb2N1bWVudEVsZW1lbnQpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICByZXR1cm4gaW1tZWRpYXRlO1xuICB9XG5cbiAgZnVuY3Rpb24gZ2V0UmVmZXJlbmNlIChkcm9wVGFyZ2V0LCB0YXJnZXQsIHgsIHkpIHtcbiAgICB2YXIgaG9yaXpvbnRhbCA9IG8uZGlyZWN0aW9uID09PSAnaG9yaXpvbnRhbCc7XG4gICAgdmFyIHJlZmVyZW5jZSA9IHRhcmdldCAhPT0gZHJvcFRhcmdldCA/IGluc2lkZSgpIDogb3V0c2lkZSgpO1xuICAgIHJldHVybiByZWZlcmVuY2U7XG5cbiAgICBmdW5jdGlvbiBvdXRzaWRlICgpIHsgLy8gc2xvd2VyLCBidXQgYWJsZSB0byBmaWd1cmUgb3V0IGFueSBwb3NpdGlvblxuICAgICAgdmFyIGxlbiA9IGRyb3BUYXJnZXQuY2hpbGRyZW4ubGVuZ3RoO1xuICAgICAgdmFyIGk7XG4gICAgICB2YXIgZWw7XG4gICAgICB2YXIgcmVjdDtcbiAgICAgIGZvciAoaSA9IDA7IGkgPCBsZW47IGkrKykge1xuICAgICAgICBlbCA9IGRyb3BUYXJnZXQuY2hpbGRyZW5baV07XG4gICAgICAgIHJlY3QgPSBlbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgaWYgKGhvcml6b250YWwgJiYgKHJlY3QubGVmdCArIHJlY3Qud2lkdGggLyAyKSA+IHgpIHsgcmV0dXJuIGVsOyB9XG4gICAgICAgIGlmICghaG9yaXpvbnRhbCAmJiAocmVjdC50b3AgKyByZWN0LmhlaWdodCAvIDIpID4geSkgeyByZXR1cm4gZWw7IH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGluc2lkZSAoKSB7IC8vIGZhc3RlciwgYnV0IG9ubHkgYXZhaWxhYmxlIGlmIGRyb3BwZWQgaW5zaWRlIGEgY2hpbGQgZWxlbWVudFxuICAgICAgdmFyIHJlY3QgPSB0YXJnZXQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICBpZiAoaG9yaXpvbnRhbCkge1xuICAgICAgICByZXR1cm4gcmVzb2x2ZSh4ID4gcmVjdC5sZWZ0ICsgZ2V0UmVjdFdpZHRoKHJlY3QpIC8gMik7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzb2x2ZSh5ID4gcmVjdC50b3AgKyBnZXRSZWN0SGVpZ2h0KHJlY3QpIC8gMik7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcmVzb2x2ZSAoYWZ0ZXIpIHtcbiAgICAgIHJldHVybiBhZnRlciA/IG5leHRFbCh0YXJnZXQpIDogdGFyZ2V0O1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGlzQ29weSAoaXRlbSwgY29udGFpbmVyKSB7XG4gICAgcmV0dXJuIHR5cGVvZiBvLmNvcHkgPT09ICdib29sZWFuJyA/IG8uY29weSA6IG8uY29weShpdGVtLCBjb250YWluZXIpO1xuICB9XG59XG5cbmZ1bmN0aW9uIHRvdWNoeSAoZWwsIG9wLCB0eXBlLCBmbikge1xuICB2YXIgdG91Y2ggPSB7XG4gICAgbW91c2V1cDogJ3RvdWNoZW5kJyxcbiAgICBtb3VzZWRvd246ICd0b3VjaHN0YXJ0JyxcbiAgICBtb3VzZW1vdmU6ICd0b3VjaG1vdmUnXG4gIH07XG4gIHZhciBwb2ludGVycyA9IHtcbiAgICBtb3VzZXVwOiAncG9pbnRlcnVwJyxcbiAgICBtb3VzZWRvd246ICdwb2ludGVyZG93bicsXG4gICAgbW91c2Vtb3ZlOiAncG9pbnRlcm1vdmUnXG4gIH07XG4gIHZhciBtaWNyb3NvZnQgPSB7XG4gICAgbW91c2V1cDogJ01TUG9pbnRlclVwJyxcbiAgICBtb3VzZWRvd246ICdNU1BvaW50ZXJEb3duJyxcbiAgICBtb3VzZW1vdmU6ICdNU1BvaW50ZXJNb3ZlJ1xuICB9O1xuICBpZiAoZ2xvYmFsLm5hdmlnYXRvci5wb2ludGVyRW5hYmxlZCkge1xuICAgIGNyb3NzdmVudFtvcF0oZWwsIHBvaW50ZXJzW3R5cGVdLCBmbik7XG4gIH0gZWxzZSBpZiAoZ2xvYmFsLm5hdmlnYXRvci5tc1BvaW50ZXJFbmFibGVkKSB7XG4gICAgY3Jvc3N2ZW50W29wXShlbCwgbWljcm9zb2Z0W3R5cGVdLCBmbik7XG4gIH0gZWxzZSB7XG4gICAgY3Jvc3N2ZW50W29wXShlbCwgdG91Y2hbdHlwZV0sIGZuKTtcbiAgICBjcm9zc3ZlbnRbb3BdKGVsLCB0eXBlLCBmbik7XG4gIH1cbn1cblxuZnVuY3Rpb24gd2hpY2hNb3VzZUJ1dHRvbiAoZSkge1xuICBpZiAoZS50b3VjaGVzICE9PSB2b2lkIDApIHsgcmV0dXJuIGUudG91Y2hlcy5sZW5ndGg7IH1cbiAgaWYgKGUud2hpY2ggIT09IHZvaWQgMCAmJiBlLndoaWNoICE9PSAwKSB7IHJldHVybiBlLndoaWNoOyB9IC8vIHNlZSBodHRwczovL2dpdGh1Yi5jb20vYmV2YWNxdWEvZHJhZ3VsYS9pc3N1ZXMvMjYxXG4gIGlmIChlLmJ1dHRvbnMgIT09IHZvaWQgMCkgeyByZXR1cm4gZS5idXR0b25zOyB9XG4gIHZhciBidXR0b24gPSBlLmJ1dHRvbjtcbiAgaWYgKGJ1dHRvbiAhPT0gdm9pZCAwKSB7IC8vIHNlZSBodHRwczovL2dpdGh1Yi5jb20vanF1ZXJ5L2pxdWVyeS9ibG9iLzk5ZThmZjFiYWE3YWUzNDFlOTRiYjg5YzNlODQ1NzBjN2MzYWQ5ZWEvc3JjL2V2ZW50LmpzI0w1NzMtTDU3NVxuICAgIHJldHVybiBidXR0b24gJiAxID8gMSA6IGJ1dHRvbiAmIDIgPyAzIDogKGJ1dHRvbiAmIDQgPyAyIDogMCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gZ2V0T2Zmc2V0IChlbCkge1xuICB2YXIgcmVjdCA9IGVsLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICByZXR1cm4ge1xuICAgIGxlZnQ6IHJlY3QubGVmdCArIGdldFNjcm9sbCgnc2Nyb2xsTGVmdCcsICdwYWdlWE9mZnNldCcpLFxuICAgIHRvcDogcmVjdC50b3AgKyBnZXRTY3JvbGwoJ3Njcm9sbFRvcCcsICdwYWdlWU9mZnNldCcpXG4gIH07XG59XG5cbmZ1bmN0aW9uIGdldFNjcm9sbCAoc2Nyb2xsUHJvcCwgb2Zmc2V0UHJvcCkge1xuICBpZiAodHlwZW9mIGdsb2JhbFtvZmZzZXRQcm9wXSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICByZXR1cm4gZ2xvYmFsW29mZnNldFByb3BdO1xuICB9XG4gIGlmIChkb2N1bWVudEVsZW1lbnQuY2xpZW50SGVpZ2h0KSB7XG4gICAgcmV0dXJuIGRvY3VtZW50RWxlbWVudFtzY3JvbGxQcm9wXTtcbiAgfVxuICByZXR1cm4gZG9jLmJvZHlbc2Nyb2xsUHJvcF07XG59XG5cbmZ1bmN0aW9uIGdldEVsZW1lbnRCZWhpbmRQb2ludCAocG9pbnQsIHgsIHkpIHtcbiAgdmFyIHAgPSBwb2ludCB8fCB7fTtcbiAgdmFyIHN0YXRlID0gcC5jbGFzc05hbWU7XG4gIHZhciBlbDtcbiAgcC5jbGFzc05hbWUgKz0gJyBndS1oaWRlJztcbiAgZWwgPSBkb2MuZWxlbWVudEZyb21Qb2ludCh4LCB5KTtcbiAgcC5jbGFzc05hbWUgPSBzdGF0ZTtcbiAgcmV0dXJuIGVsO1xufVxuXG5mdW5jdGlvbiBuZXZlciAoKSB7IHJldHVybiBmYWxzZTsgfVxuZnVuY3Rpb24gYWx3YXlzICgpIHsgcmV0dXJuIHRydWU7IH1cbmZ1bmN0aW9uIGdldFJlY3RXaWR0aCAocmVjdCkgeyByZXR1cm4gcmVjdC53aWR0aCB8fCAocmVjdC5yaWdodCAtIHJlY3QubGVmdCk7IH1cbmZ1bmN0aW9uIGdldFJlY3RIZWlnaHQgKHJlY3QpIHsgcmV0dXJuIHJlY3QuaGVpZ2h0IHx8IChyZWN0LmJvdHRvbSAtIHJlY3QudG9wKTsgfVxuZnVuY3Rpb24gZ2V0UGFyZW50IChlbCkgeyByZXR1cm4gZWwucGFyZW50Tm9kZSA9PT0gZG9jID8gbnVsbCA6IGVsLnBhcmVudE5vZGU7IH1cbmZ1bmN0aW9uIGlzSW5wdXQgKGVsKSB7IHJldHVybiBlbC50YWdOYW1lID09PSAnSU5QVVQnIHx8IGVsLnRhZ05hbWUgPT09ICdURVhUQVJFQScgfHwgZWwudGFnTmFtZSA9PT0gJ1NFTEVDVCcgfHwgaXNFZGl0YWJsZShlbCk7IH1cbmZ1bmN0aW9uIGlzRWRpdGFibGUgKGVsKSB7XG4gIGlmICghZWwpIHsgcmV0dXJuIGZhbHNlOyB9IC8vIG5vIHBhcmVudHMgd2VyZSBlZGl0YWJsZVxuICBpZiAoZWwuY29udGVudEVkaXRhYmxlID09PSAnZmFsc2UnKSB7IHJldHVybiBmYWxzZTsgfSAvLyBzdG9wIHRoZSBsb29rdXBcbiAgaWYgKGVsLmNvbnRlbnRFZGl0YWJsZSA9PT0gJ3RydWUnKSB7IHJldHVybiB0cnVlOyB9IC8vIGZvdW5kIGEgY29udGVudEVkaXRhYmxlIGVsZW1lbnQgaW4gdGhlIGNoYWluXG4gIHJldHVybiBpc0VkaXRhYmxlKGdldFBhcmVudChlbCkpOyAvLyBjb250ZW50RWRpdGFibGUgaXMgc2V0IHRvICdpbmhlcml0J1xufVxuXG5mdW5jdGlvbiBuZXh0RWwgKGVsKSB7XG4gIHJldHVybiBlbC5uZXh0RWxlbWVudFNpYmxpbmcgfHwgbWFudWFsbHkoKTtcbiAgZnVuY3Rpb24gbWFudWFsbHkgKCkge1xuICAgIHZhciBzaWJsaW5nID0gZWw7XG4gICAgZG8ge1xuICAgICAgc2libGluZyA9IHNpYmxpbmcubmV4dFNpYmxpbmc7XG4gICAgfSB3aGlsZSAoc2libGluZyAmJiBzaWJsaW5nLm5vZGVUeXBlICE9PSAxKTtcbiAgICByZXR1cm4gc2libGluZztcbiAgfVxufVxuXG5mdW5jdGlvbiBnZXRFdmVudEhvc3QgKGUpIHtcbiAgLy8gb24gdG91Y2hlbmQgZXZlbnQsIHdlIGhhdmUgdG8gdXNlIGBlLmNoYW5nZWRUb3VjaGVzYFxuICAvLyBzZWUgaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy83MTkyNTYzL3RvdWNoZW5kLWV2ZW50LXByb3BlcnRpZXNcbiAgLy8gc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9iZXZhY3F1YS9kcmFndWxhL2lzc3Vlcy8zNFxuICBpZiAoZS50YXJnZXRUb3VjaGVzICYmIGUudGFyZ2V0VG91Y2hlcy5sZW5ndGgpIHtcbiAgICByZXR1cm4gZS50YXJnZXRUb3VjaGVzWzBdO1xuICB9XG4gIGlmIChlLmNoYW5nZWRUb3VjaGVzICYmIGUuY2hhbmdlZFRvdWNoZXMubGVuZ3RoKSB7XG4gICAgcmV0dXJuIGUuY2hhbmdlZFRvdWNoZXNbMF07XG4gIH1cbiAgcmV0dXJuIGU7XG59XG5cbmZ1bmN0aW9uIGdldENvb3JkIChjb29yZCwgZSkge1xuICB2YXIgaG9zdCA9IGdldEV2ZW50SG9zdChlKTtcbiAgdmFyIG1pc3NNYXAgPSB7XG4gICAgcGFnZVg6ICdjbGllbnRYJywgLy8gSUU4XG4gICAgcGFnZVk6ICdjbGllbnRZJyAvLyBJRThcbiAgfTtcbiAgaWYgKGNvb3JkIGluIG1pc3NNYXAgJiYgIShjb29yZCBpbiBob3N0KSAmJiBtaXNzTWFwW2Nvb3JkXSBpbiBob3N0KSB7XG4gICAgY29vcmQgPSBtaXNzTWFwW2Nvb3JkXTtcbiAgfVxuICByZXR1cm4gaG9zdFtjb29yZF07XG59XG5cbm1vZHVsZS5leHBvcnRzID0gZHJhZ3VsYTtcbiIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gYXRvYSAoYSwgbikgeyByZXR1cm4gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYSwgbik7IH1cbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIHRpY2t5ID0gcmVxdWlyZSgndGlja3knKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBkZWJvdW5jZSAoZm4sIGFyZ3MsIGN0eCkge1xuICBpZiAoIWZuKSB7IHJldHVybjsgfVxuICB0aWNreShmdW5jdGlvbiBydW4gKCkge1xuICAgIGZuLmFwcGx5KGN0eCB8fCBudWxsLCBhcmdzIHx8IFtdKTtcbiAgfSk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgYXRvYSA9IHJlcXVpcmUoJ2F0b2EnKTtcbnZhciBkZWJvdW5jZSA9IHJlcXVpcmUoJy4vZGVib3VuY2UnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBlbWl0dGVyICh0aGluZywgb3B0aW9ucykge1xuICB2YXIgb3B0cyA9IG9wdGlvbnMgfHwge307XG4gIHZhciBldnQgPSB7fTtcbiAgaWYgKHRoaW5nID09PSB1bmRlZmluZWQpIHsgdGhpbmcgPSB7fTsgfVxuICB0aGluZy5vbiA9IGZ1bmN0aW9uICh0eXBlLCBmbikge1xuICAgIGlmICghZXZ0W3R5cGVdKSB7XG4gICAgICBldnRbdHlwZV0gPSBbZm5dO1xuICAgIH0gZWxzZSB7XG4gICAgICBldnRbdHlwZV0ucHVzaChmbik7XG4gICAgfVxuICAgIHJldHVybiB0aGluZztcbiAgfTtcbiAgdGhpbmcub25jZSA9IGZ1bmN0aW9uICh0eXBlLCBmbikge1xuICAgIGZuLl9vbmNlID0gdHJ1ZTsgLy8gdGhpbmcub2ZmKGZuKSBzdGlsbCB3b3JrcyFcbiAgICB0aGluZy5vbih0eXBlLCBmbik7XG4gICAgcmV0dXJuIHRoaW5nO1xuICB9O1xuICB0aGluZy5vZmYgPSBmdW5jdGlvbiAodHlwZSwgZm4pIHtcbiAgICB2YXIgYyA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gICAgaWYgKGMgPT09IDEpIHtcbiAgICAgIGRlbGV0ZSBldnRbdHlwZV07XG4gICAgfSBlbHNlIGlmIChjID09PSAwKSB7XG4gICAgICBldnQgPSB7fTtcbiAgICB9IGVsc2Uge1xuICAgICAgdmFyIGV0ID0gZXZ0W3R5cGVdO1xuICAgICAgaWYgKCFldCkgeyByZXR1cm4gdGhpbmc7IH1cbiAgICAgIGV0LnNwbGljZShldC5pbmRleE9mKGZuKSwgMSk7XG4gICAgfVxuICAgIHJldHVybiB0aGluZztcbiAgfTtcbiAgdGhpbmcuZW1pdCA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgYXJncyA9IGF0b2EoYXJndW1lbnRzKTtcbiAgICByZXR1cm4gdGhpbmcuZW1pdHRlclNuYXBzaG90KGFyZ3Muc2hpZnQoKSkuYXBwbHkodGhpcywgYXJncyk7XG4gIH07XG4gIHRoaW5nLmVtaXR0ZXJTbmFwc2hvdCA9IGZ1bmN0aW9uICh0eXBlKSB7XG4gICAgdmFyIGV0ID0gKGV2dFt0eXBlXSB8fCBbXSkuc2xpY2UoMCk7XG4gICAgcmV0dXJuIGZ1bmN0aW9uICgpIHtcbiAgICAgIHZhciBhcmdzID0gYXRvYShhcmd1bWVudHMpO1xuICAgICAgdmFyIGN0eCA9IHRoaXMgfHwgdGhpbmc7XG4gICAgICBpZiAodHlwZSA9PT0gJ2Vycm9yJyAmJiBvcHRzLnRocm93cyAhPT0gZmFsc2UgJiYgIWV0Lmxlbmd0aCkgeyB0aHJvdyBhcmdzLmxlbmd0aCA9PT0gMSA/IGFyZ3NbMF0gOiBhcmdzOyB9XG4gICAgICBldC5mb3JFYWNoKGZ1bmN0aW9uIGVtaXR0ZXIgKGxpc3Rlbikge1xuICAgICAgICBpZiAob3B0cy5hc3luYykgeyBkZWJvdW5jZShsaXN0ZW4sIGFyZ3MsIGN0eCk7IH0gZWxzZSB7IGxpc3Rlbi5hcHBseShjdHgsIGFyZ3MpOyB9XG4gICAgICAgIGlmIChsaXN0ZW4uX29uY2UpIHsgdGhpbmcub2ZmKHR5cGUsIGxpc3Rlbik7IH1cbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHRoaW5nO1xuICAgIH07XG4gIH07XG4gIHJldHVybiB0aGluZztcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBjdXN0b21FdmVudCA9IHJlcXVpcmUoJ2N1c3RvbS1ldmVudCcpO1xudmFyIGV2ZW50bWFwID0gcmVxdWlyZSgnLi9ldmVudG1hcCcpO1xudmFyIGRvYyA9IGdsb2JhbC5kb2N1bWVudDtcbnZhciBhZGRFdmVudCA9IGFkZEV2ZW50RWFzeTtcbnZhciByZW1vdmVFdmVudCA9IHJlbW92ZUV2ZW50RWFzeTtcbnZhciBoYXJkQ2FjaGUgPSBbXTtcblxuaWYgKCFnbG9iYWwuYWRkRXZlbnRMaXN0ZW5lcikge1xuICBhZGRFdmVudCA9IGFkZEV2ZW50SGFyZDtcbiAgcmVtb3ZlRXZlbnQgPSByZW1vdmVFdmVudEhhcmQ7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBhZGQ6IGFkZEV2ZW50LFxuICByZW1vdmU6IHJlbW92ZUV2ZW50LFxuICBmYWJyaWNhdGU6IGZhYnJpY2F0ZUV2ZW50XG59O1xuXG5mdW5jdGlvbiBhZGRFdmVudEVhc3kgKGVsLCB0eXBlLCBmbiwgY2FwdHVyaW5nKSB7XG4gIHJldHVybiBlbC5hZGRFdmVudExpc3RlbmVyKHR5cGUsIGZuLCBjYXB0dXJpbmcpO1xufVxuXG5mdW5jdGlvbiBhZGRFdmVudEhhcmQgKGVsLCB0eXBlLCBmbikge1xuICByZXR1cm4gZWwuYXR0YWNoRXZlbnQoJ29uJyArIHR5cGUsIHdyYXAoZWwsIHR5cGUsIGZuKSk7XG59XG5cbmZ1bmN0aW9uIHJlbW92ZUV2ZW50RWFzeSAoZWwsIHR5cGUsIGZuLCBjYXB0dXJpbmcpIHtcbiAgcmV0dXJuIGVsLnJlbW92ZUV2ZW50TGlzdGVuZXIodHlwZSwgZm4sIGNhcHR1cmluZyk7XG59XG5cbmZ1bmN0aW9uIHJlbW92ZUV2ZW50SGFyZCAoZWwsIHR5cGUsIGZuKSB7XG4gIHZhciBsaXN0ZW5lciA9IHVud3JhcChlbCwgdHlwZSwgZm4pO1xuICBpZiAobGlzdGVuZXIpIHtcbiAgICByZXR1cm4gZWwuZGV0YWNoRXZlbnQoJ29uJyArIHR5cGUsIGxpc3RlbmVyKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBmYWJyaWNhdGVFdmVudCAoZWwsIHR5cGUsIG1vZGVsKSB7XG4gIHZhciBlID0gZXZlbnRtYXAuaW5kZXhPZih0eXBlKSA9PT0gLTEgPyBtYWtlQ3VzdG9tRXZlbnQoKSA6IG1ha2VDbGFzc2ljRXZlbnQoKTtcbiAgaWYgKGVsLmRpc3BhdGNoRXZlbnQpIHtcbiAgICBlbC5kaXNwYXRjaEV2ZW50KGUpO1xuICB9IGVsc2Uge1xuICAgIGVsLmZpcmVFdmVudCgnb24nICsgdHlwZSwgZSk7XG4gIH1cbiAgZnVuY3Rpb24gbWFrZUNsYXNzaWNFdmVudCAoKSB7XG4gICAgdmFyIGU7XG4gICAgaWYgKGRvYy5jcmVhdGVFdmVudCkge1xuICAgICAgZSA9IGRvYy5jcmVhdGVFdmVudCgnRXZlbnQnKTtcbiAgICAgIGUuaW5pdEV2ZW50KHR5cGUsIHRydWUsIHRydWUpO1xuICAgIH0gZWxzZSBpZiAoZG9jLmNyZWF0ZUV2ZW50T2JqZWN0KSB7XG4gICAgICBlID0gZG9jLmNyZWF0ZUV2ZW50T2JqZWN0KCk7XG4gICAgfVxuICAgIHJldHVybiBlO1xuICB9XG4gIGZ1bmN0aW9uIG1ha2VDdXN0b21FdmVudCAoKSB7XG4gICAgcmV0dXJuIG5ldyBjdXN0b21FdmVudCh0eXBlLCB7IGRldGFpbDogbW9kZWwgfSk7XG4gIH1cbn1cblxuZnVuY3Rpb24gd3JhcHBlckZhY3RvcnkgKGVsLCB0eXBlLCBmbikge1xuICByZXR1cm4gZnVuY3Rpb24gd3JhcHBlciAob3JpZ2luYWxFdmVudCkge1xuICAgIHZhciBlID0gb3JpZ2luYWxFdmVudCB8fCBnbG9iYWwuZXZlbnQ7XG4gICAgZS50YXJnZXQgPSBlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQ7XG4gICAgZS5wcmV2ZW50RGVmYXVsdCA9IGUucHJldmVudERlZmF1bHQgfHwgZnVuY3Rpb24gcHJldmVudERlZmF1bHQgKCkgeyBlLnJldHVyblZhbHVlID0gZmFsc2U7IH07XG4gICAgZS5zdG9wUHJvcGFnYXRpb24gPSBlLnN0b3BQcm9wYWdhdGlvbiB8fCBmdW5jdGlvbiBzdG9wUHJvcGFnYXRpb24gKCkgeyBlLmNhbmNlbEJ1YmJsZSA9IHRydWU7IH07XG4gICAgZS53aGljaCA9IGUud2hpY2ggfHwgZS5rZXlDb2RlO1xuICAgIGZuLmNhbGwoZWwsIGUpO1xuICB9O1xufVxuXG5mdW5jdGlvbiB3cmFwIChlbCwgdHlwZSwgZm4pIHtcbiAgdmFyIHdyYXBwZXIgPSB1bndyYXAoZWwsIHR5cGUsIGZuKSB8fCB3cmFwcGVyRmFjdG9yeShlbCwgdHlwZSwgZm4pO1xuICBoYXJkQ2FjaGUucHVzaCh7XG4gICAgd3JhcHBlcjogd3JhcHBlcixcbiAgICBlbGVtZW50OiBlbCxcbiAgICB0eXBlOiB0eXBlLFxuICAgIGZuOiBmblxuICB9KTtcbiAgcmV0dXJuIHdyYXBwZXI7XG59XG5cbmZ1bmN0aW9uIHVud3JhcCAoZWwsIHR5cGUsIGZuKSB7XG4gIHZhciBpID0gZmluZChlbCwgdHlwZSwgZm4pO1xuICBpZiAoaSkge1xuICAgIHZhciB3cmFwcGVyID0gaGFyZENhY2hlW2ldLndyYXBwZXI7XG4gICAgaGFyZENhY2hlLnNwbGljZShpLCAxKTsgLy8gZnJlZSB1cCBhIHRhZCBvZiBtZW1vcnlcbiAgICByZXR1cm4gd3JhcHBlcjtcbiAgfVxufVxuXG5mdW5jdGlvbiBmaW5kIChlbCwgdHlwZSwgZm4pIHtcbiAgdmFyIGksIGl0ZW07XG4gIGZvciAoaSA9IDA7IGkgPCBoYXJkQ2FjaGUubGVuZ3RoOyBpKyspIHtcbiAgICBpdGVtID0gaGFyZENhY2hlW2ldO1xuICAgIGlmIChpdGVtLmVsZW1lbnQgPT09IGVsICYmIGl0ZW0udHlwZSA9PT0gdHlwZSAmJiBpdGVtLmZuID09PSBmbikge1xuICAgICAgcmV0dXJuIGk7XG4gICAgfVxuICB9XG59XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBldmVudG1hcCA9IFtdO1xudmFyIGV2ZW50bmFtZSA9ICcnO1xudmFyIHJvbiA9IC9eb24vO1xuXG5mb3IgKGV2ZW50bmFtZSBpbiBnbG9iYWwpIHtcbiAgaWYgKHJvbi50ZXN0KGV2ZW50bmFtZSkpIHtcbiAgICBldmVudG1hcC5wdXNoKGV2ZW50bmFtZS5zbGljZSgyKSk7XG4gIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBldmVudG1hcDtcbiIsIlxudmFyIE5hdGl2ZUN1c3RvbUV2ZW50ID0gZ2xvYmFsLkN1c3RvbUV2ZW50O1xuXG5mdW5jdGlvbiB1c2VOYXRpdmUgKCkge1xuICB0cnkge1xuICAgIHZhciBwID0gbmV3IE5hdGl2ZUN1c3RvbUV2ZW50KCdjYXQnLCB7IGRldGFpbDogeyBmb286ICdiYXInIH0gfSk7XG4gICAgcmV0dXJuICAnY2F0JyA9PT0gcC50eXBlICYmICdiYXInID09PSBwLmRldGFpbC5mb287XG4gIH0gY2F0Y2ggKGUpIHtcbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG5cbi8qKlxuICogQ3Jvc3MtYnJvd3NlciBgQ3VzdG9tRXZlbnRgIGNvbnN0cnVjdG9yLlxuICpcbiAqIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9DdXN0b21FdmVudC5DdXN0b21FdmVudFxuICpcbiAqIEBwdWJsaWNcbiAqL1xuXG5tb2R1bGUuZXhwb3J0cyA9IHVzZU5hdGl2ZSgpID8gTmF0aXZlQ3VzdG9tRXZlbnQgOlxuXG4vLyBJRSA+PSA5XG4nZnVuY3Rpb24nID09PSB0eXBlb2YgZG9jdW1lbnQuY3JlYXRlRXZlbnQgPyBmdW5jdGlvbiBDdXN0b21FdmVudCAodHlwZSwgcGFyYW1zKSB7XG4gIHZhciBlID0gZG9jdW1lbnQuY3JlYXRlRXZlbnQoJ0N1c3RvbUV2ZW50Jyk7XG4gIGlmIChwYXJhbXMpIHtcbiAgICBlLmluaXRDdXN0b21FdmVudCh0eXBlLCBwYXJhbXMuYnViYmxlcywgcGFyYW1zLmNhbmNlbGFibGUsIHBhcmFtcy5kZXRhaWwpO1xuICB9IGVsc2Uge1xuICAgIGUuaW5pdEN1c3RvbUV2ZW50KHR5cGUsIGZhbHNlLCBmYWxzZSwgdm9pZCAwKTtcbiAgfVxuICByZXR1cm4gZTtcbn0gOlxuXG4vLyBJRSA8PSA4XG5mdW5jdGlvbiBDdXN0b21FdmVudCAodHlwZSwgcGFyYW1zKSB7XG4gIHZhciBlID0gZG9jdW1lbnQuY3JlYXRlRXZlbnRPYmplY3QoKTtcbiAgZS50eXBlID0gdHlwZTtcbiAgaWYgKHBhcmFtcykge1xuICAgIGUuYnViYmxlcyA9IEJvb2xlYW4ocGFyYW1zLmJ1YmJsZXMpO1xuICAgIGUuY2FuY2VsYWJsZSA9IEJvb2xlYW4ocGFyYW1zLmNhbmNlbGFibGUpO1xuICAgIGUuZGV0YWlsID0gcGFyYW1zLmRldGFpbDtcbiAgfSBlbHNlIHtcbiAgICBlLmJ1YmJsZXMgPSBmYWxzZTtcbiAgICBlLmNhbmNlbGFibGUgPSBmYWxzZTtcbiAgICBlLmRldGFpbCA9IHZvaWQgMDtcbiAgfVxuICByZXR1cm4gZTtcbn1cbiIsInZhciBzaSA9IHR5cGVvZiBzZXRJbW1lZGlhdGUgPT09ICdmdW5jdGlvbicsIHRpY2s7XG5pZiAoc2kpIHtcbiAgdGljayA9IGZ1bmN0aW9uIChmbikgeyBzZXRJbW1lZGlhdGUoZm4pOyB9O1xufSBlbHNlIHtcbiAgdGljayA9IGZ1bmN0aW9uIChmbikgeyBzZXRUaW1lb3V0KGZuLCAwKTsgfTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0aWNrOyJdfQ==
diff --git a/vendor/assets/editormd/css/editormd.min.css b/vendor/assets/editormd/css/editormd.min.css
new file mode 100755
index 000000000..5ebd91de2
--- /dev/null
+++ b/vendor/assets/editormd/css/editormd.min.css
@@ -0,0 +1,5 @@
+/*! Editor.md v1.5.0 | editormd.min.css | Open source online markdown editor. | MIT License | By: Pandao | https://github.com/pandao/editor.md | 2015-06-09 */
+@charset "UTF-8";/*! prefixes.scss v0.1.0 | Author: Pandao | https://github.com/pandao/prefixes.scss | MIT license | Copyright (c) 2015 */.fa-ul,.markdown-body .task-list-item,li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}.editormd-form br,.markdown-body hr:after{clear:both}.editormd{width:90%;height:640px;margin:0 auto 15px;text-align:left;overflow:hidden;position:relative;border:1px solid #ddd;font-family:"Meiryo UI","Microsoft YaHei","Malgun Gothic","Segoe UI","Trebuchet MS",Helvetica,Monaco,monospace,Tahoma,STXihei,"华文细黑",STHeiti,"Helvetica Neue","Droid Sans","wenquanyi micro hei",FreeSans,Arimo,Arial,SimSun,"宋体",Heiti,"黑体",sans-serif}.editormd *,.editormd :after,.editormd :before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.editormd a{text-decoration:none}.editormd img{border:none;vertical-align:middle}.editormd .editormd-html-textarea,.editormd .editormd-markdown-textarea,.editormd>textarea{width:0;height:0;outline:0;resize:none}.editormd .editormd-html-textarea,.editormd .editormd-markdown-textarea{display:none}.editormd button,.editormd input[type=text],.editormd input[type=button],.editormd input[type=submit],.editormd select,.editormd textarea{-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;appearance:none}.editormd ::-webkit-scrollbar{height:10px;width:7px;background:rgba(0,0,0,.1)}.editormd ::-webkit-scrollbar:hover{background:rgba(0,0,0,.2)}.editormd ::-webkit-scrollbar-thumb{background:rgba(0,0,0,.3);-webkit-border-radius:6px;-moz-border-radius:6px;-ms-border-radius:6px;-o-border-radius:6px;border-radius:6px}.editormd ::-webkit-scrollbar-thumb:hover{-webkit-box-shadow:inset 1px 1px 1px rgba(0,0,0,.25);-moz-box-shadow:inset 1px 1px 1px rgba(0,0,0,.25);-ms-box-shadow:inset 1px 1px 1px rgba(0,0,0,.25);-o-box-shadow:inset 1px 1px 1px rgba(0,0,0,.25);box-shadow:inset 1px 1px 1px rgba(0,0,0,.25);background-color:rgba(0,0,0,.4)}.editormd-user-unselect{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.editormd-toolbar{width:100%;min-height:37px;background:#fff;display:none;position:absolute;top:0;left:0;z-index:10;border-bottom:1px solid #ddd}.editormd-toolbar-container{padding:0 8px;min-height:35px;-o-user-select:none;user-select:none}.editormd-toolbar-container,.markdown-body .octicon{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.editormd-menu,.markdown-body ol,.markdown-body td,.markdown-body th,.markdown-body ul{padding:0}.editormd-menu{margin:0;list-style:none}.editormd-menu>li{margin:0;padding:5px 1px;display:inline-block;position:relative}.editormd-menu>li.divider{display:inline-block;text-indent:-9999px;margin:0 5px;height:65%;border-right:1px solid #ddd}.editormd-menu>li>a{outline:0;color:#666;display:inline-block;min-width:24px;font-size:16px;text-decoration:none;text-align:center;-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;-o-border-radius:2px;border-radius:2px;border:1px solid #fff;transition:all 300ms ease-out}.editormd-dropdown-menu>li>a:hover,.editormd-menu>li>a{-webkit-transition:all 300ms ease-out;-moz-transition:all 300ms ease-out}.editormd-menu>li>a.active,.editormd-menu>li>a:hover{border:1px solid #ddd;background:#eee}.editormd-menu>li>a>.fa{text-align:center;display:block;padding:5px}.editormd-menu>li>a>.editormd-bold{padding:5px 2px;display:inline-block;font-weight:700}.editormd-menu>li:hover .editormd-dropdown-menu{display:block}.editormd-menu>li+li>a{margin-left:3px}.editormd-dropdown-menu{display:none;background:#fff;border:1px solid #ddd;width:148px;list-style:none;position:absolute;top:33px;left:0;z-index:100;-webkit-box-shadow:1px 2px 6px rgba(0,0,0,.15);-moz-box-shadow:1px 2px 6px rgba(0,0,0,.15);-ms-box-shadow:1px 2px 6px rgba(0,0,0,.15);-o-box-shadow:1px 2px 6px rgba(0,0,0,.15);box-shadow:1px 2px 6px rgba(0,0,0,.15)}.editormd-dropdown-menu:after,.editormd-dropdown-menu:before{width:0;height:0;display:block;content:"";position:absolute;top:-11px;left:8px;border:5px solid transparent}.editormd-dropdown-menu:before{border-bottom-color:#ccc}.editormd-dropdown-menu:after{border-bottom-color:#fff;top:-10px}.editormd-dropdown-menu>li>a{color:#666;display:block;text-decoration:none;padding:8px 10px}.editormd-dropdown-menu>li>a:hover{background:#f6f6f6;transition:all 300ms ease-out}.editormd-dropdown-menu>li+li{border-top:1px solid #ddd}.editormd-container{margin:0;width:100%;height:100%;overflow:hidden;padding:35px 0 0;position:relative;background:#fff;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.editormd-dialog{color:#666;position:fixed;z-index:99999;display:none;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 0 10px rgba(0,0,0,.3);-moz-box-shadow:0 0 10px rgba(0,0,0,.3);-ms-box-shadow:0 0 10px rgba(0,0,0,.3);-o-box-shadow:0 0 10px rgba(0,0,0,.3);box-shadow:0 0 10px rgba(0,0,0,.3);background:#fff;font-size:14px}.editormd-dialog-container{position:relative;padding:20px;line-height:1.4}.editormd-dialog-container h1{font-size:24px;margin-bottom:10px}.editormd-dialog-container h1 .fa{color:#2C7EEA;padding-right:5px}.editormd-dialog-container h1 small{padding-left:5px;font-weight:400;font-size:12px;color:#999}.editormd-dialog-container select{color:#999;padding:3px 8px;border:1px solid #ddd}.editormd-dialog-close{position:absolute;top:12px;right:15px;font-size:18px;color:#ccc;-webkit-transition:color 300ms ease-out;-moz-transition:color 300ms ease-out;transition:color 300ms ease-out}.editormd-dialog-close:hover{color:#999}.editormd-dialog-header{padding:11px 20px;border-bottom:1px solid #eee;-webkit-transition:background 300ms ease-out;-moz-transition:background 300ms ease-out;transition:background 300ms ease-out}.editormd-dialog-header:hover{background:#f6f6f6}.editormd-dialog-title{font-size:14px}.editormd-dialog-footer{padding:10px 0 0;text-align:right}.editormd-dialog-info{width:420px}.editormd-dialog-info h1{font-weight:400}.editormd-dialog-info .editormd-dialog-container{padding:20px 25px 25px}.editormd-dialog-info .editormd-dialog-close{top:10px;right:10px}.editormd-dialog-info .hover-link:hover,.editormd-dialog-info p>a{color:#2196F3}.editormd-dialog-info .hover-link{color:#666}.editormd-dialog-info a .fa-external-link{display:none}.editormd-dialog-info a:hover{color:#2196F3}.editormd-dialog-info a:hover .fa-external-link{display:inline-block}.editormd-container-mask,.editormd-dialog-mask,.editormd-mask{display:none;width:100%;height:100%;position:absolute;top:0;left:0}.editormd-dialog-mask-bg,.editormd-mask{background:#fff;opacity:.5;filter:alpha(opacity=50)}.editormd-mask{position:fixed;background:#000;opacity:.2;filter:alpha(opacity=20);z-index:99998}.editormd-container-mask,.editormd-dialog-mask-con{background:url(../images/loading.gif)center center no-repeat;-webkit-background-size:32px 32px;-moz-background-size:32px 32px;-o-background-size:32px 32px;background-size:32px 32px}.editormd-container-mask{z-index:20;display:block;background-color:#fff}@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2){.editormd-container-mask,.editormd-dialog-mask-con{background-image:url(../images/loading@2x.gif)}}@media only screen and (-webkit-min-device-pixel-ratio:3),only screen and (min-device-pixel-ratio:3){.editormd-container-mask,.editormd-dialog-mask-con{background-image:url(../images/loading@3x.gif)}}.editormd-code-block-dialog textarea,.editormd-preformatted-text-dialog textarea{width:100%;height:400px;margin-bottom:6px;overflow:auto;border:1px solid #eee;background:#fff;padding:15px;resize:none}.editormd-code-toolbar{color:#999;font-size:14px;margin:-5px 0 10px}.editormd-grid-table{width:99%;display:table;border:1px solid #ddd;border-collapse:collapse}.editormd-grid-table-row{width:100%;display:table-row}.editormd-grid-table-row a{font-size:1.4em;width:5%;height:36px;color:#999;text-align:center;display:table-cell;vertical-align:middle;border:1px solid #ddd;text-decoration:none;-webkit-transition:background-color 300ms ease-out,color 100ms ease-in;-moz-transition:background-color 300ms ease-out,color 100ms ease-in;transition:background-color 300ms ease-out,color 100ms ease-in}.editormd-grid-table-row a.selected{color:#666;background-color:#eee}.editormd-grid-table-row a:hover{color:#777;background-color:#f6f6f6}.editormd-tab-head{list-style:none;border-bottom:1px solid #ddd}.editormd-tab-head li{display:inline-block}.editormd-tab-head li a{color:#999;display:block;padding:6px 12px 5px;text-align:center;text-decoration:none;margin-bottom:-1px;border:1px solid #ddd;-webkit-border-top-left-radius:3px;-moz-border-top-left-radius:3px;-ms-border-top-left-radius:3px;-o-border-top-left-radius:3px;border-top-left-radius:3px;-webkit-border-top-right-radius:3px;-moz-border-top-right-radius:3px;-ms-border-top-right-radius:3px;-o-border-top-right-radius:3px;border-top-right-radius:3px;background:#f6f6f6;-webkit-transition:all 300ms ease-out;-moz-transition:all 300ms ease-out;transition:all 300ms ease-out}.editormd-tab-head li a:hover{color:#666;background:#eee}.editormd-tab-head li.active a{color:#666;background:#fff;border-bottom-color:#fff}.editormd-tab-head li+li{margin-left:3px}.editormd-tab-box{padding:20px 0}.editormd-form{color:#666}.editormd-form label{float:left;display:block;width:75px;text-align:left;padding:7px 0 15px 5px;margin:0 0 2px;font-weight:400}.editormd-form iframe{display:none}.editormd-form input:focus{outline:0}.editormd-form input[type=text],.editormd-form input[type=number]{color:#999;padding:8px;border:1px solid #ddd}.editormd-form input[type=number]{width:40px;display:inline-block;padding:6px 8px}.editormd-form input[type=text]{display:inline-block;width:264px}.editormd-form .fa-btns{display:inline-block}.editormd-form .fa-btns a{color:#999;padding:7px 10px 0 0;display:inline-block;text-decoration:none;text-align:center}.editormd-form .fa-btns .fa{font-size:1.3em}.editormd-form .fa-btns label{float:none;display:inline-block;width:auto;text-align:left;padding:0 0 0 5px;cursor:pointer}.fa-fw,.fa-li{text-align:center}.editormd-dialog-container .editormd-btn,.editormd-dialog-container button,.editormd-dialog-container input[type=submit],.editormd-dialog-footer .editormd-btn,.editormd-dialog-footer button,.editormd-dialog-footer input[type=submit],.editormd-form .editormd-btn,.editormd-form button,.editormd-form input[type=submit]{color:#666;min-width:75px;cursor:pointer;background:#fff;padding:7px 10px;border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;-webkit-transition:background 300ms ease-out;-moz-transition:background 300ms ease-out;transition:background 300ms ease-out}.editormd-dialog-container .editormd-btn:hover,.editormd-dialog-container button:hover,.editormd-dialog-container input[type=submit]:hover,.editormd-dialog-footer .editormd-btn:hover,.editormd-dialog-footer button:hover,.editormd-dialog-footer input[type=submit]:hover,.editormd-form .editormd-btn:hover,.editormd-form button:hover,.editormd-form input[type=submit]:hover{background:#eee}.editormd-dialog-container .editormd-btn+.editormd-btn,.editormd-dialog-footer .editormd-btn+.editormd-btn,.editormd-form .editormd-btn+.editormd-btn{margin-left:8px}.editormd-file-input{width:75px;height:32px;margin-left:8px;position:relative;display:inline-block}.editormd-file-input input[type=file]{width:75px;height:32px;opacity:0;cursor:pointer;background:#000;display:inline-block;position:absolute;top:0;right:0}.editormd-file-input input[type=file]::-webkit-file-upload-button{visibility:hidden}.editormd-file-input:hover input[type=submit]{background:#eee}.editormd .CodeMirror,.editormd-preview{display:inline-block;width:50%;height:100%;vertical-align:top;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0}.editormd-preview{position:absolute;top:35px;right:0;overflow:auto;line-height:1.6;display:none;background:#fff}.fa,.fa-stack{display:inline-block}.editormd .CodeMirror{z-index:10;float:left;border-right:1px solid #ddd;font-size:14px;font-family:"YaHei Consolas Hybrid",Consolas,"微软雅黑","Meiryo UI","Malgun Gothic","Segoe UI","Trebuchet MS",Helvetica,Monaco,courier,monospace;line-height:1.6;margin-top:35px}.editormd .CodeMirror pre{font-size:14px;padding:0 12px}.editormd .CodeMirror-linenumbers{padding:0 5px}.editormd .CodeMirror-focused .CodeMirror-selected,.editormd .CodeMirror-selected{background:#70B7FF}.editormd .CodeMirror,.editormd .CodeMirror-scroll,.editormd .editormd-preview{-webkit-overflow-scrolling:touch}.editormd .styled-background{background-color:#ff7}.editormd .CodeMirror-focused .cm-matchhighlight{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);background-position:bottom;background-repeat:repeat-x}.editormd .CodeMirror-empty.CodeMirror-focused{outline:0}.editormd .CodeMirror pre.CodeMirror-placeholder{color:#999}.editormd .cm-trailingspace{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QUXCToH00Y1UgAAACFJREFUCNdjPMDBUc/AwNDAAAFMTAwMDA0OP34wQgX/AQBYgwYEx4f9lQAAAABJRU5ErkJggg==);background-position:bottom left;background-repeat:repeat-x}.editormd .cm-tab{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAGFJREFUSMft1LsRQFAQheHPowAKoACx3IgEKtaEHujDjORSgWTH/ZOdnZOcM/sgk/kFFWY0qV8foQwS4MKBCS3qR6ixBJvElOobYAtivseIE120FaowJPN75GMu8j/LfMwNjh4HUpwg4LUAAAAASUVORK5CYII=)right no-repeat}/*! prefixes.scss v0.1.0 | Author: Pandao | https://github.com/pandao/prefixes.scss | MIT license | Copyright (c) 2015 *//*!
+ * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */@font-face{font-family:FontAwesome;src:url(../fonts/fontawesome-webfont.eot?v=4.3.0);src:url(../fonts/fontawesome-webfont.eot?#iefix&v=4.3.0)format("embedded-opentype"),url(../fonts/fontawesome-webfont.woff2?v=4.3.0)format("woff2"),url(../fonts/fontawesome-webfont.woff?v=4.3.0)format("woff"),url(../fonts/fontawesome-webfont.ttf?v=4.3.0)format("truetype"),url(../fonts/fontawesome-webfont.svg?v=4.3.0#fontawesomeregular)format("svg");font-weight:400;font-style:normal}.fa{font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0,0)}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em}.fa-ul{padding-left:0;margin-left:2.14285714em}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{filter:none}.fa-stack{position:relative;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-repeat:before,.fa-rotate-right:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-exclamation-triangle:before,.fa-warning:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-floppy-o:before,.fa-save:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-bolt:before,.fa-flash:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-chain-broken:before,.fa-unlink:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"\f150"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"\f151"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"\f152"}.fa-eur:before,.fa-euro:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-inr:before,.fa-rupee:before{content:"\f156"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"\f158"}.fa-krw:before,.fa-won:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-try:before,.fa-turkish-lira:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"\f19c"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"\f1c5"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"\f1c6"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-empire:before,.fa-ge:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-paper-plane:before,.fa-send:before{content:"\f1d8"}.fa-paper-plane-o:before,.fa-send-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before,.fa-genderless:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-bed:before,.fa-hotel:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}/*! prefixes.scss v0.1.0 | Author: Pandao | https://github.com/pandao/prefixes.scss | MIT license | Copyright (c) 2015 */@font-face{font-family:editormd-logo;src:url(../fonts/editormd-logo.eot?-5y8q6h);src:url(.../fonts/editormd-logo.eot?#iefix-5y8q6h)format("embedded-opentype"),url(../fonts/editormd-logo.woff?-5y8q6h)format("woff"),url(../fonts/editormd-logo.ttf?-5y8q6h)format("truetype"),url(../fonts/editormd-logo.svg?-5y8q6h#icomoon)format("svg");font-weight:400;font-style:normal}.editormd-logo,.editormd-logo-1x,.editormd-logo-2x,.editormd-logo-3x,.editormd-logo-4x,.editormd-logo-5x,.editormd-logo-6x,.editormd-logo-7x,.editormd-logo-8x{font-family:editormd-logo;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;font-size:inherit;line-height:1;display:inline-block;text-rendering:auto;vertical-align:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.markdown-body hr:after,.markdown-body hr:before{content:"";display:table}.editormd-logo-1x:before,.editormd-logo-2x:before,.editormd-logo-3x:before,.editormd-logo-4x:before,.editormd-logo-5x:before,.editormd-logo-6x:before,.editormd-logo-7x:before,.editormd-logo-8x:before,.editormd-logo:before{content:"\e1987"}.editormd-logo-1x{font-size:1em}.editormd-logo-lg{font-size:1.2em}.editormd-logo-2x{font-size:2em}.editormd-logo-3x{font-size:3em}.editormd-logo-4x{font-size:4em}.editormd-logo-5x{font-size:5em}.editormd-logo-6x{font-size:6em}.editormd-logo-7x{font-size:7em}.editormd-logo-8x{font-size:8em}.editormd-logo-color{color:#2196F3}/*! github-markdown-css | The MIT License (MIT) | Copyright (c) Sindre Sorhus (sindresorhus.com) | https://github.com/sindresorhus/github-markdown-css */@font-face{font-family:octicons-anchor;src:url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABMAAAABwAAAAca8vGTk9TLzIAAAFMAAAARAAAAFZG1VHVY21hcAAAAZAAAAA+AAABQgAP9AdjdnQgAAAB0AAAAAQAAAAEACICiGdhc3AAAAHUAAAACAAAAAj//wADZ2x5ZgAAAdwAAADRAAABEKyikaNoZWFkAAACsAAAAC0AAAA2AtXoA2hoZWEAAALgAAAAHAAAACQHngNFaG10eAAAAvwAAAAQAAAAEAwAACJsb2NhAAADDAAAAAoAAAAKALIAVG1heHAAAAMYAAAAHwAAACABEAB2bmFtZQAAAzgAAALBAAAFu3I9x/Nwb3N0AAAF/AAAAB0AAAAvaoFvbwAAAAEAAAAAzBdyYwAAAADP2IQvAAAAAM/bz7t4nGNgZGFgnMDAysDB1Ml0hoGBoR9CM75mMGLkYGBgYmBlZsAKAtJcUxgcPsR8iGF2+O/AEMPsznAYKMwIkgMA5REMOXicY2BgYGaAYBkGRgYQsAHyGMF8FgYFIM0ChED+h5j//yEk/3KoSgZGNgYYk4GRCUgwMaACRoZhDwCs7QgGAAAAIgKIAAAAAf//AAJ4nHWMMQrCQBBF/0zWrCCIKUQsTDCL2EXMohYGSSmorScInsRGL2DOYJe0Ntp7BK+gJ1BxF1stZvjz/v8DRghQzEc4kIgKwiAppcA9LtzKLSkdNhKFY3HF4lK69ExKslx7Xa+vPRVS43G98vG1DnkDMIBUgFN0MDXflU8tbaZOUkXUH0+U27RoRpOIyCKjbMCVejwypzJJG4jIwb43rfl6wbwanocrJm9XFYfskuVC5K/TPyczNU7b84CXcbxks1Un6H6tLH9vf2LRnn8Ax7A5WQAAAHicY2BkYGAA4teL1+yI57f5ysDNwgAC529f0kOmWRiYVgEpDgYmEA8AUzEKsQAAAHicY2BkYGB2+O/AEMPCAAJAkpEBFbAAADgKAe0EAAAiAAAAAAQAAAAEAAAAAAAAKgAqACoAiAAAeJxjYGRgYGBhsGFgYgABEMkFhAwM/xn0QAIAD6YBhwB4nI1Ty07cMBS9QwKlQapQW3VXySvEqDCZGbGaHULiIQ1FKgjWMxknMfLEke2A+IJu+wntrt/QbVf9gG75jK577Lg8K1qQPCfnnnt8fX1NRC/pmjrk/zprC+8D7tBy9DHgBXoWfQ44Av8t4Bj4Z8CLtBL9CniJluPXASf0Lm4CXqFX8Q84dOLnMB17N4c7tBo1AS/Qi+hTwBH4rwHHwN8DXqQ30XXAS7QaLwSc0Gn8NuAVWou/gFmnjLrEaEh9GmDdDGgL3B4JsrRPDU2hTOiMSuJUIdKQQayiAth69r6akSSFqIJuA19TrzCIaY8sIoxyrNIrL//pw7A2iMygkX5vDj+G+kuoLdX4GlGK/8Lnlz6/h9MpmoO9rafrz7ILXEHHaAx95s9lsI7AHNMBWEZHULnfAXwG9/ZqdzLI08iuwRloXE8kfhXYAvE23+23DU3t626rbs8/8adv+9DWknsHp3E17oCf+Z48rvEQNZ78paYM38qfk3v/u3l3u3GXN2Dmvmvpf1Srwk3pB/VSsp512bA/GG5i2WJ7wu430yQ5K3nFGiOqgtmSB5pJVSizwaacmUZzZhXLlZTq8qGGFY2YcSkqbth6aW1tRmlaCFs2016m5qn36SbJrqosG4uMV4aP2PHBmB3tjtmgN2izkGQyLWprekbIntJFing32a5rKWCN/SdSoga45EJykyQ7asZvHQ8PTm6cslIpwyeyjbVltNikc2HTR7YKh9LBl9DADC0U/jLcBZDKrMhUBfQBvXRzLtFtjU9eNHKin0x5InTqb8lNpfKv1s1xHzTXRqgKzek/mb7nB8RZTCDhGEX3kK/8Q75AmUM/eLkfA+0Hi908Kx4eNsMgudg5GLdRD7a84npi+YxNr5i5KIbW5izXas7cHXIMAau1OueZhfj+cOcP3P8MNIWLyYOBuxL6DRylJ4cAAAB4nGNgYoAALjDJyIAOWMCiTIxMLDmZedkABtIBygAAAA==)format("woff")}.markdown-body{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;color:#333;overflow:hidden;font-family:"Microsoft YaHei",Helvetica,"Meiryo UI","Malgun Gothic","Segoe UI","Trebuchet MS",Monaco,monospace,Tahoma,STXihei,"华文细黑",STHeiti,"Helvetica Neue","Droid Sans","wenquanyi micro hei",FreeSans,Arimo,Arial,SimSun,"宋体",Heiti,"黑体",sans-serif;font-size:16px;line-height:1.6;word-wrap:break-word}.markdown-body strong{font-weight:700}.markdown-body h1{margin:.67em 0}.markdown-body img{border:0}.markdown-body hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}.markdown-body input{color:inherit;margin:0;line-height:normal;font:13px/1.4 Helvetica,arial,freesans,clean,sans-serif,"Segoe UI Emoji","Segoe UI Symbol"}.markdown-body html input[disabled]{cursor:default}.markdown-body input[type=checkbox]{-moz-box-sizing:border-box;box-sizing:border-box;padding:0}.markdown-body *{-moz-box-sizing:border-box;box-sizing:border-box}.markdown-body a{background:0 0;color:#4183c4;text-decoration:none}.markdown-body a:active,.markdown-body a:hover{outline:0;text-decoration:underline}.markdown-body hr{margin:15px 0;overflow:hidden;background:0 0;border:0;border-bottom:1px solid #ddd}.markdown-body h1,.markdown-body h2{padding-bottom:.3em;border-bottom:1px solid #eee}.markdown-body blockquote{margin:0}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ol ol ol,.markdown-body ol ul ol,.markdown-body ul ol ol,.markdown-body ul ul ol{list-style-type:lower-alpha}.markdown-body dd{margin-left:0}.markdown-body code{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace}.markdown-body pre{font:12px Consolas,"Liberation Mono",Menlo,Courier,monospace;word-wrap:normal}.markdown-body .octicon{font:normal normal 16px octicons-anchor;line-height:1;display:inline-block;text-decoration:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;user-select:none}.markdown-body .octicon-link:before{content:'\f05c'}.markdown-body>:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdown-body .anchor{position:absolute;top:0;left:0;display:block;padding-right:6px;padding-left:30px;margin-left:-30px}.markdown-body .anchor:focus{outline:0}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{position:relative;margin-top:1em;margin-bottom:16px;font-weight:700;line-height:1.4}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{display:none;color:#000;vertical-align:middle}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{padding-left:8px;margin-left:-30px;text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{display:inline-block}.markdown-body h1{font-size:2.25em;line-height:1.2}.markdown-body h1 .anchor{line-height:1}.markdown-body h2{font-size:1.75em;line-height:1.225}.markdown-body h2 .anchor{line-height:1}.markdown-body h3{font-size:1.5em;line-height:1.43}.markdown-body h3 .anchor,.markdown-body h4 .anchor{line-height:1.2}.markdown-body h4{font-size:1.25em}.markdown-body h5 .anchor,.markdown-body h6 .anchor{line-height:1.1}.markdown-body h5{font-size:1em}.markdown-body h6{font-size:1em;color:#777}.markdown-body blockquote,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-top:0;margin-bottom:16px}.markdown-body ol,.markdown-body ul{padding-left:2em}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:0;margin-bottom:0}.markdown-body li>p{margin-top:16px}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:700}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body blockquote{padding:0 15px;color:#777;border-left:4px solid #ddd}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body table{border-collapse:collapse;border-spacing:0;display:block;width:100%;overflow:auto;word-break:normal;word-break:keep-all}.markdown-body table th{font-weight:700}.markdown-body table td,.markdown-body table th{padding:6px 13px;border:1px solid #ddd}.markdown-body table tr{background-color:#fff;border-top:1px solid #ccc}.markdown-body table tr:nth-child(2n){background-color:#f8f8f8}.markdown-body img{max-width:100%;-moz-box-sizing:border-box;box-sizing:border-box}.markdown-body code{padding:.2em 0;margin:0;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px}.markdown-body code:after,.markdown-body code:before{letter-spacing:-.2em;content:"\00a0"}.markdown-body pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:0 0;border:0}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre,.markdown-body pre{padding:16px;overflow:auto;font-size:85%;background-color:#f7f7f7;border-radius:3px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body pre code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body pre code:after,.markdown-body pre code:before{content:normal}.markdown-body .pl-c{color:#969896}.markdown-body .pl-c1,.markdown-body .pl-mdh,.markdown-body .pl-mm,.markdown-body .pl-mp,.markdown-body .pl-mr,.markdown-body .pl-s1 .pl-v,.markdown-body .pl-s3,.markdown-body .pl-sc,.markdown-body .pl-sv{color:#0086b3}.markdown-body .pl-e,.markdown-body .pl-en{color:#795da3}.markdown-body .pl-s1 .pl-s2,.markdown-body .pl-smi,.markdown-body .pl-smp,.markdown-body .pl-stj,.markdown-body .pl-vo,.markdown-body .pl-vpf{color:#333}.markdown-body .pl-ent{color:#63a35c}.markdown-body .pl-k,.markdown-body .pl-s,.markdown-body .pl-st{color:#a71d5d}.markdown-body .pl-pds,.markdown-body .pl-s1,.markdown-body .pl-s1 .pl-pse .pl-s2,.markdown-body .pl-sr,.markdown-body .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sra,.markdown-body .pl-sr .pl-sre,.markdown-body .pl-src{color:#df5000}.markdown-body .pl-mo,.markdown-body .pl-v{color:#1d3e81}.markdown-body .pl-id{color:#b52a1d}.markdown-body .pl-ii{background-color:#b52a1d;color:#f8f8f8}.markdown-body .pl-sr .pl-cce{color:#63a35c;font-weight:700}.markdown-body .pl-ml{color:#693a17}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body .pl-ms{color:#1d3e81;font-weight:700}.markdown-body .pl-mq{color:teal}.markdown-body .pl-mi{color:#333;font-style:italic}.markdown-body .pl-mb{color:#333;font-weight:700}.markdown-body .pl-md,.markdown-body .pl-mdhf{background-color:#ffecec;color:#bd2c00}.markdown-body .pl-mdht,.markdown-body .pl-mi1{background-color:#eaffea;color:#55a532}.markdown-body .pl-mdr{color:#795da3;font-weight:700}.markdown-body kbd{display:inline-block;padding:3px 5px;font:11px Consolas,"Liberation Mono",Menlo,Courier,monospace;line-height:10px;color:#555;vertical-align:middle;background-color:#fcfcfc;border:1px solid #ccc;border-bottom-color:#bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb}.markdown-body .task-list-item+.task-list-item{margin-top:3px}.markdown-body .task-list-item input{float:left;margin:.3em 0 .25em -1.6em;vertical-align:middle}.markdown-body :checked+.radio-label{z-index:1;position:relative;border-color:#4183c4}.editormd-html-preview,.editormd-preview-container{text-align:left;font-size:14px;line-height:1.6;padding:20px;overflow:auto;width:100%;background-color:#fff}.editormd-html-preview blockquote,.editormd-preview-container blockquote{color:#666;border-left:4px solid #ddd;padding-left:20px;margin-left:0;font-size:14px;font-style:italic}.editormd-html-preview p code,.editormd-preview-container p code{margin-left:5px;margin-right:4px}.editormd-html-preview abbr,.editormd-preview-container abbr{background:#ffd}.editormd-html-preview hr,.editormd-preview-container hr{height:1px;border:none;border-top:1px solid #ddd;background:0 0}.editormd-html-preview code,.editormd-preview-container code{border:1px solid #ddd;background:#f6f6f6;padding:3px;border-radius:3px;font-size:14px}.editormd-html-preview pre,.editormd-preview-container pre{border:1px solid #ddd;background:#f6f6f6;padding:10px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px}.editormd-html-preview pre code,.editormd-preview-container pre code{padding:0}.editormd-html-preview code,.editormd-html-preview kbd,.editormd-html-preview pre,.editormd-preview-container code,.editormd-preview-container kbd,.editormd-preview-container pre{font-family:"YaHei Consolas Hybrid",Consolas,"Meiryo UI","Malgun Gothic","Segoe UI","Trebuchet MS",Helvetica,monospace,monospace}.editormd-html-preview table thead tr,.editormd-preview-container table thead tr{background-color:#F8F8F8}.editormd-html-preview p.editormd-tex,.editormd-preview-container p.editormd-tex{text-align:center}.editormd-html-preview span.editormd-tex,.editormd-preview-container span.editormd-tex{margin:0 5px}.editormd-html-preview .emoji,.editormd-preview-container .emoji{width:24px;height:24px}.editormd-html-preview .katex,.editormd-preview-container .katex{font-size:1.4em}.editormd-html-preview .flowchart,.editormd-html-preview .sequence-diagram,.editormd-preview-container .flowchart,.editormd-preview-container .sequence-diagram{margin:0 auto;text-align:center}.editormd-html-preview .flowchart svg,.editormd-html-preview .sequence-diagram svg,.editormd-preview-container .flowchart svg,.editormd-preview-container .sequence-diagram svg{margin:0 auto}.editormd-html-preview .flowchart text,.editormd-html-preview .sequence-diagram text,.editormd-preview-container .flowchart text,.editormd-preview-container .sequence-diagram text{font-size:15px!important;font-family:"YaHei Consolas Hybrid",Consolas,"Microsoft YaHei","Malgun Gothic","Segoe UI",Helvetica,Arial!important}/*! Pretty printing styles. Used with prettify.js. */.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.clo,.opn,.pun{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.kwd,.tag,.typ{font-weight:700}.str{color:#060}.kwd{color:#006}.com{color:#600;font-style:italic}.typ{color:#404}.lit{color:#044}.clo,.opn,.pun{color:#440}.tag{color:#006}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}.editormd-html-preview pre.prettyprint,.editormd-preview-container pre.prettyprint{padding:10px;border:1px solid #ddd;white-space:pre-wrap;word-wrap:break-word}.editormd-html-preview ol.linenums,.editormd-preview-container ol.linenums{color:#999;padding-left:2.5em}.editormd-html-preview ol.linenums li,.editormd-preview-container ol.linenums li{list-style-type:decimal}.editormd-html-preview ol.linenums li code,.editormd-preview-container ol.linenums li code{border:none;background:0 0;padding:0}.editormd-html-preview .editormd-toc-menu,.editormd-preview-container .editormd-toc-menu{margin:8px 0 12px;display:inline-block}.editormd-html-preview .editormd-toc-menu>.markdown-toc,.editormd-preview-container .editormd-toc-menu>.markdown-toc{position:relative;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;border:1px solid #ddd;display:inline-block;font-size:1em}.editormd-html-preview .editormd-toc-menu>.markdown-toc>ul,.editormd-preview-container .editormd-toc-menu>.markdown-toc>ul{width:160%;min-width:180px;position:absolute;left:-1px;top:-2px;z-index:100;padding:0 10px 10px;display:none;background:#fff;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 3px 5px rgba(0,0,0,.2);-moz-box-shadow:0 3px 5px rgba(0,0,0,.2);-ms-box-shadow:0 3px 5px rgba(0,0,0,.2);-o-box-shadow:0 3px 5px rgba(0,0,0,.2);box-shadow:0 3px 5px rgba(0,0,0,.2)}.editormd-html-preview .editormd-toc-menu>.markdown-toc>ul>li ul,.editormd-preview-container .editormd-toc-menu>.markdown-toc>ul>li ul{width:100%;min-width:180px;border:1px solid #ddd;display:none;background:#fff;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px}.editormd-html-preview .editormd-toc-menu .toc-menu-btn:hover,.editormd-html-preview .editormd-toc-menu>.markdown-toc>ul>li a:hover,.editormd-preview-container .editormd-toc-menu .toc-menu-btn:hover,.editormd-preview-container .editormd-toc-menu>.markdown-toc>ul>li a:hover{background-color:#f6f6f6}.editormd-html-preview .editormd-toc-menu>.markdown-toc>ul>li a,.editormd-preview-container .editormd-toc-menu>.markdown-toc>ul>li a{color:#666;padding:6px 10px;display:block;-webkit-transition:background-color 500ms ease-out;-moz-transition:background-color 500ms ease-out;transition:background-color 500ms ease-out}.editormd-html-preview .editormd-toc-menu>.markdown-toc li,.editormd-preview-container .editormd-toc-menu>.markdown-toc li{position:relative}.editormd-html-preview .editormd-toc-menu>.markdown-toc li>ul,.editormd-preview-container .editormd-toc-menu>.markdown-toc li>ul{position:absolute;top:32px;left:10%;display:none;-webkit-box-shadow:0 3px 5px rgba(0,0,0,.2);-moz-box-shadow:0 3px 5px rgba(0,0,0,.2);-ms-box-shadow:0 3px 5px rgba(0,0,0,.2);-o-box-shadow:0 3px 5px rgba(0,0,0,.2);box-shadow:0 3px 5px rgba(0,0,0,.2)}.editormd-html-preview .editormd-toc-menu>.markdown-toc li>ul:after,.editormd-html-preview .editormd-toc-menu>.markdown-toc li>ul:before,.editormd-preview-container .editormd-toc-menu>.markdown-toc li>ul:after,.editormd-preview-container .editormd-toc-menu>.markdown-toc li>ul:before{pointer-events:pointer-events;position:absolute;left:15px;top:-6px;display:block;content:"";width:0;height:0;border:6px solid transparent;border-width:0 6px 6px;z-index:10}.editormd-html-preview .editormd-toc-menu>.markdown-toc li>ul:before,.editormd-preview-container .editormd-toc-menu>.markdown-toc li>ul:before{border-bottom-color:#ccc}.editormd-html-preview .editormd-toc-menu>.markdown-toc li>ul:after,.editormd-preview-container .editormd-toc-menu>.markdown-toc li>ul:after{border-bottom-color:#fff;top:-5px}.editormd-html-preview .editormd-toc-menu ul,.editormd-preview-container .editormd-toc-menu ul{list-style:none}.editormd-html-preview .editormd-toc-menu a,.editormd-preview-container .editormd-toc-menu a{text-decoration:none}.editormd-html-preview .editormd-toc-menu h1,.editormd-preview-container .editormd-toc-menu h1{font-size:16px;padding:5px 0 10px 10px;line-height:1;border-bottom:1px solid #eee}.editormd-html-preview .editormd-toc-menu h1 .fa,.editormd-preview-container .editormd-toc-menu h1 .fa{padding-left:10px}.editormd-html-preview .editormd-toc-menu .toc-menu-btn,.editormd-preview-container .editormd-toc-menu .toc-menu-btn{color:#666;min-width:180px;padding:5px 10px;border-radius:4px;display:inline-block;-webkit-transition:background-color 500ms ease-out;-moz-transition:background-color 500ms ease-out;transition:background-color 500ms ease-out}.editormd-html-preview textarea,.editormd-onlyread .editormd-toolbar{display:none}.editormd-html-preview .editormd-toc-menu .toc-menu-btn .fa,.editormd-preview-container .editormd-toc-menu .toc-menu-btn .fa{float:right;padding:3px 0 0 10px;font-size:1.3em}.markdown-body .editormd-toc-menu ul{padding-left:0}.markdown-body .highlight pre,.markdown-body pre{line-height:1.6}hr.editormd-page-break{border:1px dotted #ccc;font-size:0;height:2px}@media only print{hr.editormd-page-break{background:0 0;border:none;height:0}}.editormd-html-preview hr.editormd-page-break{background:0 0;border:none;height:0}.editormd-preview-close-btn{color:#fff;padding:4px 6px;font-size:18px;-webkit-border-radius:500px;-moz-border-radius:500px;-ms-border-radius:500px;-o-border-radius:500px;border-radius:500px;display:none;background-color:#ccc;position:absolute;top:25px;right:35px;z-index:19;-webkit-transition:background-color 300ms ease-out;-moz-transition:background-color 300ms ease-out;transition:background-color 300ms ease-out}.editormd-preview-close-btn:hover{background-color:#999}.editormd-preview-active{width:100%;padding:40px}.editormd-preview-theme-dark{color:#777;background:#2C2827}.editormd-preview-theme-dark .editormd-preview-container{color:#888;background-color:#2C2827}.editormd-preview-theme-dark .editormd-preview-container pre.prettyprint{border:none}.editormd-preview-theme-dark .editormd-preview-container blockquote{color:#555;padding:.5em;background:#222;border-color:#333}.editormd-preview-theme-dark .editormd-preview-container abbr{color:#fff;padding:1px 3px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;background:#f90}.editormd-preview-theme-dark .editormd-preview-container code{color:#fff;border:none;padding:1px 3px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;background:#5A9600}.editormd-preview-theme-dark .editormd-preview-container table{border:none}.editormd-preview-theme-dark .editormd-preview-container .fa-emoji{color:#B4BF42}.editormd-preview-theme-dark .editormd-preview-container .katex{color:#FEC93F}.editormd-preview-theme-dark .editormd-toc-menu>.markdown-toc{background:#fff;border:none}.editormd-preview-theme-dark .editormd-toc-menu>.markdown-toc h1{border-color:#ddd}.editormd-preview-theme-dark .markdown-body h1,.editormd-preview-theme-dark .markdown-body h2,.editormd-preview-theme-dark .markdown-body hr{border-color:#222}.editormd-preview-theme-dark pre{color:#999;background-color:#111;background-color:rgba(0,0,0,.4)}.editormd-preview-theme-dark pre .pln{color:#999}.editormd-preview-theme-dark li.L1,.editormd-preview-theme-dark li.L3,.editormd-preview-theme-dark li.L5,.editormd-preview-theme-dark li.L7,.editormd-preview-theme-dark li.L9{background:0 0}.editormd-preview-theme-dark [class*=editormd-logo]{color:#2196F3}.editormd-preview-theme-dark .sequence-diagram text{fill:#fff}.editormd-preview-theme-dark .sequence-diagram path,.editormd-preview-theme-dark .sequence-diagram rect{color:#fff;fill:#64D1CB;stroke:#64D1CB}.editormd-preview-theme-dark .flowchart path,.editormd-preview-theme-dark .flowchart rect{stroke:#A6C6FF}.editormd-preview-theme-dark .flowchart rect{fill:#A6C6FF}.editormd-preview-theme-dark .flowchart text{fill:#5879B4}@media screen{.editormd-preview-theme-dark .str{color:#080}.editormd-preview-theme-dark .kwd{color:#f90}.editormd-preview-theme-dark .com{color:#444}.editormd-preview-theme-dark .typ{color:#606}.editormd-preview-theme-dark .lit{color:#066}.editormd-preview-theme-dark .clo,.editormd-preview-theme-dark .opn,.editormd-preview-theme-dark .pun{color:#660}.editormd-preview-theme-dark .tag{color:#f90}.editormd-preview-theme-dark .atn{color:#6C95F5}.editormd-preview-theme-dark .atv{color:#080}.editormd-preview-theme-dark .dec,.editormd-preview-theme-dark .var{color:#008BA7}.editormd-preview-theme-dark .fun{color:red}}.editormd-onlyread .CodeMirror{margin-top:0}.editormd-onlyread .editormd-preview{top:0}.editormd-fullscreen{position:fixed;top:0;left:0;border:none;margin:0 auto}.editormd-theme-dark{border-color:#1a1a17}.editormd-theme-dark .editormd-toolbar{background:#1A1A17;border-color:#1a1a17}.editormd-theme-dark .editormd-menu>li>a{color:#777;border-color:#1a1a17}.editormd-theme-dark .editormd-menu>li>a.active,.editormd-theme-dark .editormd-menu>li>a:hover{border-color:#333;background:#333}.editormd-theme-dark .editormd-menu>li.divider{border-right:1px solid #111}.editormd-theme-dark .CodeMirror{border-right:1px solid rgba(0,0,0,.1)}
\ No newline at end of file
diff --git a/vendor/assets/editormd/editormd.js b/vendor/assets/editormd/editormd.js
new file mode 100755
index 000000000..1f18a474f
--- /dev/null
+++ b/vendor/assets/editormd/editormd.js
@@ -0,0 +1,4597 @@
+/*
+ * Editor.md
+ *
+ * @file editormd.js
+ * @version v1.5.0
+ * @description Open source online markdown editor.
+ * @license MIT License
+ * @author Pandao
+ * {@link https://github.com/pandao/editor.md}
+ * @updateTime 2015-06-09
+ */
+
+;(function(factory) {
+ "use strict";
+
+ // CommonJS/Node.js
+ if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
+ {
+ module.exports = factory;
+ }
+ else if (typeof define === "function") // AMD/CMD/Sea.js
+ {
+ if (define.amd) // for Require.js
+ {
+ /* Require.js define replace */
+ }
+ else
+ {
+ define(["jquery"], factory); // for Sea.js
+ }
+ }
+ else
+ {
+ window.editormd = factory();
+ }
+
+}(function() {
+
+ /* Require.js assignment replace */
+
+ "use strict";
+
+ var $ = (typeof (jQuery) !== "undefined") ? jQuery : Zepto;
+
+ if (typeof ($) === "undefined") {
+ return ;
+ }
+
+ /**
+ * editormd
+ *
+ * @param {String} id 编辑器的ID
+ * @param {Object} options 配置选项 Key/Value
+ * @returns {Object} editormd 返回editormd对象
+ */
+
+ var editormd = function (id, options) {
+ return new editormd.fn.init(id, options);
+ };
+
+ editormd.title = editormd.$name = "Editor.md";
+ editormd.version = "1.5.0";
+ editormd.homePage = "https://pandao.github.io/editor.md/";
+ editormd.classPrefix = "editormd-";
+
+ editormd.toolbarModes = {
+ full : [
+ "undo", "redo", "|",
+ "bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|",
+ "h1", "h2", "h3", "h4", "h5", "h6", "|",
+ "list-ul", "list-ol", "hr", "|",
+ "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|",
+ "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|",
+ "help", "info"
+ ],
+ simple : [
+ "undo", "redo", "|",
+ "bold", "del", "italic", "quote", "uppercase", "lowercase", "|",
+ "h1", "h2", "h3", "h4", "h5", "h6", "|",
+ "list-ul", "list-ol", "hr", "|",
+ "watch", "preview", "fullscreen", "|",
+ "help", "info"
+ ],
+ mini : [
+ "undo", "redo", "|",
+ "watch", "preview", "|",
+ "help", "info"
+ ]
+ };
+
+ editormd.defaults = {
+ mode : "gfm", //gfm or markdown
+ name : "", // Form element name
+ value : "", // value for CodeMirror, if mode not gfm/markdown
+ theme : "", // Editor.md self themes, before v1.5.0 is CodeMirror theme, default empty
+ editorTheme : "default", // Editor area, this is CodeMirror theme at v1.5.0
+ previewTheme : "", // Preview area theme, default empty
+ markdown : "", // Markdown source code
+ appendMarkdown : "", // if in init textarea value not empty, append markdown to textarea
+ width : "100%",
+ height : "100%",
+ path : "./lib/", // Dependents module file directory
+ pluginPath : "", // If this empty, default use settings.path + "../plugins/"
+ delay : 300, // Delay parse markdown to html, Uint : ms
+ autoLoadModules : true, // Automatic load dependent module files
+ watch : true,
+ placeholder : "Enjoy Markdown! coding now...",
+ gotoLine : true,
+ codeFold : false,
+ autoHeight : false,
+ autoFocus : true,
+ autoCloseTags : true,
+ searchReplace : true,
+ syncScrolling : true, // true | false | "single", default true
+ readOnly : false,
+ tabSize : 4,
+ indentUnit : 4,
+ lineNumbers : true,
+ lineWrapping : true,
+ autoCloseBrackets : true,
+ showTrailingSpace : true,
+ matchBrackets : true,
+ indentWithTabs : true,
+ styleSelectedText : true,
+ matchWordHighlight : true, // options: true, false, "onselected"
+ styleActiveLine : true, // Highlight the current line
+ dialogLockScreen : true,
+ dialogShowMask : true,
+ dialogDraggable : true,
+ dialogMaskBgColor : "#fff",
+ dialogMaskOpacity : 0.1,
+ fontSize : "13px",
+ saveHTMLToTextarea : false,
+ disabledKeyMaps : [],
+
+ onload : function() {},
+ onresize : function() {},
+ onchange : function() {},
+ onwatch : null,
+ onunwatch : null,
+ onpreviewing : function() {},
+ onpreviewed : function() {},
+ onfullscreen : function() {},
+ onfullscreenExit : function() {},
+ onscroll : function() {},
+ onpreviewscroll : function() {},
+
+ imageUpload : false,
+ imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
+ imageUploadURL : "",
+ crossDomainUpload : false,
+ uploadCallbackURL : "",
+
+ toc : true, // Table of contents
+ tocm : false, // Using [TOCM], auto create ToC dropdown menu
+ tocTitle : "", // for ToC dropdown menu btn
+ tocDropdown : false,
+ tocContainer : "",
+ tocStartLevel : 1, // Said from H1 to create ToC
+ htmlDecode : false, // Open the HTML tag identification
+ pageBreak : true, // Enable parse page break [========]
+ atLink : true, // for @link
+ emailLink : true, // for email address auto link
+ taskList : false, // Enable Github Flavored Markdown task lists
+ emoji : false, // :emoji: , Support Github emoji, Twitter Emoji (Twemoji);
+ // Support FontAwesome icon emoji :fa-xxx: > Using fontAwesome icon web fonts;
+ // Support Editor.md logo icon emoji :editormd-logo: :editormd-logo-1x: > 1~8x;
+ tex : false, // TeX(LaTeX), based on KaTeX
+ flowChart : false, // flowChart.js only support IE9+
+ sequenceDiagram : false, // sequenceDiagram.js only support IE9+
+ previewCodeHighlight : true,
+
+ toolbar : true, // show/hide toolbar
+ toolbarAutoFixed : true, // on window scroll auto fixed position
+ toolbarIcons : "full",
+ toolbarTitles : {},
+ toolbarHandlers : {
+ ucwords : function() {
+ return editormd.toolbarHandlers.ucwords;
+ },
+ lowercase : function() {
+ return editormd.toolbarHandlers.lowercase;
+ }
+ },
+ toolbarCustomIcons : { // using html tag create toolbar icon, unused default tag.
+ lowercase : " a ",
+ "ucwords" : "Aa "
+ },
+ toolbarIconsClass : {
+ undo : "fa-undo",
+ redo : "fa-repeat",
+ bold : "fa-bold",
+ del : "fa-strikethrough",
+ italic : "fa-italic",
+ quote : "fa-quote-left",
+ uppercase : "fa-font",
+ h1 : editormd.classPrefix + "bold",
+ h2 : editormd.classPrefix + "bold",
+ h3 : editormd.classPrefix + "bold",
+ h4 : editormd.classPrefix + "bold",
+ h5 : editormd.classPrefix + "bold",
+ h6 : editormd.classPrefix + "bold",
+ "list-ul" : "fa-list-ul",
+ "list-ol" : "fa-list-ol",
+ hr : "fa-minus",
+ link : "fa-link",
+ "reference-link" : "fa-anchor",
+ image : "fa-picture-o",
+ code : "fa-code",
+ "preformatted-text" : "fa-file-code-o",
+ "code-block" : "fa-file-code-o",
+ table : "fa-table",
+ datetime : "fa-clock-o",
+ emoji : "fa-smile-o",
+ "html-entities" : "fa-copyright",
+ pagebreak : "fa-newspaper-o",
+ "goto-line" : "fa-terminal", // fa-crosshairs
+ watch : "fa-eye-slash",
+ unwatch : "fa-eye",
+ preview : "fa-desktop",
+ search : "fa-search",
+ fullscreen : "fa-arrows-alt",
+ clear : "fa-eraser",
+ help : "fa-question-circle",
+ info : "fa-info-circle"
+ },
+ toolbarIconTexts : {},
+
+ lang : {
+ name : "zh-cn",
+ description : "开源在线Markdown编辑器 Open source online Markdown editor.",
+ tocTitle : "目录",
+ toolbar : {
+ undo : "撤销(Ctrl+Z)",
+ redo : "重做(Ctrl+Y)",
+ bold : "粗体",
+ del : "删除线",
+ italic : "斜体",
+ quote : "引用",
+ ucwords : "将每个单词首字母转成大写",
+ uppercase : "将所选转换成大写",
+ lowercase : "将所选转换成小写",
+ h1 : "标题1",
+ h2 : "标题2",
+ h3 : "标题3",
+ h4 : "标题4",
+ h5 : "标题5",
+ h6 : "标题6",
+ "list-ul" : "无序列表",
+ "list-ol" : "有序列表",
+ hr : "横线",
+ link : "链接",
+ "reference-link" : "引用链接",
+ image : "添加图片",
+ code : "行内代码",
+ "preformatted-text" : "预格式文本 / 代码块(缩进风格)",
+ "code-block" : "代码块(多语言风格)",
+ table : "添加表格",
+ datetime : "日期时间",
+ emoji : "Emoji表情",
+ "html-entities" : "HTML实体字符",
+ pagebreak : "插入分页符",
+ "goto-line" : "跳转到行",
+ watch : "关闭实时预览",
+ unwatch : "开启实时预览",
+ preview : "全窗口预览HTML(按 Shift + ESC还原)",
+ fullscreen : "全屏(按ESC还原)",
+ clear : "清空",
+ search : "搜索",
+ help : "使用帮助",
+ info : "关于" + editormd.title
+ },
+ buttons : {
+ enter : "确定",
+ cancel : "取消",
+ close : "关闭"
+ },
+ dialog : {
+ link : {
+ title : "添加链接",
+ url : "链接地址",
+ urlTitle : "链接标题",
+ urlEmpty : "错误:请填写链接地址。"
+ },
+ referenceLink : {
+ title : "添加引用链接",
+ name : "引用名称",
+ url : "链接地址",
+ urlId : "链接ID",
+ urlTitle : "链接标题",
+ nameEmpty: "错误:引用链接的名称不能为空。",
+ idEmpty : "错误:请填写引用链接的ID。",
+ urlEmpty : "错误:请填写引用链接的URL地址。"
+ },
+ image : {
+ title : "添加图片",
+ url : "图片地址",
+ link : "图片链接",
+ alt : "图片描述",
+ uploadButton : "本地上传",
+ imageURLEmpty : "错误:图片地址不能为空。",
+ uploadFileEmpty : "错误:上传的图片不能为空。",
+ formatNotAllowed : "错误:只允许上传图片文件,允许上传的图片文件格式有:"
+ },
+ preformattedText : {
+ title : "添加预格式文本或代码块",
+ emptyAlert : "错误:请填写预格式文本或代码的内容。"
+ },
+ codeBlock : {
+ title : "添加代码块",
+ selectLabel : "代码语言:",
+ selectDefaultText : "请选择代码语言",
+ otherLanguage : "其他语言",
+ unselectedLanguageAlert : "错误:请选择代码所属的语言类型。",
+ codeEmptyAlert : "错误:请填写代码内容。"
+ },
+ htmlEntities : {
+ title : "HTML 实体字符"
+ },
+ help : {
+ title : "使用帮助"
+ }
+ }
+ }
+ };
+
+ editormd.classNames = {
+ tex : editormd.classPrefix + "tex"
+ };
+
+ editormd.dialogZindex = 99999;
+
+ editormd.$katex = null;
+ editormd.$marked = null;
+ editormd.$CodeMirror = null;
+ editormd.$prettyPrint = null;
+
+ var timer, flowchartTimer;
+
+ editormd.prototype = editormd.fn = {
+ state : {
+ watching : false,
+ loaded : false,
+ preview : false,
+ fullscreen : false
+ },
+
+ /**
+ * 构造函数/实例初始化
+ * Constructor / instance initialization
+ *
+ * @param {String} id 编辑器的ID
+ * @param {Object} [options={}] 配置选项 Key/Value
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ init : function (id, options) {
+
+ options = options || {};
+
+ if (typeof id === "object")
+ {
+ options = id;
+ }
+
+ var _this = this;
+ var classPrefix = this.classPrefix = editormd.classPrefix;
+ var settings = this.settings = $.extend(true, editormd.defaults, options);
+
+ id = (typeof id === "object") ? settings.id : id;
+
+ var editor = this.editor = $("#" + id);
+
+ this.id = id;
+ this.lang = settings.lang;
+
+ var classNames = this.classNames = {
+ textarea : {
+ html : classPrefix + "html-textarea",
+ markdown : classPrefix + "markdown-textarea"
+ }
+ };
+
+ settings.pluginPath = (settings.pluginPath === "") ? settings.path + "../plugins/" : settings.pluginPath;
+
+ this.state.watching = (settings.watch) ? true : false;
+
+ if ( !editor.hasClass("editormd") ) {
+ editor.addClass("editormd");
+ }
+
+ editor.css({
+ width : (typeof settings.width === "number") ? settings.width + "px" : settings.width,
+ height : (typeof settings.height === "number") ? settings.height + "px" : settings.height
+ });
+
+ if (settings.autoHeight)
+ {
+ editor.css("height", "auto");
+ }
+
+ var markdownTextarea = this.markdownTextarea = editor.children("textarea");
+
+ if (markdownTextarea.length < 1)
+ {
+ editor.append("");
+ markdownTextarea = this.markdownTextarea = editor.children("textarea");
+ }
+
+ markdownTextarea.addClass(classNames.textarea.markdown).attr("placeholder", settings.placeholder);
+
+ if (typeof markdownTextarea.attr("name") === "undefined" || markdownTextarea.attr("name") === "")
+ {
+ markdownTextarea.attr("name", (settings.name !== "") ? settings.name : id + "-markdown-doc");
+ }
+
+ var appendElements = [
+ (!settings.readOnly) ? " " : "",
+ ( (settings.saveHTMLToTextarea) ? "" : "" ),
+ "",
+ "
",
+ "
"
+ ].join("\n");
+
+ editor.append(appendElements).addClass(classPrefix + "vertical");
+
+ if (settings.theme !== "")
+ {
+ editor.addClass(classPrefix + "theme-" + settings.theme);
+ }
+
+ this.mask = editor.children("." + classPrefix + "mask");
+ this.containerMask = editor.children("." + classPrefix + "container-mask");
+
+ if (settings.markdown !== "")
+ {
+ markdownTextarea.val(settings.markdown);
+ }
+
+ if (settings.appendMarkdown !== "")
+ {
+ markdownTextarea.val(markdownTextarea.val() + settings.appendMarkdown);
+ }
+
+ this.htmlTextarea = editor.children("." + classNames.textarea.html);
+ this.preview = editor.children("." + classPrefix + "preview");
+ this.previewContainer = this.preview.children("." + classPrefix + "preview-container");
+
+ if (settings.previewTheme !== "")
+ {
+ this.preview.addClass(classPrefix + "preview-theme-" + settings.previewTheme);
+ }
+
+ if (typeof define === "function" && define.amd)
+ {
+ if (typeof katex !== "undefined")
+ {
+ editormd.$katex = katex;
+ }
+
+ if (settings.searchReplace && !settings.readOnly)
+ {
+ editormd.loadCSS(settings.path + "codemirror/addon/dialog/dialog");
+ editormd.loadCSS(settings.path + "codemirror/addon/search/matchesonscrollbar");
+ }
+ }
+
+ if ((typeof define === "function" && define.amd) || !settings.autoLoadModules)
+ {
+ if (typeof CodeMirror !== "undefined") {
+ editormd.$CodeMirror = CodeMirror;
+ }
+
+ if (typeof marked !== "undefined") {
+ editormd.$marked = marked;
+ }
+
+ this.setCodeMirror().setToolbar().loadedDisplay();
+ }
+ else
+ {
+ this.loadQueues();
+ }
+
+ return this;
+ },
+
+ /**
+ * 所需组件加载队列
+ * Required components loading queue
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ loadQueues : function() {
+ var _this = this;
+ var settings = this.settings;
+ var loadPath = settings.path;
+
+ var loadFlowChartOrSequenceDiagram = function() {
+
+ if (editormd.isIE8)
+ {
+ _this.loadedDisplay();
+
+ return ;
+ }
+
+ if (settings.flowChart || settings.sequenceDiagram)
+ {
+ editormd.loadScript(loadPath + "raphael.min", function() {
+
+ editormd.loadScript(loadPath + "underscore.min", function() {
+
+ if (!settings.flowChart && settings.sequenceDiagram)
+ {
+ editormd.loadScript(loadPath + "sequence-diagram.min", function() {
+ _this.loadedDisplay();
+ });
+ }
+ else if (settings.flowChart && !settings.sequenceDiagram)
+ {
+ editormd.loadScript(loadPath + "flowchart.min", function() {
+ editormd.loadScript(loadPath + "jquery.flowchart.min", function() {
+ _this.loadedDisplay();
+ });
+ });
+ }
+ else if (settings.flowChart && settings.sequenceDiagram)
+ {
+ editormd.loadScript(loadPath + "flowchart.min", function() {
+ editormd.loadScript(loadPath + "jquery.flowchart.min", function() {
+ editormd.loadScript(loadPath + "sequence-diagram.min", function() {
+ _this.loadedDisplay();
+ });
+ });
+ });
+ }
+ });
+
+ });
+ }
+ else
+ {
+ _this.loadedDisplay();
+ }
+ };
+
+ editormd.loadCSS(loadPath + "codemirror/codemirror.min");
+
+ if (settings.searchReplace && !settings.readOnly)
+ {
+ editormd.loadCSS(loadPath + "codemirror/addon/dialog/dialog");
+ editormd.loadCSS(loadPath + "codemirror/addon/search/matchesonscrollbar");
+ }
+
+ if (settings.codeFold)
+ {
+ editormd.loadCSS(loadPath + "codemirror/addon/fold/foldgutter");
+ }
+
+ editormd.loadScript(loadPath + "codemirror/codemirror.min", function() {
+ editormd.$CodeMirror = CodeMirror;
+
+ editormd.loadScript(loadPath + "codemirror/modes.min", function() {
+
+ editormd.loadScript(loadPath + "codemirror/addons.min", function() {
+
+ _this.setCodeMirror();
+
+ if (settings.mode !== "gfm" && settings.mode !== "markdown")
+ {
+ _this.loadedDisplay();
+
+ return false;
+ }
+
+ _this.setToolbar();
+
+ editormd.loadScript(loadPath + "marked.min", function() {
+
+ editormd.$marked = marked;
+
+ if (settings.previewCodeHighlight)
+ {
+ editormd.loadScript(loadPath + "prettify.min", function() {
+ loadFlowChartOrSequenceDiagram();
+ });
+ }
+ else
+ {
+ loadFlowChartOrSequenceDiagram();
+ }
+ });
+
+ });
+
+ });
+
+ });
+
+ return this;
+ },
+
+ /**
+ * 设置 Editor.md 的整体主题,主要是工具栏
+ * Setting Editor.md theme
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setTheme : function(theme) {
+ var editor = this.editor;
+ var oldTheme = this.settings.theme;
+ var themePrefix = this.classPrefix + "theme-";
+
+ editor.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme);
+
+ this.settings.theme = theme;
+
+ return this;
+ },
+
+ /**
+ * 设置 CodeMirror(编辑区)的主题
+ * Setting CodeMirror (Editor area) theme
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setEditorTheme : function(theme) {
+ var settings = this.settings;
+ settings.editorTheme = theme;
+
+ if (theme !== "default")
+ {
+ editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme);
+ }
+
+ this.cm.setOption("theme", theme);
+
+ return this;
+ },
+
+ /**
+ * setEditorTheme() 的别名
+ * setEditorTheme() alias
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setCodeMirrorTheme : function (theme) {
+ this.setEditorTheme(theme);
+
+ return this;
+ },
+
+ /**
+ * 设置 Editor.md 的主题
+ * Setting Editor.md theme
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setPreviewTheme : function(theme) {
+ var preview = this.preview;
+ var oldTheme = this.settings.previewTheme;
+ var themePrefix = this.classPrefix + "preview-theme-";
+
+ preview.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme);
+
+ this.settings.previewTheme = theme;
+
+ return this;
+ },
+
+ /**
+ * 配置和初始化CodeMirror组件
+ * CodeMirror initialization
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setCodeMirror : function() {
+ var settings = this.settings;
+ var editor = this.editor;
+
+ if (settings.editorTheme !== "default")
+ {
+ editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme);
+ }
+
+ var codeMirrorConfig = {
+ mode : settings.mode,
+ theme : settings.editorTheme,
+ tabSize : settings.tabSize,
+ dragDrop : false,
+ autofocus : settings.autoFocus,
+ autoCloseTags : settings.autoCloseTags,
+ readOnly : (settings.readOnly) ? "nocursor" : false,
+ indentUnit : settings.indentUnit,
+ lineNumbers : settings.lineNumbers,
+ lineWrapping : settings.lineWrapping,
+ extraKeys : {
+ "Ctrl-Q": function(cm) {
+ cm.foldCode(cm.getCursor());
+ }
+ },
+ foldGutter : settings.codeFold,
+ gutters : ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
+ matchBrackets : settings.matchBrackets,
+ indentWithTabs : settings.indentWithTabs,
+ styleActiveLine : settings.styleActiveLine,
+ styleSelectedText : settings.styleSelectedText,
+ autoCloseBrackets : settings.autoCloseBrackets,
+ showTrailingSpace : settings.showTrailingSpace,
+ highlightSelectionMatches : ( (!settings.matchWordHighlight) ? false : { showToken: (settings.matchWordHighlight === "onselected") ? false : /\w/ } )
+ };
+
+ this.codeEditor = this.cm = editormd.$CodeMirror.fromTextArea(this.markdownTextarea[0], codeMirrorConfig);
+ this.codeMirror = this.cmElement = editor.children(".CodeMirror");
+
+ if (settings.value !== "")
+ {
+ this.cm.setValue(settings.value);
+ }
+
+ this.codeMirror.css({
+ fontSize : settings.fontSize,
+ width : (!settings.watch) ? "100%" : "50%"
+ });
+
+ if (settings.autoHeight)
+ {
+ this.codeMirror.css("height", "auto");
+ this.cm.setOption("viewportMargin", Infinity);
+ }
+
+ if (!settings.lineNumbers)
+ {
+ this.codeMirror.find(".CodeMirror-gutters").css("border-right", "none");
+ }
+
+ return this;
+ },
+
+ /**
+ * 获取CodeMirror的配置选项
+ * Get CodeMirror setting options
+ *
+ * @returns {Mixed} return CodeMirror setting option value
+ */
+
+ getCodeMirrorOption : function(key) {
+ return this.cm.getOption(key);
+ },
+
+ /**
+ * 配置和重配置CodeMirror的选项
+ * CodeMirror setting options / resettings
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setCodeMirrorOption : function(key, value) {
+
+ this.cm.setOption(key, value);
+
+ return this;
+ },
+
+ /**
+ * 添加 CodeMirror 键盘快捷键
+ * Add CodeMirror keyboard shortcuts key map
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ addKeyMap : function(map, bottom) {
+ this.cm.addKeyMap(map, bottom);
+
+ return this;
+ },
+
+ /**
+ * 移除 CodeMirror 键盘快捷键
+ * Remove CodeMirror keyboard shortcuts key map
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ removeKeyMap : function(map) {
+ this.cm.removeKeyMap(map);
+
+ return this;
+ },
+
+ /**
+ * 跳转到指定的行
+ * Goto CodeMirror line
+ *
+ * @param {String|Intiger} line line number or "first"|"last"
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ gotoLine : function (line) {
+
+ var settings = this.settings;
+
+ if (!settings.gotoLine)
+ {
+ return this;
+ }
+
+ var cm = this.cm;
+ var editor = this.editor;
+ var count = cm.lineCount();
+ var preview = this.preview;
+
+ if (typeof line === "string")
+ {
+ if(line === "last")
+ {
+ line = count;
+ }
+
+ if (line === "first")
+ {
+ line = 1;
+ }
+ }
+
+ if (typeof line !== "number")
+ {
+ alert("Error: The line number must be an integer.");
+ return this;
+ }
+
+ line = parseInt(line) - 1;
+
+ if (line > count)
+ {
+ alert("Error: The line number range 1-" + count);
+
+ return this;
+ }
+
+ cm.setCursor( {line : line, ch : 0} );
+
+ var scrollInfo = cm.getScrollInfo();
+ var clientHeight = scrollInfo.clientHeight;
+ var coords = cm.charCoords({line : line, ch : 0}, "local");
+
+ cm.scrollTo(null, (coords.top + coords.bottom - clientHeight) / 2);
+
+ if (settings.watch)
+ {
+ var cmScroll = this.codeMirror.find(".CodeMirror-scroll")[0];
+ var height = $(cmScroll).height();
+ var scrollTop = cmScroll.scrollTop;
+ var percent = (scrollTop / cmScroll.scrollHeight);
+
+ if (scrollTop === 0)
+ {
+ preview.scrollTop(0);
+ }
+ else if (scrollTop + height >= cmScroll.scrollHeight - 16)
+ {
+ preview.scrollTop(preview[0].scrollHeight);
+ }
+ else
+ {
+ preview.scrollTop(preview[0].scrollHeight * percent);
+ }
+ }
+
+ cm.focus();
+
+ return this;
+ },
+
+ /**
+ * 扩展当前实例对象,可同时设置多个或者只设置一个
+ * Extend editormd instance object, can mutil setting.
+ *
+ * @returns {editormd} this(editormd instance object.)
+ */
+
+ extend : function() {
+ if (typeof arguments[1] !== "undefined")
+ {
+ if (typeof arguments[1] === "function")
+ {
+ arguments[1] = $.proxy(arguments[1], this);
+ }
+
+ this[arguments[0]] = arguments[1];
+ }
+
+ if (typeof arguments[0] === "object" && typeof arguments[0].length === "undefined")
+ {
+ $.extend(true, this, arguments[0]);
+ }
+
+ return this;
+ },
+
+ /**
+ * 设置或扩展当前实例对象,单个设置
+ * Extend editormd instance object, one by one
+ *
+ * @param {String|Object} key option key
+ * @param {String|Object} value option value
+ * @returns {editormd} this(editormd instance object.)
+ */
+
+ set : function (key, value) {
+
+ if (typeof value !== "undefined" && typeof value === "function")
+ {
+ value = $.proxy(value, this);
+ }
+
+ this[key] = value;
+
+ return this;
+ },
+
+ /**
+ * 重新配置
+ * Resetting editor options
+ *
+ * @param {String|Object} key option key
+ * @param {String|Object} value option value
+ * @returns {editormd} this(editormd instance object.)
+ */
+
+ config : function(key, value) {
+ var settings = this.settings;
+
+ if (typeof key === "object")
+ {
+ settings = $.extend(true, settings, key);
+ }
+
+ if (typeof key === "string")
+ {
+ settings[key] = value;
+ }
+
+ this.settings = settings;
+ this.recreate();
+
+ return this;
+ },
+
+ /**
+ * 注册事件处理方法
+ * Bind editor event handle
+ *
+ * @param {String} eventType event type
+ * @param {Function} callback 回调函数
+ * @returns {editormd} this(editormd instance object.)
+ */
+
+ on : function(eventType, callback) {
+ var settings = this.settings;
+
+ if (typeof settings["on" + eventType] !== "undefined")
+ {
+ settings["on" + eventType] = $.proxy(callback, this);
+ }
+
+ return this;
+ },
+
+ /**
+ * 解除事件处理方法
+ * Unbind editor event handle
+ *
+ * @param {String} eventType event type
+ * @returns {editormd} this(editormd instance object.)
+ */
+
+ off : function(eventType) {
+ var settings = this.settings;
+
+ if (typeof settings["on" + eventType] !== "undefined")
+ {
+ settings["on" + eventType] = function(){};
+ }
+
+ return this;
+ },
+
+ /**
+ * 显示工具栏
+ * Display toolbar
+ *
+ * @param {Function} [callback=function(){}] 回调函数
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ showToolbar : function(callback) {
+ var settings = this.settings;
+
+ if(settings.readOnly) {
+ return this;
+ }
+
+ if (settings.toolbar && (this.toolbar.length < 1 || this.toolbar.find("." + this.classPrefix + "menu").html() === "") )
+ {
+ this.setToolbar();
+ }
+
+ settings.toolbar = true;
+
+ this.toolbar.show();
+ this.resize();
+
+ $.proxy(callback || function(){}, this)();
+
+ return this;
+ },
+
+ /**
+ * 隐藏工具栏
+ * Hide toolbar
+ *
+ * @param {Function} [callback=function(){}] 回调函数
+ * @returns {editormd} this(editormd instance object.)
+ */
+
+ hideToolbar : function(callback) {
+ var settings = this.settings;
+
+ settings.toolbar = false;
+ this.toolbar.hide();
+ this.resize();
+
+ $.proxy(callback || function(){}, this)();
+
+ return this;
+ },
+
+ /**
+ * 页面滚动时工具栏的固定定位
+ * Set toolbar in window scroll auto fixed position
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setToolbarAutoFixed : function(fixed) {
+
+ var state = this.state;
+ var editor = this.editor;
+ var toolbar = this.toolbar;
+ var settings = this.settings;
+
+ if (typeof fixed !== "undefined")
+ {
+ settings.toolbarAutoFixed = fixed;
+ }
+
+ var autoFixedHandle = function(){
+ var $window = $(window);
+ var top = $window.scrollTop();
+
+ if (!settings.toolbarAutoFixed)
+ {
+ return false;
+ }
+
+ if (top - editor.offset().top > 10 && top < editor.height())
+ {
+ toolbar.css({
+ position : "fixed",
+ width : editor.width() + "px",
+ left : ($window.width() - editor.width()) / 2 + "px"
+ });
+ }
+ else
+ {
+ toolbar.css({
+ position : "absolute",
+ width : "100%",
+ left : 0
+ });
+ }
+ };
+
+ if (!state.fullscreen && !state.preview && settings.toolbar && settings.toolbarAutoFixed)
+ {
+ $(window).bind("scroll", autoFixedHandle);
+ }
+
+ return this;
+ },
+
+ /**
+ * 配置和初始化工具栏
+ * Set toolbar and Initialization
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setToolbar : function() {
+ var settings = this.settings;
+
+ if(settings.readOnly) {
+ return this;
+ }
+
+ var editor = this.editor;
+ var preview = this.preview;
+ var classPrefix = this.classPrefix;
+
+ var toolbar = this.toolbar = editor.children("." + classPrefix + "toolbar");
+
+ if (settings.toolbar && toolbar.length < 1)
+ {
+ var toolbarHTML = "";
+
+ editor.append(toolbarHTML);
+ toolbar = this.toolbar = editor.children("." + classPrefix + "toolbar");
+ }
+
+ if (!settings.toolbar)
+ {
+ toolbar.hide();
+
+ return this;
+ }
+
+ toolbar.show();
+
+ var icons = (typeof settings.toolbarIcons === "function") ? settings.toolbarIcons()
+ : ((typeof settings.toolbarIcons === "string") ? editormd.toolbarModes[settings.toolbarIcons] : settings.toolbarIcons);
+
+ var toolbarMenu = toolbar.find("." + this.classPrefix + "menu"), menu = "";
+ var pullRight = false;
+
+ for (var i = 0, len = icons.length; i < len; i++)
+ {
+ var name = icons[i];
+
+ if (name === "||")
+ {
+ pullRight = true;
+ }
+ else if (name === "|")
+ {
+ menu += "| ";
+ }
+ else
+ {
+ var isHeader = (/h(\d)/.test(name));
+ var index = name;
+
+ if (name === "watch" && !settings.watch) {
+ index = "unwatch";
+ }
+
+ var title = settings.lang.toolbar[index];
+ var iconTexts = settings.toolbarIconTexts[index];
+ var iconClass = settings.toolbarIconsClass[index];
+
+ title = (typeof title === "undefined") ? "" : title;
+ iconTexts = (typeof iconTexts === "undefined") ? "" : iconTexts;
+ iconClass = (typeof iconClass === "undefined") ? "" : iconClass;
+
+ var menuItem = pullRight ? "" : " ";
+
+ if (typeof settings.toolbarCustomIcons[name] !== "undefined" && typeof settings.toolbarCustomIcons[name] !== "function")
+ {
+ menuItem += settings.toolbarCustomIcons[name];
+ }
+ else
+ {
+ menuItem += "";
+ menuItem += ""+((isHeader) ? name.toUpperCase() : ( (iconClass === "") ? iconTexts : "") ) + " ";
+ menuItem += " ";
+ }
+
+ menuItem += " ";
+
+ menu = pullRight ? menuItem + menu : menu + menuItem;
+ }
+ }
+
+ toolbarMenu.html(menu);
+
+ toolbarMenu.find("[title=\"Lowercase\"]").attr("title", settings.lang.toolbar.lowercase);
+ toolbarMenu.find("[title=\"ucwords\"]").attr("title", settings.lang.toolbar.ucwords);
+
+ this.setToolbarHandler();
+ this.setToolbarAutoFixed();
+
+ return this;
+ },
+
+ /**
+ * 工具栏图标事件处理对象序列
+ * Get toolbar icons event handlers
+ *
+ * @param {Object} cm CodeMirror的实例对象
+ * @param {String} name 要获取的事件处理器名称
+ * @returns {Object} 返回处理对象序列
+ */
+
+ dialogLockScreen : function() {
+ $.proxy(editormd.dialogLockScreen, this)();
+
+ return this;
+ },
+
+ dialogShowMask : function(dialog) {
+ $.proxy(editormd.dialogShowMask, this)(dialog);
+
+ return this;
+ },
+
+ getToolbarHandles : function(name) {
+ var toolbarHandlers = this.toolbarHandlers = editormd.toolbarHandlers;
+
+ return (name && typeof toolbarIconHandlers[name] !== "undefined") ? toolbarHandlers[name] : toolbarHandlers;
+ },
+
+ /**
+ * 工具栏图标事件处理器
+ * Bind toolbar icons event handle
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setToolbarHandler : function() {
+ var _this = this;
+ var settings = this.settings;
+
+ if (!settings.toolbar || settings.readOnly) {
+ return this;
+ }
+
+ var toolbar = this.toolbar;
+ var cm = this.cm;
+ var classPrefix = this.classPrefix;
+ var toolbarIcons = this.toolbarIcons = toolbar.find("." + classPrefix + "menu > li > a");
+ var toolbarIconHandlers = this.getToolbarHandles();
+
+ toolbarIcons.bind(editormd.mouseOrTouch("click", "touchend"), function(event) {
+
+ var icon = $(this).children(".fa");
+ var name = icon.attr("name");
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if (name === "") {
+ return ;
+ }
+
+ _this.activeIcon = icon;
+
+ if (typeof toolbarIconHandlers[name] !== "undefined")
+ {
+ $.proxy(toolbarIconHandlers[name], _this)(cm);
+ }
+ else
+ {
+ if (typeof settings.toolbarHandlers[name] !== "undefined")
+ {
+ $.proxy(settings.toolbarHandlers[name], _this)(cm, icon, cursor, selection);
+ }
+ }
+
+ if (name !== "link" && name !== "reference-link" && name !== "image" && name !== "code-block" &&
+ name !== "preformatted-text" && name !== "watch" && name !== "preview" && name !== "search" && name !== "fullscreen" && name !== "info")
+ {
+ cm.focus();
+ }
+
+ return false;
+
+ });
+
+ return this;
+ },
+
+ /**
+ * 动态创建对话框
+ * Creating custom dialogs
+ *
+ * @param {Object} options 配置项键值对 Key/Value
+ * @returns {dialog} 返回创建的dialog的jQuery实例对象
+ */
+
+ createDialog : function(options) {
+ return $.proxy(editormd.createDialog, this)(options);
+ },
+
+ /**
+ * 创建关于Editor.md的对话框
+ * Create about Editor.md dialog
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ createInfoDialog : function() {
+ var _this = this;
+ var editor = this.editor;
+ var classPrefix = this.classPrefix;
+
+ var infoDialogHTML = [
+ "",
+ "
",
+ "
" + editormd.title + "v" + editormd.version + " ",
+ "
" + this.lang.description + "
",
+ "
" + editormd.homePage + "
",
+ "
Copyright © 2015 Pandao , The MIT License.
",
+ "
",
+ "
",
+ "
"
+ ].join("\n");
+
+ editor.append(infoDialogHTML);
+
+ var infoDialog = this.infoDialog = editor.children("." + classPrefix + "dialog-info");
+
+ infoDialog.find("." + classPrefix + "dialog-close").bind(editormd.mouseOrTouch("click", "touchend"), function() {
+ _this.hideInfoDialog();
+ });
+
+ infoDialog.css("border", (editormd.isIE8) ? "1px solid #ddd" : "").css("z-index", editormd.dialogZindex).show();
+
+ this.infoDialogPosition();
+
+ return this;
+ },
+
+ /**
+ * 关于Editor.md对话居中定位
+ * Editor.md dialog position handle
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ infoDialogPosition : function() {
+ var infoDialog = this.infoDialog;
+
+ var _infoDialogPosition = function() {
+ infoDialog.css({
+ top : ($(window).height() - infoDialog.height()) / 2 + "px",
+ left : ($(window).width() - infoDialog.width()) / 2 + "px"
+ });
+ };
+
+ _infoDialogPosition();
+
+ $(window).resize(_infoDialogPosition);
+
+ return this;
+ },
+
+ /**
+ * 显示关于Editor.md
+ * Display about Editor.md dialog
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ showInfoDialog : function() {
+
+ $("html,body").css("overflow-x", "hidden");
+
+ var _this = this;
+ var editor = this.editor;
+ var settings = this.settings;
+ var infoDialog = this.infoDialog = editor.children("." + this.classPrefix + "dialog-info");
+
+ if (infoDialog.length < 1)
+ {
+ this.createInfoDialog();
+ }
+
+ this.lockScreen(true);
+
+ this.mask.css({
+ opacity : settings.dialogMaskOpacity,
+ backgroundColor : settings.dialogMaskBgColor
+ }).show();
+
+ infoDialog.css("z-index", editormd.dialogZindex).show();
+
+ this.infoDialogPosition();
+
+ return this;
+ },
+
+ /**
+ * 隐藏关于Editor.md
+ * Hide about Editor.md dialog
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ hideInfoDialog : function() {
+ $("html,body").css("overflow-x", "");
+ this.infoDialog.hide();
+ this.mask.hide();
+ this.lockScreen(false);
+
+ return this;
+ },
+
+ /**
+ * 锁屏
+ * lock screen
+ *
+ * @param {Boolean} lock Boolean 布尔值,是否锁屏
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ lockScreen : function(lock) {
+ editormd.lockScreen(lock);
+ this.resize();
+
+ return this;
+ },
+
+ /**
+ * 编辑器界面重建,用于动态语言包或模块加载等
+ * Recreate editor
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ recreate : function() {
+ var _this = this;
+ var editor = this.editor;
+ var settings = this.settings;
+
+ this.codeMirror.remove();
+
+ this.setCodeMirror();
+
+ if (!settings.readOnly)
+ {
+ if (editor.find(".editormd-dialog").length > 0) {
+ editor.find(".editormd-dialog").remove();
+ }
+
+ if (settings.toolbar)
+ {
+ this.getToolbarHandles();
+ this.setToolbar();
+ }
+ }
+
+ this.loadedDisplay(true);
+
+ return this;
+ },
+
+ /**
+ * 高亮预览HTML的pre代码部分
+ * highlight of preview codes
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ previewCodeHighlight : function() {
+ var settings = this.settings;
+ var previewContainer = this.previewContainer;
+
+ if (settings.previewCodeHighlight)
+ {
+ previewContainer.find("pre").addClass("prettyprint linenums");
+
+ if (typeof prettyPrint !== "undefined")
+ {
+ prettyPrint();
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * 解析TeX(KaTeX)科学公式
+ * TeX(KaTeX) Renderer
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ katexRender : function() {
+
+ if (timer === null)
+ {
+ return this;
+ }
+
+ this.previewContainer.find("." + editormd.classNames.tex).each(function(){
+ var tex = $(this);
+ editormd.$katex.render(tex.text(), tex[0]);
+
+ tex.find(".katex").css("font-size", "1.6em");
+ });
+
+ return this;
+ },
+
+ /**
+ * 解析和渲染流程图及时序图
+ * FlowChart and SequenceDiagram Renderer
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ flowChartAndSequenceDiagramRender : function() {
+ var $this = this;
+ var settings = this.settings;
+ var previewContainer = this.previewContainer;
+
+ if (editormd.isIE8) {
+ return this;
+ }
+
+ if (settings.flowChart) {
+ if (flowchartTimer === null) {
+ return this;
+ }
+
+ previewContainer.find(".flowchart").flowChart();
+ }
+
+ if (settings.sequenceDiagram) {
+ previewContainer.find(".sequence-diagram").sequenceDiagram({theme: "simple"});
+ }
+
+ var preview = $this.preview;
+ var codeMirror = $this.codeMirror;
+ var codeView = codeMirror.find(".CodeMirror-scroll");
+
+ var height = codeView.height();
+ var scrollTop = codeView.scrollTop();
+ var percent = (scrollTop / codeView[0].scrollHeight);
+ var tocHeight = 0;
+
+ preview.find(".markdown-toc-list").each(function(){
+ tocHeight += $(this).height();
+ });
+
+ var tocMenuHeight = preview.find(".editormd-toc-menu").height();
+ tocMenuHeight = (!tocMenuHeight) ? 0 : tocMenuHeight;
+
+ if (scrollTop === 0)
+ {
+ preview.scrollTop(0);
+ }
+ else if (scrollTop + height >= codeView[0].scrollHeight - 16)
+ {
+ preview.scrollTop(preview[0].scrollHeight);
+ }
+ else
+ {
+ preview.scrollTop((preview[0].scrollHeight + tocHeight + tocMenuHeight) * percent);
+ }
+
+ return this;
+ },
+
+ /**
+ * 注册键盘快捷键处理
+ * Register CodeMirror keyMaps (keyboard shortcuts).
+ *
+ * @param {Object} keyMap KeyMap key/value {"(Ctrl/Shift/Alt)-Key" : function(){}}
+ * @returns {editormd} return this
+ */
+
+ registerKeyMaps : function(keyMap) {
+
+ var _this = this;
+ var cm = this.cm;
+ var settings = this.settings;
+ var toolbarHandlers = editormd.toolbarHandlers;
+ var disabledKeyMaps = settings.disabledKeyMaps;
+
+ keyMap = keyMap || null;
+
+ if (keyMap)
+ {
+ for (var i in keyMap)
+ {
+ if ($.inArray(i, disabledKeyMaps) < 0)
+ {
+ var map = {};
+ map[i] = keyMap[i];
+
+ cm.addKeyMap(keyMap);
+ }
+ }
+ }
+ else
+ {
+ for (var k in editormd.keyMaps)
+ {
+ var _keyMap = editormd.keyMaps[k];
+ var handle = (typeof _keyMap === "string") ? $.proxy(toolbarHandlers[_keyMap], _this) : $.proxy(_keyMap, _this);
+
+ if ($.inArray(k, ["F9", "F10", "F11"]) < 0 && $.inArray(k, disabledKeyMaps) < 0)
+ {
+ var _map = {};
+ _map[k] = handle;
+
+ cm.addKeyMap(_map);
+ }
+ }
+
+ $(window).keydown(function(event) {
+
+ var keymaps = {
+ "120" : "F9",
+ "121" : "F10",
+ "122" : "F11"
+ };
+
+ if ( $.inArray(keymaps[event.keyCode], disabledKeyMaps) < 0 )
+ {
+ switch (event.keyCode)
+ {
+ case 120:
+ $.proxy(toolbarHandlers["watch"], _this)();
+ return false;
+ break;
+
+ case 121:
+ $.proxy(toolbarHandlers["preview"], _this)();
+ return false;
+ break;
+
+ case 122:
+ $.proxy(toolbarHandlers["fullscreen"], _this)();
+ return false;
+ break;
+
+ default:
+ break;
+ }
+ }
+ });
+ }
+
+ return this;
+ },
+
+ /**
+ * 绑定同步滚动
+ *
+ * @returns {editormd} return this
+ */
+
+ bindScrollEvent : function() {
+
+ var _this = this;
+ var preview = this.preview;
+ var settings = this.settings;
+ var codeMirror = this.codeMirror;
+ var mouseOrTouch = editormd.mouseOrTouch;
+
+ if (!settings.syncScrolling) {
+ return this;
+ }
+
+ var cmBindScroll = function() {
+ codeMirror.find(".CodeMirror-scroll").bind(mouseOrTouch("scroll", "touchmove"), function(event) {
+ var height = $(this).height();
+ var scrollTop = $(this).scrollTop();
+ var percent = (scrollTop / $(this)[0].scrollHeight);
+
+ var tocHeight = 0;
+
+ preview.find(".markdown-toc-list").each(function(){
+ tocHeight += $(this).height();
+ });
+
+ var tocMenuHeight = preview.find(".editormd-toc-menu").height();
+ tocMenuHeight = (!tocMenuHeight) ? 0 : tocMenuHeight;
+
+ if (scrollTop === 0)
+ {
+ preview.scrollTop(0);
+ }
+ else if (scrollTop + height >= $(this)[0].scrollHeight - 16)
+ {
+ preview.scrollTop(preview[0].scrollHeight);
+ }
+ else
+ {
+ preview.scrollTop((preview[0].scrollHeight + tocHeight + tocMenuHeight) * percent);
+ }
+
+ $.proxy(settings.onscroll, _this)(event);
+ });
+ };
+
+ var cmUnbindScroll = function() {
+ codeMirror.find(".CodeMirror-scroll").unbind(mouseOrTouch("scroll", "touchmove"));
+ };
+
+ var previewBindScroll = function() {
+
+ preview.bind(mouseOrTouch("scroll", "touchmove"), function(event) {
+ var height = $(this).height();
+ var scrollTop = $(this).scrollTop();
+ var percent = (scrollTop / $(this)[0].scrollHeight);
+ var codeView = codeMirror.find(".CodeMirror-scroll");
+
+ if(scrollTop === 0)
+ {
+ codeView.scrollTop(0);
+ }
+ else if (scrollTop + height >= $(this)[0].scrollHeight)
+ {
+ codeView.scrollTop(codeView[0].scrollHeight);
+ }
+ else
+ {
+ codeView.scrollTop(codeView[0].scrollHeight * percent);
+ }
+
+ $.proxy(settings.onpreviewscroll, _this)(event);
+ });
+
+ };
+
+ var previewUnbindScroll = function() {
+ preview.unbind(mouseOrTouch("scroll", "touchmove"));
+ };
+
+ codeMirror.bind({
+ mouseover : cmBindScroll,
+ mouseout : cmUnbindScroll,
+ touchstart : cmBindScroll,
+ touchend : cmUnbindScroll
+ });
+
+ if (settings.syncScrolling === "single") {
+ return this;
+ }
+
+ preview.bind({
+ mouseover : previewBindScroll,
+ mouseout : previewUnbindScroll,
+ touchstart : previewBindScroll,
+ touchend : previewUnbindScroll
+ });
+
+ return this;
+ },
+
+ bindChangeEvent : function() {
+
+ var _this = this;
+ var cm = this.cm;
+ var settings = this.settings;
+
+ if (!settings.syncScrolling) {
+ return this;
+ }
+
+ cm.on("change", function(_cm, changeObj) {
+
+ if (settings.watch)
+ {
+ _this.previewContainer.css("padding", settings.autoHeight ? "20px 20px 50px 40px" : "20px");
+ }
+
+ timer = setTimeout(function() {
+ clearTimeout(timer);
+ _this.save();
+ timer = null;
+ }, settings.delay);
+ });
+
+ return this;
+ },
+
+ /**
+ * 加载队列完成之后的显示处理
+ * Display handle of the module queues loaded after.
+ *
+ * @param {Boolean} recreate 是否为重建编辑器
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ loadedDisplay : function(recreate) {
+
+ recreate = recreate || false;
+
+ var _this = this;
+ var editor = this.editor;
+ var preview = this.preview;
+ var settings = this.settings;
+
+ this.containerMask.hide();
+
+ this.save();
+
+ if (settings.watch) {
+ preview.show();
+ }
+
+ editor.data("oldWidth", editor.width()).data("oldHeight", editor.height()); // 为了兼容Zepto
+
+ this.resize();
+ this.registerKeyMaps();
+
+ $(window).resize(function(){
+ _this.resize();
+ });
+
+ this.bindScrollEvent().bindChangeEvent();
+
+ if (!recreate)
+ {
+ $.proxy(settings.onload, this)();
+ }
+
+ this.state.loaded = true;
+
+ return this;
+ },
+
+ /**
+ * 设置编辑器的宽度
+ * Set editor width
+ *
+ * @param {Number|String} width 编辑器宽度值
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ width : function(width) {
+
+ this.editor.css("width", (typeof width === "number") ? width + "px" : width);
+ this.resize();
+
+ return this;
+ },
+
+ /**
+ * 设置编辑器的高度
+ * Set editor height
+ *
+ * @param {Number|String} height 编辑器高度值
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ height : function(height) {
+
+ this.editor.css("height", (typeof height === "number") ? height + "px" : height);
+ this.resize();
+
+ return this;
+ },
+
+ /**
+ * 调整编辑器的尺寸和布局
+ * Resize editor layout
+ *
+ * @param {Number|String} [width=null] 编辑器宽度值
+ * @param {Number|String} [height=null] 编辑器高度值
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ resize : function(width, height) {
+
+ width = width || null;
+ height = height || null;
+
+ var state = this.state;
+ var editor = this.editor;
+ var preview = this.preview;
+ var toolbar = this.toolbar;
+ var settings = this.settings;
+ var codeMirror = this.codeMirror;
+
+ if (width)
+ {
+ editor.css("width", (typeof width === "number") ? width + "px" : width);
+ }
+
+ if (settings.autoHeight && !state.fullscreen && !state.preview)
+ {
+ editor.css("height", "auto");
+ codeMirror.css("height", "auto");
+ }
+ else
+ {
+ if (height)
+ {
+ editor.css("height", (typeof height === "number") ? height + "px" : height);
+ }
+
+ if (state.fullscreen)
+ {
+ editor.height($(window).height());
+ }
+
+ if (settings.toolbar && !settings.readOnly)
+ {
+ codeMirror.css("margin-top", toolbar.height() + 1).height(editor.height() - toolbar.height());
+ }
+ else
+ {
+ codeMirror.css("margin-top", 0).height(editor.height());
+ }
+ }
+
+ if(settings.watch)
+ {
+ codeMirror.width(editor.width() / 2);
+ preview.width((!state.preview) ? editor.width() / 2 : editor.width());
+
+ this.previewContainer.css("padding", settings.autoHeight ? "20px 20px 50px 40px" : "20px");
+
+ if (settings.toolbar && !settings.readOnly)
+ {
+ preview.css("top", toolbar.height() + 1);
+ }
+ else
+ {
+ preview.css("top", 0);
+ }
+
+ if (settings.autoHeight && !state.fullscreen && !state.preview)
+ {
+ preview.height("");
+ }
+ else
+ {
+ var previewHeight = (settings.toolbar && !settings.readOnly) ? editor.height() - toolbar.height() : editor.height();
+
+ preview.height(previewHeight);
+ }
+ }
+ else
+ {
+ codeMirror.width(editor.width());
+ preview.hide();
+ }
+
+ if (state.loaded)
+ {
+ $.proxy(settings.onresize, this)();
+ }
+
+ return this;
+ },
+
+ /**
+ * 解析和保存Markdown代码
+ * Parse & Saving Markdown source code
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ save : function() {
+
+ if (timer === null)
+ {
+ return this;
+ }
+
+ var _this = this;
+ var state = this.state;
+ var settings = this.settings;
+ var cm = this.cm;
+ var cmValue = cm.getValue();
+ var previewContainer = this.previewContainer;
+
+ if (settings.mode !== "gfm" && settings.mode !== "markdown")
+ {
+ this.markdownTextarea.val(cmValue);
+
+ return this;
+ }
+
+ var marked = editormd.$marked;
+ var markdownToC = this.markdownToC = [];
+ var rendererOptions = this.markedRendererOptions = {
+ toc : settings.toc,
+ tocm : settings.tocm,
+ tocStartLevel : settings.tocStartLevel,
+ pageBreak : settings.pageBreak,
+ taskList : settings.taskList,
+ emoji : settings.emoji,
+ tex : settings.tex,
+ atLink : settings.atLink, // for @link
+ emailLink : settings.emailLink, // for mail address auto link
+ flowChart : settings.flowChart,
+ sequenceDiagram : settings.sequenceDiagram,
+ previewCodeHighlight : settings.previewCodeHighlight,
+ };
+
+ var markedOptions = this.markedOptions = {
+ renderer : editormd.markedRenderer(markdownToC, rendererOptions),
+ gfm : true,
+ tables : true,
+ breaks : true,
+ pedantic : false,
+ sanitize : (settings.htmlDecode) ? false : true, // 关闭忽略HTML标签,即开启识别HTML标签,默认为false
+ smartLists : true,
+ smartypants : true
+ };
+
+ marked.setOptions(markedOptions);
+
+ var newMarkdownDoc = editormd.$marked(cmValue, markedOptions);
+
+ //console.info("cmValue", cmValue, newMarkdownDoc);
+
+ newMarkdownDoc = editormd.filterHTMLTags(newMarkdownDoc, settings.htmlDecode);
+
+ //console.error("cmValue", cmValue, newMarkdownDoc);
+
+ this.markdownTextarea.text(cmValue);
+
+ cm.save();
+
+ if (settings.saveHTMLToTextarea)
+ {
+ this.htmlTextarea.text(newMarkdownDoc);
+ }
+
+ if(settings.watch || (!settings.watch && state.preview))
+ {
+ previewContainer.html(newMarkdownDoc);
+
+ this.previewCodeHighlight();
+
+ if (settings.toc)
+ {
+ var tocContainer = (settings.tocContainer === "") ? previewContainer : $(settings.tocContainer);
+ var tocMenu = tocContainer.find("." + this.classPrefix + "toc-menu");
+
+ tocContainer.attr("previewContainer", (settings.tocContainer === "") ? "true" : "false");
+
+ if (settings.tocContainer !== "" && tocMenu.length > 0)
+ {
+ tocMenu.remove();
+ }
+
+ editormd.markdownToCRenderer(markdownToC, tocContainer, settings.tocDropdown, settings.tocStartLevel);
+
+ if (settings.tocDropdown || tocContainer.find("." + this.classPrefix + "toc-menu").length > 0)
+ {
+ editormd.tocDropdownMenu(tocContainer, (settings.tocTitle !== "") ? settings.tocTitle : this.lang.tocTitle);
+ }
+
+ if (settings.tocContainer !== "")
+ {
+ previewContainer.find(".markdown-toc").css("border", "none");
+ }
+ }
+
+ if (settings.tex)
+ {
+ if (!editormd.kaTeXLoaded && settings.autoLoadModules)
+ {
+ editormd.loadKaTeX(function() {
+ editormd.$katex = katex;
+ editormd.kaTeXLoaded = true;
+ _this.katexRender();
+ });
+ }
+ else
+ {
+ editormd.$katex = katex;
+ this.katexRender();
+ }
+ }
+
+ if (settings.flowChart || settings.sequenceDiagram)
+ {
+ flowchartTimer = setTimeout(function(){
+ clearTimeout(flowchartTimer);
+ _this.flowChartAndSequenceDiagramRender();
+ flowchartTimer = null;
+ }, 10);
+ }
+
+ if (state.loaded)
+ {
+ $.proxy(settings.onchange, this)();
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * 聚焦光标位置
+ * Focusing the cursor position
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ focus : function() {
+ this.cm.focus();
+
+ return this;
+ },
+
+ /**
+ * 设置光标的位置
+ * Set cursor position
+ *
+ * @param {Object} cursor 要设置的光标位置键值对象,例:{line:1, ch:0}
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setCursor : function(cursor) {
+ this.cm.setCursor(cursor);
+
+ return this;
+ },
+
+ /**
+ * 获取当前光标的位置
+ * Get the current position of the cursor
+ *
+ * @returns {Cursor} 返回一个光标Cursor对象
+ */
+
+ getCursor : function() {
+ return this.cm.getCursor();
+ },
+
+ /**
+ * 设置光标选中的范围
+ * Set cursor selected ranges
+ *
+ * @param {Object} from 开始位置的光标键值对象,例:{line:1, ch:0}
+ * @param {Object} to 结束位置的光标键值对象,例:{line:1, ch:0}
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setSelection : function(from, to) {
+
+ this.cm.setSelection(from, to);
+
+ return this;
+ },
+
+ /**
+ * 获取光标选中的文本
+ * Get the texts from cursor selected
+ *
+ * @returns {String} 返回选中文本的字符串形式
+ */
+
+ getSelection : function() {
+ return this.cm.getSelection();
+ },
+
+ /**
+ * 设置光标选中的文本范围
+ * Set the cursor selection ranges
+ *
+ * @param {Array} ranges cursor selection ranges array
+ * @returns {Array} return this
+ */
+
+ setSelections : function(ranges) {
+ this.cm.setSelections(ranges);
+
+ return this;
+ },
+
+ /**
+ * 获取光标选中的文本范围
+ * Get the cursor selection ranges
+ *
+ * @returns {Array} return selection ranges array
+ */
+
+ getSelections : function() {
+ return this.cm.getSelections();
+ },
+
+ /**
+ * 替换当前光标选中的文本或在当前光标处插入新字符
+ * Replace the text at the current cursor selected or insert a new character at the current cursor position
+ *
+ * @param {String} value 要插入的字符值
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ replaceSelection : function(value) {
+ this.cm.replaceSelection(value);
+
+ return this;
+ },
+
+ /**
+ * 在当前光标处插入新字符
+ * Insert a new character at the current cursor position
+ *
+ * 同replaceSelection()方法
+ * With the replaceSelection() method
+ *
+ * @param {String} value 要插入的字符值
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ insertValue : function(value) {
+ this.replaceSelection(value);
+
+ return this;
+ },
+
+ /**
+ * 追加markdown
+ * append Markdown to editor
+ *
+ * @param {String} md 要追加的markdown源文档
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ appendMarkdown : function(md) {
+ var settings = this.settings;
+ var cm = this.cm;
+
+ cm.setValue(cm.getValue() + md);
+
+ return this;
+ },
+
+ /**
+ * 设置和传入编辑器的markdown源文档
+ * Set Markdown source document
+ *
+ * @param {String} md 要传入的markdown源文档
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setMarkdown : function(md) {
+ this.cm.setValue(md || this.settings.markdown);
+
+ return this;
+ },
+
+ /**
+ * 获取编辑器的markdown源文档
+ * Set Editor.md markdown/CodeMirror value
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ getMarkdown : function() {
+ return this.cm.getValue();
+ },
+
+ /**
+ * 获取编辑器的源文档
+ * Get CodeMirror value
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ getValue : function() {
+ return this.cm.getValue();
+ },
+
+ /**
+ * 设置编辑器的源文档
+ * Set CodeMirror value
+ *
+ * @param {String} value set code/value/string/text
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ setValue : function(value) {
+ this.cm.setValue(value);
+
+ return this;
+ },
+
+ /**
+ * 清空编辑器
+ * Empty CodeMirror editor container
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ clear : function() {
+ this.cm.setValue("");
+
+ return this;
+ },
+
+ /**
+ * 获取解析后存放在Textarea的HTML源码
+ * Get parsed html code from Textarea
+ *
+ * @returns {String} 返回HTML源码
+ */
+
+ getHTML : function() {
+ if (!this.settings.saveHTMLToTextarea)
+ {
+ alert("Error: settings.saveHTMLToTextarea == false");
+
+ return false;
+ }
+
+ return this.htmlTextarea.val();
+ },
+
+ /**
+ * getHTML()的别名
+ * getHTML (alias)
+ *
+ * @returns {String} Return html code 返回HTML源码
+ */
+
+ getTextareaSavedHTML : function() {
+ return this.getHTML();
+ },
+
+ /**
+ * 获取预览窗口的HTML源码
+ * Get html from preview container
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ getPreviewedHTML : function() {
+ if (!this.settings.watch)
+ {
+ alert("Error: settings.watch == false");
+
+ return false;
+ }
+
+ return this.previewContainer.html();
+ },
+
+ /**
+ * 开启实时预览
+ * Enable real-time watching
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ watch : function(callback) {
+ var settings = this.settings;
+
+ if ($.inArray(settings.mode, ["gfm", "markdown"]) < 0)
+ {
+ return this;
+ }
+
+ this.state.watching = settings.watch = true;
+ this.preview.show();
+
+ if (this.toolbar)
+ {
+ var watchIcon = settings.toolbarIconsClass.watch;
+ var unWatchIcon = settings.toolbarIconsClass.unwatch;
+
+ var icon = this.toolbar.find(".fa[name=watch]");
+ icon.parent().attr("title", settings.lang.toolbar.watch);
+ icon.removeClass(unWatchIcon).addClass(watchIcon);
+ }
+
+ this.codeMirror.css("border-right", "1px solid #ddd").width(this.editor.width() / 2);
+
+ timer = 0;
+
+ this.save().resize();
+
+ if (!settings.onwatch)
+ {
+ settings.onwatch = callback || function() {};
+ }
+
+ $.proxy(settings.onwatch, this)();
+
+ return this;
+ },
+
+ /**
+ * 关闭实时预览
+ * Disable real-time watching
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ unwatch : function(callback) {
+ var settings = this.settings;
+ this.state.watching = settings.watch = false;
+ this.preview.hide();
+
+ if (this.toolbar)
+ {
+ var watchIcon = settings.toolbarIconsClass.watch;
+ var unWatchIcon = settings.toolbarIconsClass.unwatch;
+
+ var icon = this.toolbar.find(".fa[name=watch]");
+ icon.parent().attr("title", settings.lang.toolbar.unwatch);
+ icon.removeClass(watchIcon).addClass(unWatchIcon);
+ }
+
+ this.codeMirror.css("border-right", "none").width(this.editor.width());
+
+ this.resize();
+
+ if (!settings.onunwatch)
+ {
+ settings.onunwatch = callback || function() {};
+ }
+
+ $.proxy(settings.onunwatch, this)();
+
+ return this;
+ },
+
+ /**
+ * 显示编辑器
+ * Show editor
+ *
+ * @param {Function} [callback=function()] 回调函数
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ show : function(callback) {
+ callback = callback || function() {};
+
+ var _this = this;
+ this.editor.show(0, function() {
+ $.proxy(callback, _this)();
+ });
+
+ return this;
+ },
+
+ /**
+ * 隐藏编辑器
+ * Hide editor
+ *
+ * @param {Function} [callback=function()] 回调函数
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ hide : function(callback) {
+ callback = callback || function() {};
+
+ var _this = this;
+ this.editor.hide(0, function() {
+ $.proxy(callback, _this)();
+ });
+
+ return this;
+ },
+
+ /**
+ * 隐藏编辑器部分,只预览HTML
+ * Enter preview html state
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ previewing : function() {
+
+ var _this = this;
+ var editor = this.editor;
+ var preview = this.preview;
+ var toolbar = this.toolbar;
+ var settings = this.settings;
+ var codeMirror = this.codeMirror;
+ var previewContainer = this.previewContainer;
+
+ if ($.inArray(settings.mode, ["gfm", "markdown"]) < 0) {
+ return this;
+ }
+
+ if (settings.toolbar && toolbar) {
+ toolbar.toggle();
+ toolbar.find(".fa[name=preview]").toggleClass("active");
+ }
+
+ codeMirror.toggle();
+
+ var escHandle = function(event) {
+ if (event.shiftKey && event.keyCode === 27) {
+ _this.previewed();
+ }
+ };
+
+ if (codeMirror.css("display") === "none") // 为了兼容Zepto,而不使用codeMirror.is(":hidden")
+ {
+ this.state.preview = true;
+
+ if (this.state.fullscreen) {
+ preview.css("background", "#fff");
+ }
+
+ editor.find("." + this.classPrefix + "preview-close-btn").show().bind(editormd.mouseOrTouch("click", "touchend"), function(){
+ _this.previewed();
+ });
+
+ if (!settings.watch)
+ {
+ this.save();
+ }
+ else
+ {
+ previewContainer.css("padding", "");
+ }
+
+ previewContainer.addClass(this.classPrefix + "preview-active");
+
+ preview.show().css({
+ position : "",
+ top : 0,
+ width : editor.width(),
+ height : (settings.autoHeight && !this.state.fullscreen) ? "auto" : editor.height()
+ });
+
+ if (this.state.loaded)
+ {
+ $.proxy(settings.onpreviewing, this)();
+ }
+
+ $(window).bind("keyup", escHandle);
+ }
+ else
+ {
+ $(window).unbind("keyup", escHandle);
+ this.previewed();
+ }
+ },
+
+ /**
+ * 显示编辑器部分,退出只预览HTML
+ * Exit preview html state
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ previewed : function() {
+
+ var editor = this.editor;
+ var preview = this.preview;
+ var toolbar = this.toolbar;
+ var settings = this.settings;
+ var previewContainer = this.previewContainer;
+ var previewCloseBtn = editor.find("." + this.classPrefix + "preview-close-btn");
+
+ this.state.preview = false;
+
+ this.codeMirror.show();
+
+ if (settings.toolbar) {
+ toolbar.show();
+ }
+
+ preview[(settings.watch) ? "show" : "hide"]();
+
+ previewCloseBtn.hide().unbind(editormd.mouseOrTouch("click", "touchend"));
+
+ previewContainer.removeClass(this.classPrefix + "preview-active");
+
+ if (settings.watch)
+ {
+ previewContainer.css("padding", "20px");
+ }
+
+ preview.css({
+ background : null,
+ position : "absolute",
+ width : editor.width() / 2,
+ height : (settings.autoHeight && !this.state.fullscreen) ? "auto" : editor.height() - toolbar.height(),
+ top : (settings.toolbar) ? toolbar.height() : 0
+ });
+
+ if (this.state.loaded)
+ {
+ $.proxy(settings.onpreviewed, this)();
+ }
+
+ return this;
+ },
+
+ /**
+ * 编辑器全屏显示
+ * Fullscreen show
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ fullscreen : function() {
+
+ var _this = this;
+ var state = this.state;
+ var editor = this.editor;
+ var preview = this.preview;
+ var toolbar = this.toolbar;
+ var settings = this.settings;
+ var fullscreenClass = this.classPrefix + "fullscreen";
+
+ if (toolbar) {
+ toolbar.find(".fa[name=fullscreen]").parent().toggleClass("active");
+ }
+
+ var escHandle = function(event) {
+ if (!event.shiftKey && event.keyCode === 27)
+ {
+ if (state.fullscreen)
+ {
+ _this.fullscreenExit();
+ }
+ }
+ };
+
+ if (!editor.hasClass(fullscreenClass))
+ {
+ state.fullscreen = true;
+
+ $("html,body").css("overflow", "hidden");
+
+ editor.css({
+ width : $(window).width(),
+ height : $(window).height()
+ }).addClass(fullscreenClass);
+
+ this.resize();
+
+ $.proxy(settings.onfullscreen, this)();
+
+ $(window).bind("keyup", escHandle);
+ }
+ else
+ {
+ $(window).unbind("keyup", escHandle);
+ this.fullscreenExit();
+ }
+
+ return this;
+ },
+
+ /**
+ * 编辑器退出全屏显示
+ * Exit fullscreen state
+ *
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ fullscreenExit : function() {
+
+ var editor = this.editor;
+ var settings = this.settings;
+ var toolbar = this.toolbar;
+ var fullscreenClass = this.classPrefix + "fullscreen";
+
+ this.state.fullscreen = false;
+
+ if (toolbar) {
+ toolbar.find(".fa[name=fullscreen]").parent().removeClass("active");
+ }
+
+ $("html,body").css("overflow", "");
+
+ editor.css({
+ width : editor.data("oldWidth"),
+ height : editor.data("oldHeight")
+ }).removeClass(fullscreenClass);
+
+ this.resize();
+
+ $.proxy(settings.onfullscreenExit, this)();
+
+ return this;
+ },
+
+ /**
+ * 加载并执行插件
+ * Load and execute the plugin
+ *
+ * @param {String} name plugin name / function name
+ * @param {String} path plugin load path
+ * @returns {editormd} 返回editormd的实例对象
+ */
+
+ executePlugin : function(name, path) {
+
+ var _this = this;
+ var cm = this.cm;
+ var settings = this.settings;
+
+ path = settings.pluginPath + path;
+
+ if (typeof define === "function")
+ {
+ if (typeof this[name] === "undefined")
+ {
+ alert("Error: " + name + " plugin is not found, you are not load this plugin.");
+
+ return this;
+ }
+
+ this[name](cm);
+
+ return this;
+ }
+
+ if ($.inArray(path, editormd.loadFiles.plugin) < 0)
+ {
+ editormd.loadPlugin(path, function() {
+ editormd.loadPlugins[name] = _this[name];
+ _this[name](cm);
+ });
+ }
+ else
+ {
+ $.proxy(editormd.loadPlugins[name], this)(cm);
+ }
+
+ return this;
+ },
+
+ /**
+ * 搜索替换
+ * Search & replace
+ *
+ * @param {String} command CodeMirror serach commands, "find, fintNext, fintPrev, clearSearch, replace, replaceAll"
+ * @returns {editormd} return this
+ */
+
+ search : function(command) {
+ var settings = this.settings;
+
+ if (!settings.searchReplace)
+ {
+ alert("Error: settings.searchReplace == false");
+ return this;
+ }
+
+ if (!settings.readOnly)
+ {
+ this.cm.execCommand(command || "find");
+ }
+
+ return this;
+ },
+
+ searchReplace : function() {
+ this.search("replace");
+
+ return this;
+ },
+
+ searchReplaceAll : function() {
+ this.search("replaceAll");
+
+ return this;
+ }
+ };
+
+ editormd.fn.init.prototype = editormd.fn;
+
+ /**
+ * 锁屏
+ * lock screen when dialog opening
+ *
+ * @returns {void}
+ */
+
+ editormd.dialogLockScreen = function() {
+ var settings = this.settings || {dialogLockScreen : true};
+
+ if (settings.dialogLockScreen)
+ {
+ $("html,body").css("overflow", "hidden");
+ this.resize();
+ }
+ };
+
+ /**
+ * 显示透明背景层
+ * Display mask layer when dialog opening
+ *
+ * @param {Object} dialog dialog jQuery object
+ * @returns {void}
+ */
+
+ editormd.dialogShowMask = function(dialog) {
+ var editor = this.editor;
+ var settings = this.settings || {dialogShowMask : true};
+
+ dialog.css({
+ top : ($(window).height() - dialog.height()) / 2 + "px",
+ left : ($(window).width() - dialog.width()) / 2 + "px"
+ });
+
+ if (settings.dialogShowMask) {
+ editor.children("." + this.classPrefix + "mask").css("z-index", parseInt(dialog.css("z-index")) - 1).show();
+ }
+ };
+
+ editormd.toolbarHandlers = {
+ undo : function() {
+ this.cm.undo();
+ },
+
+ redo : function() {
+ this.cm.redo();
+ },
+
+ bold : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ cm.replaceSelection("**" + selection + "**");
+
+ if(selection === "") {
+ cm.setCursor(cursor.line, cursor.ch + 2);
+ }
+ },
+
+ del : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ cm.replaceSelection("~~" + selection + "~~");
+
+ if(selection === "") {
+ cm.setCursor(cursor.line, cursor.ch + 2);
+ }
+ },
+
+ italic : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ cm.replaceSelection("*" + selection + "*");
+
+ if(selection === "") {
+ cm.setCursor(cursor.line, cursor.ch + 1);
+ }
+ },
+
+ quote : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if (cursor.ch !== 0)
+ {
+ cm.setCursor(cursor.line, 0);
+ cm.replaceSelection("> " + selection);
+ cm.setCursor(cursor.line, cursor.ch + 2);
+ }
+ else
+ {
+ cm.replaceSelection("> " + selection);
+ }
+
+ //cm.replaceSelection("> " + selection);
+ //cm.setCursor(cursor.line, (selection === "") ? cursor.ch + 2 : cursor.ch + selection.length + 2);
+ },
+
+ ucfirst : function() {
+ var cm = this.cm;
+ var selection = cm.getSelection();
+ var selections = cm.listSelections();
+
+ cm.replaceSelection(editormd.firstUpperCase(selection));
+ cm.setSelections(selections);
+ },
+
+ ucwords : function() {
+ var cm = this.cm;
+ var selection = cm.getSelection();
+ var selections = cm.listSelections();
+
+ cm.replaceSelection(editormd.wordsFirstUpperCase(selection));
+ cm.setSelections(selections);
+ },
+
+ uppercase : function() {
+ var cm = this.cm;
+ var selection = cm.getSelection();
+ var selections = cm.listSelections();
+
+ cm.replaceSelection(selection.toUpperCase());
+ cm.setSelections(selections);
+ },
+
+ lowercase : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+ var selections = cm.listSelections();
+
+ cm.replaceSelection(selection.toLowerCase());
+ cm.setSelections(selections);
+ },
+
+ h1 : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if (cursor.ch !== 0)
+ {
+ cm.setCursor(cursor.line, 0);
+ cm.replaceSelection("# " + selection);
+ cm.setCursor(cursor.line, cursor.ch + 2);
+ }
+ else
+ {
+ cm.replaceSelection("# " + selection);
+ }
+ },
+
+ h2 : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if (cursor.ch !== 0)
+ {
+ cm.setCursor(cursor.line, 0);
+ cm.replaceSelection("## " + selection);
+ cm.setCursor(cursor.line, cursor.ch + 3);
+ }
+ else
+ {
+ cm.replaceSelection("## " + selection);
+ }
+ },
+
+ h3 : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if (cursor.ch !== 0)
+ {
+ cm.setCursor(cursor.line, 0);
+ cm.replaceSelection("### " + selection);
+ cm.setCursor(cursor.line, cursor.ch + 4);
+ }
+ else
+ {
+ cm.replaceSelection("### " + selection);
+ }
+ },
+
+ h4 : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if (cursor.ch !== 0)
+ {
+ cm.setCursor(cursor.line, 0);
+ cm.replaceSelection("#### " + selection);
+ cm.setCursor(cursor.line, cursor.ch + 5);
+ }
+ else
+ {
+ cm.replaceSelection("#### " + selection);
+ }
+ },
+
+ h5 : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if (cursor.ch !== 0)
+ {
+ cm.setCursor(cursor.line, 0);
+ cm.replaceSelection("##### " + selection);
+ cm.setCursor(cursor.line, cursor.ch + 6);
+ }
+ else
+ {
+ cm.replaceSelection("##### " + selection);
+ }
+ },
+
+ h6 : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if (cursor.ch !== 0)
+ {
+ cm.setCursor(cursor.line, 0);
+ cm.replaceSelection("###### " + selection);
+ cm.setCursor(cursor.line, cursor.ch + 7);
+ }
+ else
+ {
+ cm.replaceSelection("###### " + selection);
+ }
+ },
+
+ "list-ul" : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if (selection === "")
+ {
+ cm.replaceSelection("- " + selection);
+ }
+ else
+ {
+ var selectionText = selection.split("\n");
+
+ for (var i = 0, len = selectionText.length; i < len; i++)
+ {
+ selectionText[i] = (selectionText[i] === "") ? "" : "- " + selectionText[i];
+ }
+
+ cm.replaceSelection(selectionText.join("\n"));
+ }
+ },
+
+ "list-ol" : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if(selection === "")
+ {
+ cm.replaceSelection("1. " + selection);
+ }
+ else
+ {
+ var selectionText = selection.split("\n");
+
+ for (var i = 0, len = selectionText.length; i < len; i++)
+ {
+ selectionText[i] = (selectionText[i] === "") ? "" : (i+1) + ". " + selectionText[i];
+ }
+
+ cm.replaceSelection(selectionText.join("\n"));
+ }
+ },
+
+ hr : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ cm.replaceSelection(((cursor.ch !== 0) ? "\n\n" : "\n") + "------------\n\n");
+ },
+
+ tex : function() {
+ if (!this.settings.tex)
+ {
+ alert("settings.tex === false");
+ return this;
+ }
+
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ cm.replaceSelection("$$" + selection + "$$");
+
+ if(selection === "") {
+ cm.setCursor(cursor.line, cursor.ch + 2);
+ }
+ },
+
+ link : function() {
+ this.executePlugin("linkDialog", "link-dialog/link-dialog");
+ },
+
+ "reference-link" : function() {
+ this.executePlugin("referenceLinkDialog", "reference-link-dialog/reference-link-dialog");
+ },
+
+ pagebreak : function() {
+ if (!this.settings.pageBreak)
+ {
+ alert("settings.pageBreak === false");
+ return this;
+ }
+
+ var cm = this.cm;
+ var selection = cm.getSelection();
+
+ cm.replaceSelection("\r\n[========]\r\n");
+ },
+
+ image : function() {
+ this.executePlugin("imageDialog", "image-dialog/image-dialog");
+ },
+
+ code : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ cm.replaceSelection("`" + selection + "`");
+
+ if (selection === "") {
+ cm.setCursor(cursor.line, cursor.ch + 1);
+ }
+ },
+
+ "code-block" : function() {
+ this.executePlugin("codeBlockDialog", "code-block-dialog/code-block-dialog");
+ },
+
+ "preformatted-text" : function() {
+ this.executePlugin("preformattedTextDialog", "preformatted-text-dialog/preformatted-text-dialog");
+ },
+
+ table : function() {
+ this.executePlugin("tableDialog", "table-dialog/table-dialog");
+ },
+
+ datetime : function() {
+ var cm = this.cm;
+ var selection = cm.getSelection();
+ var date = new Date();
+ var langName = this.settings.lang.name;
+ var datefmt = editormd.dateFormat() + " " + editormd.dateFormat((langName === "zh-cn" || langName === "zh-tw") ? "cn-week-day" : "week-day");
+
+ cm.replaceSelection(datefmt);
+ },
+
+ emoji : function() {
+ this.executePlugin("emojiDialog", "emoji-dialog/emoji-dialog");
+ },
+
+ "html-entities" : function() {
+ this.executePlugin("htmlEntitiesDialog", "html-entities-dialog/html-entities-dialog");
+ },
+
+ "goto-line" : function() {
+ this.executePlugin("gotoLineDialog", "goto-line-dialog/goto-line-dialog");
+ },
+
+ watch : function() {
+ this[this.settings.watch ? "unwatch" : "watch"]();
+ },
+
+ preview : function() {
+ this.previewing();
+ },
+
+ fullscreen : function() {
+ this.fullscreen();
+ },
+
+ clear : function() {
+ this.clear();
+ },
+
+ search : function() {
+ this.search();
+ },
+
+ help : function() {
+ this.executePlugin("helpDialog", "help-dialog/help-dialog");
+ },
+
+ info : function() {
+ this.showInfoDialog();
+ }
+ };
+
+ editormd.keyMaps = {
+ "Ctrl-1" : "h1",
+ "Ctrl-2" : "h2",
+ "Ctrl-3" : "h3",
+ "Ctrl-4" : "h4",
+ "Ctrl-5" : "h5",
+ "Ctrl-6" : "h6",
+ "Ctrl-B" : "bold", // if this is string == editormd.toolbarHandlers.xxxx
+ "Ctrl-D" : "datetime",
+
+ "Ctrl-E" : function() { // emoji
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if (!this.settings.emoji)
+ {
+ alert("Error: settings.emoji == false");
+ return ;
+ }
+
+ cm.replaceSelection(":" + selection + ":");
+
+ if (selection === "") {
+ cm.setCursor(cursor.line, cursor.ch + 1);
+ }
+ },
+ "Ctrl-Alt-G" : "goto-line",
+ "Ctrl-H" : "hr",
+ "Ctrl-I" : "italic",
+ "Ctrl-K" : "code",
+
+ "Ctrl-L" : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ var title = (selection === "") ? "" : " \""+selection+"\"";
+
+ cm.replaceSelection("[" + selection + "]("+title+")");
+
+ if (selection === "") {
+ cm.setCursor(cursor.line, cursor.ch + 1);
+ }
+ },
+ "Ctrl-U" : "list-ul",
+
+ "Shift-Ctrl-A" : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ if (!this.settings.atLink)
+ {
+ alert("Error: settings.atLink == false");
+ return ;
+ }
+
+ cm.replaceSelection("@" + selection);
+
+ if (selection === "") {
+ cm.setCursor(cursor.line, cursor.ch + 1);
+ }
+ },
+
+ "Shift-Ctrl-C" : "code",
+ "Shift-Ctrl-Q" : "quote",
+ "Shift-Ctrl-S" : "del",
+ "Shift-Ctrl-K" : "tex", // KaTeX
+
+ "Shift-Alt-C" : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ cm.replaceSelection(["```", selection, "```"].join("\n"));
+
+ if (selection === "") {
+ cm.setCursor(cursor.line, cursor.ch + 3);
+ }
+ },
+
+ "Shift-Ctrl-Alt-C" : "code-block",
+ "Shift-Ctrl-H" : "html-entities",
+ "Shift-Alt-H" : "help",
+ "Shift-Ctrl-E" : "emoji",
+ "Shift-Ctrl-U" : "uppercase",
+ "Shift-Alt-U" : "ucwords",
+ "Shift-Ctrl-Alt-U" : "ucfirst",
+ "Shift-Alt-L" : "lowercase",
+
+ "Shift-Ctrl-I" : function() {
+ var cm = this.cm;
+ var cursor = cm.getCursor();
+ var selection = cm.getSelection();
+
+ var title = (selection === "") ? "" : " \""+selection+"\"";
+
+ cm.replaceSelection("![" + selection + "]("+title+")");
+
+ if (selection === "") {
+ cm.setCursor(cursor.line, cursor.ch + 4);
+ }
+ },
+
+ "Shift-Ctrl-Alt-I" : "image",
+ "Shift-Ctrl-L" : "link",
+ "Shift-Ctrl-O" : "list-ol",
+ "Shift-Ctrl-P" : "preformatted-text",
+ "Shift-Ctrl-T" : "table",
+ "Shift-Alt-P" : "pagebreak",
+ "F9" : "watch",
+ "F10" : "preview",
+ "F11" : "fullscreen",
+ };
+
+ /**
+ * 清除字符串两边的空格
+ * Clear the space of strings both sides.
+ *
+ * @param {String} str string
+ * @returns {String} trimed string
+ */
+
+ var trim = function(str) {
+ return (!String.prototype.trim) ? str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "") : str.trim();
+ };
+
+ editormd.trim = trim;
+
+ /**
+ * 所有单词首字母大写
+ * Words first to uppercase
+ *
+ * @param {String} str string
+ * @returns {String} string
+ */
+
+ var ucwords = function (str) {
+ return str.toLowerCase().replace(/\b(\w)|\s(\w)/g, function($1) {
+ return $1.toUpperCase();
+ });
+ };
+
+ editormd.ucwords = editormd.wordsFirstUpperCase = ucwords;
+
+ /**
+ * 字符串首字母大写
+ * Only string first char to uppercase
+ *
+ * @param {String} str string
+ * @returns {String} string
+ */
+
+ var firstUpperCase = function(str) {
+ return str.toLowerCase().replace(/\b(\w)/, function($1){
+ return $1.toUpperCase();
+ });
+ };
+
+ var ucfirst = firstUpperCase;
+
+ editormd.firstUpperCase = editormd.ucfirst = firstUpperCase;
+
+ editormd.urls = {
+ atLinkBase : "https://github.com/"
+ };
+
+ editormd.regexs = {
+ atLink : /@(\w+)/g,
+ email : /(\w+)@(\w+)\.(\w+)\.?(\w+)?/g,
+ emailLink : /(mailto:)?([\w\.\_]+)@(\w+)\.(\w+)\.?(\w+)?/g,
+ emoji : /:([\w\+-]+):/g,
+ emojiDatetime : /(\d{2}:\d{2}:\d{2})/g,
+ twemoji : /:(tw-([\w]+)-?(\w+)?):/g,
+ fontAwesome : /:(fa-([\w]+)(-(\w+)){0,}):/g,
+ editormdLogo : /:(editormd-logo-?(\w+)?):/g,
+ pageBreak : /^\[[=]{8,}\]$/
+ };
+
+ // Emoji graphics files url path
+ editormd.emoji = {
+ path : "http://www.emoji-cheat-sheet.com/graphics/emojis/",
+ ext : ".png"
+ };
+
+ // Twitter Emoji (Twemoji) graphics files url path
+ editormd.twemoji = {
+ path : "http://twemoji.maxcdn.com/36x36/",
+ ext : ".png"
+ };
+
+ /**
+ * 自定义marked的解析器
+ * Custom Marked renderer rules
+ *
+ * @param {Array} markdownToC 传入用于接收TOC的数组
+ * @returns {Renderer} markedRenderer 返回marked的Renderer自定义对象
+ */
+
+ editormd.markedRenderer = function(markdownToC, options) {
+ var defaults = {
+ toc : true, // Table of contents
+ tocm : false,
+ tocStartLevel : 1, // Said from H1 to create ToC
+ pageBreak : true,
+ atLink : true, // for @link
+ emailLink : true, // for mail address auto link
+ taskList : false, // Enable Github Flavored Markdown task lists
+ emoji : false, // :emoji: , Support Twemoji, fontAwesome, Editor.md logo emojis.
+ tex : false, // TeX(LaTeX), based on KaTeX
+ flowChart : false, // flowChart.js only support IE9+
+ sequenceDiagram : false, // sequenceDiagram.js only support IE9+
+ };
+
+ var settings = $.extend(defaults, options || {});
+ var marked = editormd.$marked;
+ var markedRenderer = new marked.Renderer();
+ markdownToC = markdownToC || [];
+
+ var regexs = editormd.regexs;
+ var atLinkReg = regexs.atLink;
+ var emojiReg = regexs.emoji;
+ var emailReg = regexs.email;
+ var emailLinkReg = regexs.emailLink;
+ var twemojiReg = regexs.twemoji;
+ var faIconReg = regexs.fontAwesome;
+ var editormdLogoReg = regexs.editormdLogo;
+ var pageBreakReg = regexs.pageBreak;
+
+ markedRenderer.emoji = function(text) {
+
+ text = text.replace(editormd.regexs.emojiDatetime, function($1) {
+ return $1.replace(/:/g, ":");
+ });
+
+ var matchs = text.match(emojiReg);
+
+ if (!matchs || !settings.emoji) {
+ return text;
+ }
+
+ for (var i = 0, len = matchs.length; i < len; i++)
+ {
+ if (matchs[i] === ":+1:") {
+ matchs[i] = ":\\+1:";
+ }
+
+ text = text.replace(new RegExp(matchs[i]), function($1, $2){
+ var faMatchs = $1.match(faIconReg);
+ var name = $1.replace(/:/g, "");
+
+ if (faMatchs)
+ {
+ for (var fa = 0, len1 = faMatchs.length; fa < len1; fa++)
+ {
+ var faName = faMatchs[fa].replace(/:/g, "");
+
+ return " ";
+ }
+ }
+ else
+ {
+ var emdlogoMathcs = $1.match(editormdLogoReg);
+ var twemojiMatchs = $1.match(twemojiReg);
+
+ if (emdlogoMathcs)
+ {
+ for (var x = 0, len2 = emdlogoMathcs.length; x < len2; x++)
+ {
+ var logoName = emdlogoMathcs[x].replace(/:/g, "");
+ return " ";
+ }
+ }
+ else if (twemojiMatchs)
+ {
+ for (var t = 0, len3 = twemojiMatchs.length; t < len3; t++)
+ {
+ var twe = twemojiMatchs[t].replace(/:/g, "").replace("tw-", "");
+ return " ";
+ }
+ }
+ else
+ {
+ var src = (name === "+1") ? "plus1" : name;
+ src = (src === "black_large_square") ? "black_square" : src;
+ src = (src === "moon") ? "waxing_gibbous_moon" : src;
+
+ return " ";
+ }
+ }
+ });
+ }
+
+ return text;
+ };
+
+ markedRenderer.atLink = function(text) {
+
+ if (atLinkReg.test(text))
+ {
+ if (settings.atLink)
+ {
+ text = text.replace(emailReg, function($1, $2, $3, $4) {
+ return $1.replace(/@/g, "_#_@_#_");
+ });
+
+ text = text.replace(atLinkReg, function($1, $2) {
+ return "" + $1 + " ";
+ }).replace(/_#_@_#_/g, "@");
+ }
+
+ if (settings.emailLink)
+ {
+ text = text.replace(emailLinkReg, function($1, $2, $3, $4, $5) {
+ return (!$2 && $.inArray($5, "jpg|jpeg|png|gif|webp|ico|icon|pdf".split("|")) < 0) ? ""+$1+" " : $1;
+ });
+ }
+
+ return text;
+ }
+
+ return text;
+ };
+
+ markedRenderer.link = function (href, title, text) {
+
+ if (this.options.sanitize) {
+ try {
+ var prot = decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase();
+ } catch(e) {
+ return "";
+ }
+
+ if (prot.indexOf("javascript:") === 0) {
+ return "";
+ }
+ }
+
+ var out = "" + text.replace(/@/g, "@") + " ";
+ }
+
+ if (title) {
+ out += " title=\"" + title + "\"";
+ }
+
+ out += ">" + text + "";
+
+ return out;
+ };
+
+ markedRenderer.heading = function(text, level, raw) {
+
+ var linkText = text;
+ var hasLinkReg = /\s*\]*)\>(.*)\<\/a\>\s*/;
+ var getLinkTextReg = /\s*\ ]+)\>([^\>]*)\<\/a\>\s*/g;
+
+ if (hasLinkReg.test(text))
+ {
+ var tempText = [];
+ text = text.split(/\ ]+)\>([^\>]*)\<\/a\>/);
+
+ for (var i = 0, len = text.length; i < len; i++)
+ {
+ tempText.push(text[i].replace(/\s*href\=\"(.*)\"\s*/g, ""));
+ }
+
+ text = tempText.join(" ");
+ }
+
+ text = trim(text);
+
+ var escapedText = text.toLowerCase().replace(/[^\w]+/g, "-");
+ var toc = {
+ text : text,
+ level : level,
+ slug : escapedText
+ };
+
+ var isChinese = /^[\u4e00-\u9fa5]+$/.test(text);
+ var id = (isChinese) ? escape(text).replace(/\%/g, "") : text.toLowerCase().replace(/[^\w]+/g, "-");
+
+ markdownToC.push(toc);
+
+ var headingHTML = "";
+
+ headingHTML += " ";
+ headingHTML += "";
+ headingHTML += (hasLinkReg) ? this.atLink(this.emoji(linkText)) : this.atLink(this.emoji(text));
+ headingHTML += " ";
+
+ return headingHTML;
+ };
+
+ markedRenderer.pageBreak = function(text) {
+ if (pageBreakReg.test(text) && settings.pageBreak)
+ {
+ text = " ";
+ }
+
+ return text;
+ };
+
+ markedRenderer.paragraph = function(text) {
+ var isTeXInline = /\$\$(.*)\$\$/g.test(text);
+ var isTeXLine = /^\$\$(.*)\$\$$/.test(text);
+ var isTeXAddClass = (isTeXLine) ? " class=\"" + editormd.classNames.tex + "\"" : "";
+ var isToC = (settings.tocm) ? /^(\[TOC\]|\[TOCM\])$/.test(text) : /^\[TOC\]$/.test(text);
+ var isToCMenu = /^\[TOCM\]$/.test(text);
+
+ if (!isTeXLine && isTeXInline)
+ {
+ text = text.replace(/(\$\$([^\$]*)\$\$)+/g, function($1, $2) {
+ return "" + $2.replace(/\$/g, "") + " ";
+ });
+ }
+ else
+ {
+ text = (isTeXLine) ? text.replace(/\$/g, "") : text;
+ }
+
+ var tocHTML = "" + text + "
";
+
+ return (isToC) ? ( (isToCMenu) ? " " : tocHTML )
+ : ( (pageBreakReg.test(text)) ? this.pageBreak(text) : "" + this.atLink(this.emoji(text)) + "
\n" );
+ };
+
+ markedRenderer.code = function (code, lang, escaped) {
+
+ if (lang === "seq" || lang === "sequence")
+ {
+ return "" + code + "
";
+ }
+ else if ( lang === "flow")
+ {
+ return "" + code + "
";
+ }
+ else if ( lang === "math" || lang === "latex" || lang === "katex")
+ {
+ return "" + code + "
";
+ }
+ else
+ {
+
+ return marked.Renderer.prototype.code.apply(this, arguments);
+ }
+ };
+
+ markedRenderer.tablecell = function(content, flags) {
+ var type = (flags.header) ? "th" : "td";
+ var tag = (flags.align) ? "<" + type +" style=\"text-align:" + flags.align + "\">" : "<" + type + ">";
+
+ return tag + this.atLink(this.emoji(content)) + "" + type + ">\n";
+ };
+
+ markedRenderer.listitem = function(text) {
+ if (settings.taskList && /^\s*\[[x\s]\]\s*/.test(text))
+ {
+ text = text.replace(/^\s*\[\s\]\s*/, " ")
+ .replace(/^\s*\[x\]\s*/, " ");
+
+ return "" + this.atLink(this.emoji(text)) + " ";
+ }
+ else
+ {
+ return "" + this.atLink(this.emoji(text)) + " ";
+ }
+ };
+
+ return markedRenderer;
+ };
+
+ /**
+ *
+ * 生成TOC(Table of Contents)
+ * Creating ToC (Table of Contents)
+ *
+ * @param {Array} toc 从marked获取的TOC数组列表
+ * @param {Element} container 插入TOC的容器元素
+ * @param {Integer} startLevel Hx 起始层级
+ * @returns {Object} tocContainer 返回ToC列表容器层的jQuery对象元素
+ */
+
+ editormd.markdownToCRenderer = function(toc, container, tocDropdown, startLevel) {
+
+ var html = "";
+ var lastLevel = 0;
+ var classPrefix = this.classPrefix;
+
+ startLevel = startLevel || 1;
+
+ for (var i = 0, len = toc.length; i < len; i++)
+ {
+ var text = toc[i].text;
+ var level = toc[i].level;
+
+ if (level < startLevel) {
+ continue;
+ }
+
+ if (level > lastLevel)
+ {
+ html += "";
+ }
+ else if (level < lastLevel)
+ {
+ html += (new Array(lastLevel - level + 2)).join("");
+ }
+ else
+ {
+ html += "";
+ }
+
+ html += "" + text + " ";
+ lastLevel = level;
+ }
+
+ var tocContainer = container.find(".markdown-toc");
+
+ if ((tocContainer.length < 1 && container.attr("previewContainer") === "false"))
+ {
+ var tocHTML = "
";
+
+ tocHTML = (tocDropdown) ? "" + tocHTML + "
" : tocHTML;
+
+ container.html(tocHTML);
+
+ tocContainer = container.find(".markdown-toc");
+ }
+
+ if (tocDropdown)
+ {
+ tocContainer.wrap("
");
+ }
+
+ tocContainer.html("").children(".markdown-toc-list").html(html.replace(/\r?\n?\\<\/ul\>/g, ""));
+
+ return tocContainer;
+ };
+
+ /**
+ *
+ * 生成TOC下拉菜单
+ * Creating ToC dropdown menu
+ *
+ * @param {Object} container 插入TOC的容器jQuery对象元素
+ * @param {String} tocTitle ToC title
+ * @returns {Object} return toc-menu object
+ */
+
+ editormd.tocDropdownMenu = function(container, tocTitle) {
+
+ tocTitle = tocTitle || "Table of Contents";
+
+ var zindex = 400;
+ var tocMenus = container.find("." + this.classPrefix + "toc-menu");
+
+ tocMenus.each(function() {
+ var $this = $(this);
+ var toc = $this.children(".markdown-toc");
+ var icon = " ";
+ var btn = "";
+ var menu = toc.children("ul");
+ var list = menu.find("li");
+
+ toc.append(btn);
+
+ list.first().before("" + tocTitle + " " + icon + " ");
+
+ $this.mouseover(function(){
+ menu.show();
+
+ list.each(function(){
+ var li = $(this);
+ var ul = li.children("ul");
+
+ if (ul.html() === "")
+ {
+ ul.remove();
+ }
+
+ if (ul.length > 0 && ul.html() !== "")
+ {
+ var firstA = li.children("a").first();
+
+ if (firstA.children(".fa").length < 1)
+ {
+ firstA.append( $(icon).css({ float:"right", paddingTop:"4px" }) );
+ }
+ }
+
+ li.mouseover(function(){
+ ul.css("z-index", zindex).show();
+ zindex += 1;
+ }).mouseleave(function(){
+ ul.hide();
+ });
+ });
+ }).mouseleave(function(){
+ menu.hide();
+ });
+ });
+
+ return tocMenus;
+ };
+
+ /**
+ * 简单地过滤指定的HTML标签
+ * Filter custom html tags
+ *
+ * @param {String} html 要过滤HTML
+ * @param {String} filters 要过滤的标签
+ * @returns {String} html 返回过滤的HTML
+ */
+
+ editormd.filterHTMLTags = function(html, filters) {
+
+ if (typeof html !== "string") {
+ html = new String(html);
+ }
+
+ if (typeof filters !== "string") {
+ return html;
+ }
+
+ var expression = filters.split("|");
+ var filterTags = expression[0].split(",");
+ var attrs = expression[1];
+
+ for (var i = 0, len = filterTags.length; i < len; i++)
+ {
+ var tag = filterTags[i];
+
+ html = html.replace(new RegExp("\<\s*" + tag + "\s*([^\>]*)\>([^\>]*)\<\s*\/" + tag + "\s*\>", "igm"), "");
+ }
+
+ //return html;
+
+ if (typeof attrs !== "undefined")
+ {
+ var htmlTagRegex = /\<(\w+)\s*([^\>]*)\>([^\>]*)\<\/(\w+)\>/ig;
+
+ if (attrs === "*")
+ {
+ html = html.replace(htmlTagRegex, function($1, $2, $3, $4, $5) {
+ return "<" + $2 + ">" + $4 + "" + $5 + ">";
+ });
+ }
+ else if (attrs === "on*")
+ {
+ html = html.replace(htmlTagRegex, function($1, $2, $3, $4, $5) {
+ var el = $("<" + $2 + ">" + $4 + "" + $5 + ">");
+ var _attrs = $($1)[0].attributes;
+ var $attrs = {};
+
+ $.each(_attrs, function(i, e) {
+ if (e.nodeName !== '"') $attrs[e.nodeName] = e.nodeValue;
+ });
+
+ $.each($attrs, function(i) {
+ if (i.indexOf("on") === 0) {
+ delete $attrs[i];
+ }
+ });
+
+ el.attr($attrs);
+
+ var text = (typeof el[1] !== "undefined") ? $(el[1]).text() : "";
+
+ return el[0].outerHTML + text;
+ });
+ }
+ else
+ {
+ html = html.replace(htmlTagRegex, function($1, $2, $3, $4) {
+ var filterAttrs = attrs.split(",");
+ var el = $($1);
+ el.html($4);
+
+ $.each(filterAttrs, function(i) {
+ el.attr(filterAttrs[i], null);
+ });
+
+ return el[0].outerHTML;
+ });
+ }
+ }
+
+ return html;
+ };
+
+ /**
+ * 将Markdown文档解析为HTML用于前台显示
+ * Parse Markdown to HTML for Font-end preview.
+ *
+ * @param {String} id 用于显示HTML的对象ID
+ * @param {Object} [options={}] 配置选项,可选
+ * @returns {Object} div 返回jQuery对象元素
+ */
+
+ editormd.markdownToHTML = function(id, options) {
+ var defaults = {
+ gfm : true,
+ toc : true,
+ tocm : false,
+ tocStartLevel : 1,
+ tocTitle : "目录",
+ tocDropdown : false,
+ tocContainer : "",
+ markdown : "",
+ markdownSourceCode : false,
+ htmlDecode : false,
+ autoLoadKaTeX : true,
+ pageBreak : true,
+ atLink : true, // for @link
+ emailLink : true, // for mail address auto link
+ tex : false,
+ taskList : false, // Github Flavored Markdown task lists
+ emoji : false,
+ flowChart : false,
+ sequenceDiagram : false,
+ previewCodeHighlight : true
+ };
+
+ editormd.$marked = marked;
+
+ var div = $("#" + id);
+ var settings = div.settings = $.extend(true, defaults, options || {});
+ var saveTo = div.find("textarea");
+
+ if (saveTo.length < 1)
+ {
+ div.append("");
+ saveTo = div.find("textarea");
+ }
+
+ var markdownDoc = (settings.markdown === "") ? saveTo.val() : settings.markdown;
+ var markdownToC = [];
+
+ var rendererOptions = {
+ toc : settings.toc,
+ tocm : settings.tocm,
+ tocStartLevel : settings.tocStartLevel,
+ taskList : settings.taskList,
+ emoji : settings.emoji,
+ tex : settings.tex,
+ pageBreak : settings.pageBreak,
+ atLink : settings.atLink, // for @link
+ emailLink : settings.emailLink, // for mail address auto link
+ flowChart : settings.flowChart,
+ sequenceDiagram : settings.sequenceDiagram,
+ previewCodeHighlight : settings.previewCodeHighlight,
+ };
+
+ var markedOptions = {
+ renderer : editormd.markedRenderer(markdownToC, rendererOptions),
+ gfm : settings.gfm,
+ tables : true,
+ breaks : true,
+ pedantic : false,
+ sanitize : (settings.htmlDecode) ? false : true, // 是否忽略HTML标签,即是否开启HTML标签解析,为了安全性,默认不开启
+ smartLists : true,
+ smartypants : true
+ };
+
+ markdownDoc = new String(markdownDoc);
+
+ var markdownParsed = marked(markdownDoc, markedOptions);
+
+ markdownParsed = editormd.filterHTMLTags(markdownParsed, settings.htmlDecode);
+
+ if (settings.markdownSourceCode) {
+ saveTo.text(markdownDoc);
+ } else {
+ saveTo.remove();
+ }
+
+ div.addClass("markdown-body " + this.classPrefix + "html-preview").append(markdownParsed);
+
+ var tocContainer = (settings.tocContainer !== "") ? $(settings.tocContainer) : div;
+
+ if (settings.tocContainer !== "")
+ {
+ tocContainer.attr("previewContainer", false);
+ }
+
+ if (settings.toc)
+ {
+ div.tocContainer = this.markdownToCRenderer(markdownToC, tocContainer, settings.tocDropdown, settings.tocStartLevel);
+
+ if (settings.tocDropdown || div.find("." + this.classPrefix + "toc-menu").length > 0)
+ {
+ this.tocDropdownMenu(div, settings.tocTitle);
+ }
+
+ if (settings.tocContainer !== "")
+ {
+ div.find(".editormd-toc-menu, .editormd-markdown-toc").remove();
+ }
+ }
+
+ if (settings.previewCodeHighlight)
+ {
+ div.find("pre").addClass("prettyprint linenums");
+ prettyPrint();
+ }
+
+ if (!editormd.isIE8)
+ {
+ if (settings.flowChart) {
+ div.find(".flowchart").flowChart();
+ }
+
+ if (settings.sequenceDiagram) {
+ div.find(".sequence-diagram").sequenceDiagram({theme: "simple"});
+ }
+ }
+
+ if (settings.tex)
+ {
+ var katexHandle = function() {
+ div.find("." + editormd.classNames.tex).each(function(){
+ var tex = $(this);
+ katex.render(tex.html().replace(/</g, "<").replace(/>/g, ">"), tex[0]);
+ tex.find(".katex").css("font-size", "1.6em");
+ });
+ };
+
+ if (settings.autoLoadKaTeX && !editormd.$katex && !editormd.kaTeXLoaded)
+ {
+ this.loadKaTeX(function() {
+ editormd.$katex = katex;
+ editormd.kaTeXLoaded = true;
+ katexHandle();
+ });
+ }
+ else
+ {
+ katexHandle();
+ }
+ }
+
+ div.getMarkdown = function() {
+ return saveTo.val();
+ };
+
+ return div;
+ };
+
+ // Editor.md themes, change toolbar themes etc.
+ // added @1.5.0
+ editormd.themes = ["default", "dark"];
+
+ // Preview area themes
+ // added @1.5.0
+ editormd.previewThemes = ["default", "dark"];
+
+ // CodeMirror / editor area themes
+ // @1.5.0 rename -> editorThemes, old version -> themes
+ editormd.editorThemes = [
+ "default", "3024-day", "3024-night",
+ "ambiance", "ambiance-mobile",
+ "base16-dark", "base16-light", "blackboard",
+ "cobalt",
+ "eclipse", "elegant", "erlang-dark",
+ "lesser-dark",
+ "mbo", "mdn-like", "midnight", "monokai",
+ "neat", "neo", "night",
+ "paraiso-dark", "paraiso-light", "pastel-on-dark",
+ "rubyblue",
+ "solarized",
+ "the-matrix", "tomorrow-night-eighties", "twilight",
+ "vibrant-ink",
+ "xq-dark", "xq-light"
+ ];
+
+ editormd.loadPlugins = {};
+
+ editormd.loadFiles = {
+ js : [],
+ css : [],
+ plugin : []
+ };
+
+ /**
+ * 动态加载Editor.md插件,但不立即执行
+ * Load editor.md plugins
+ *
+ * @param {String} fileName 插件文件路径
+ * @param {Function} [callback=function()] 加载成功后执行的回调函数
+ * @param {String} [into="head"] 嵌入页面的位置
+ */
+
+ editormd.loadPlugin = function(fileName, callback, into) {
+ callback = callback || function() {};
+
+ this.loadScript(fileName, function() {
+ editormd.loadFiles.plugin.push(fileName);
+ callback();
+ }, into);
+ };
+
+ /**
+ * 动态加载CSS文件的方法
+ * Load css file method
+ *
+ * @param {String} fileName CSS文件名
+ * @param {Function} [callback=function()] 加载成功后执行的回调函数
+ * @param {String} [into="head"] 嵌入页面的位置
+ */
+
+ editormd.loadCSS = function(fileName, callback, into) {
+ into = into || "head";
+ callback = callback || function() {};
+
+ var css = document.createElement("link");
+ css.type = "text/css";
+ css.rel = "stylesheet";
+ css.onload = css.onreadystatechange = function() {
+ editormd.loadFiles.css.push(fileName);
+ callback();
+ };
+
+ css.href = fileName + ".css";
+
+ if(into === "head") {
+ document.getElementsByTagName("head")[0].appendChild(css);
+ } else {
+ document.body.appendChild(css);
+ }
+ };
+
+ editormd.isIE = (navigator.appName == "Microsoft Internet Explorer");
+ editormd.isIE8 = (editormd.isIE && navigator.appVersion.match(/8./i) == "8.");
+
+ /**
+ * 动态加载JS文件的方法
+ * Load javascript file method
+ *
+ * @param {String} fileName JS文件名
+ * @param {Function} [callback=function()] 加载成功后执行的回调函数
+ * @param {String} [into="head"] 嵌入页面的位置
+ */
+
+ editormd.loadScript = function(fileName, callback, into) {
+
+ into = into || "head";
+ callback = callback || function() {};
+
+ var script = null;
+ script = document.createElement("script");
+ script.id = fileName.replace(/[\./]+/g, "-");
+ script.type = "text/javascript";
+ script.src = fileName + ".js";
+
+ if (editormd.isIE8)
+ {
+ script.onreadystatechange = function() {
+ if(script.readyState)
+ {
+ if (script.readyState === "loaded" || script.readyState === "complete")
+ {
+ script.onreadystatechange = null;
+ editormd.loadFiles.js.push(fileName);
+ callback();
+ }
+ }
+ };
+ }
+ else
+ {
+ script.onload = function() {
+ editormd.loadFiles.js.push(fileName);
+ callback();
+ };
+ }
+
+ if (into === "head") {
+ document.getElementsByTagName("head")[0].appendChild(script);
+ } else {
+ document.body.appendChild(script);
+ }
+ };
+
+ // 使用国外的CDN,加载速度有时会很慢,或者自定义URL
+ // You can custom KaTeX load url.
+ editormd.katexURL = {
+ css : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min",
+ js : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min"
+ };
+
+ editormd.kaTeXLoaded = false;
+
+ /**
+ * 加载KaTeX文件
+ * load KaTeX files
+ *
+ * @param {Function} [callback=function()] 加载成功后执行的回调函数
+ */
+
+ editormd.loadKaTeX = function (callback) {
+ editormd.loadCSS(editormd.katexURL.css, function(){
+ editormd.loadScript(editormd.katexURL.js, callback || function(){});
+ });
+ };
+
+ /**
+ * 锁屏
+ * lock screen
+ *
+ * @param {Boolean} lock Boolean 布尔值,是否锁屏
+ * @returns {void}
+ */
+
+ editormd.lockScreen = function(lock) {
+ $("html,body").css("overflow", (lock) ? "hidden" : "");
+ };
+
+ /**
+ * 动态创建对话框
+ * Creating custom dialogs
+ *
+ * @param {Object} options 配置项键值对 Key/Value
+ * @returns {dialog} 返回创建的dialog的jQuery实例对象
+ */
+
+ editormd.createDialog = function(options) {
+ var defaults = {
+ name : "",
+ width : 420,
+ height: 240,
+ title : "",
+ drag : true,
+ closed : true,
+ content : "",
+ mask : true,
+ maskStyle : {
+ backgroundColor : "#fff",
+ opacity : 0.1
+ },
+ lockScreen : true,
+ footer : true,
+ buttons : false
+ };
+
+ options = $.extend(true, defaults, options);
+
+ var $this = this;
+ var editor = this.editor;
+ var classPrefix = editormd.classPrefix;
+ var guid = (new Date()).getTime();
+ var dialogName = ( (options.name === "") ? classPrefix + "dialog-" + guid : options.name);
+ var mouseOrTouch = editormd.mouseOrTouch;
+
+ var html = "";
+
+ if (options.title !== "")
+ {
+ html += "
";
+ html += "" + options.title + " ";
+ html += "
";
+ }
+
+ if (options.closed)
+ {
+ html += "
";
+ }
+
+ html += "
" + options.content;
+
+ if (options.footer || typeof options.footer === "string")
+ {
+ html += "
" + ( (typeof options.footer === "boolean") ? "" : options.footer) + "
";
+ }
+
+ html += "
";
+
+ html += "
";
+ html += "
";
+ html += "
";
+
+ editor.append(html);
+
+ var dialog = editor.find("." + dialogName);
+
+ dialog.lockScreen = function(lock) {
+ if (options.lockScreen)
+ {
+ $("html,body").css("overflow", (lock) ? "hidden" : "");
+ $this.resize();
+ }
+
+ return dialog;
+ };
+
+ dialog.showMask = function() {
+ if (options.mask)
+ {
+ editor.find("." + classPrefix + "mask").css(options.maskStyle).css("z-index", editormd.dialogZindex - 1).show();
+ }
+ return dialog;
+ };
+
+ dialog.hideMask = function() {
+ if (options.mask)
+ {
+ editor.find("." + classPrefix + "mask").hide();
+ }
+
+ return dialog;
+ };
+
+ dialog.loading = function(show) {
+ var loading = dialog.find("." + classPrefix + "dialog-mask");
+ loading[(show) ? "show" : "hide"]();
+
+ return dialog;
+ };
+
+ dialog.lockScreen(true).showMask();
+
+ dialog.show().css({
+ zIndex : editormd.dialogZindex,
+ border : (editormd.isIE8) ? "1px solid #ddd" : "",
+ width : (typeof options.width === "number") ? options.width + "px" : options.width,
+ height : (typeof options.height === "number") ? options.height + "px" : options.height
+ });
+
+ var dialogPosition = function(){
+ dialog.css({
+ top : ($(window).height() - dialog.height()) / 2 + "px",
+ left : ($(window).width() - dialog.width()) / 2 + "px"
+ });
+ };
+
+ dialogPosition();
+
+ $(window).resize(dialogPosition);
+
+ dialog.children("." + classPrefix + "dialog-close").bind(mouseOrTouch("click", "touchend"), function() {
+ dialog.hide().lockScreen(false).hideMask();
+ });
+
+ if (typeof options.buttons === "object")
+ {
+ var footer = dialog.footer = dialog.find("." + classPrefix + "dialog-footer");
+
+ for (var key in options.buttons)
+ {
+ var btn = options.buttons[key];
+ var btnClassName = classPrefix + key + "-btn";
+
+ footer.append("" + btn[0] + " ");
+ btn[1] = $.proxy(btn[1], dialog);
+ footer.children("." + btnClassName).bind(mouseOrTouch("click", "touchend"), btn[1]);
+ }
+ }
+
+ if (options.title !== "" && options.drag)
+ {
+ var posX, posY;
+ var dialogHeader = dialog.children("." + classPrefix + "dialog-header");
+
+ if (!options.mask) {
+ dialogHeader.bind(mouseOrTouch("click", "touchend"), function(){
+ editormd.dialogZindex += 2;
+ dialog.css("z-index", editormd.dialogZindex);
+ });
+ }
+
+ dialogHeader.mousedown(function(e) {
+ e = e || window.event; //IE
+ posX = e.clientX - parseInt(dialog[0].style.left);
+ posY = e.clientY - parseInt(dialog[0].style.top);
+
+ document.onmousemove = moveAction;
+ });
+
+ var userCanSelect = function (obj) {
+ obj.removeClass(classPrefix + "user-unselect").off("selectstart");
+ };
+
+ var userUnselect = function (obj) {
+ obj.addClass(classPrefix + "user-unselect").on("selectstart", function(event) { // selectstart for IE
+ return false;
+ });
+ };
+
+ var moveAction = function (e) {
+ e = e || window.event; //IE
+
+ var left, top, nowLeft = parseInt(dialog[0].style.left), nowTop = parseInt(dialog[0].style.top);
+
+ if( nowLeft >= 0 ) {
+ if( nowLeft + dialog.width() <= $(window).width()) {
+ left = e.clientX - posX;
+ } else {
+ left = $(window).width() - dialog.width();
+ document.onmousemove = null;
+ }
+ } else {
+ left = 0;
+ document.onmousemove = null;
+ }
+
+ if( nowTop >= 0 ) {
+ top = e.clientY - posY;
+ } else {
+ top = 0;
+ document.onmousemove = null;
+ }
+
+
+ document.onselectstart = function() {
+ return false;
+ };
+
+ userUnselect($("body"));
+ userUnselect(dialog);
+ dialog[0].style.left = left + "px";
+ dialog[0].style.top = top + "px";
+ };
+
+ document.onmouseup = function() {
+ userCanSelect($("body"));
+ userCanSelect(dialog);
+
+ document.onselectstart = null;
+ document.onmousemove = null;
+ };
+
+ dialogHeader.touchDraggable = function() {
+ var offset = null;
+ var start = function(e) {
+ var orig = e.originalEvent;
+ var pos = $(this).parent().position();
+
+ offset = {
+ x : orig.changedTouches[0].pageX - pos.left,
+ y : orig.changedTouches[0].pageY - pos.top
+ };
+ };
+
+ var move = function(e) {
+ e.preventDefault();
+ var orig = e.originalEvent;
+
+ $(this).parent().css({
+ top : orig.changedTouches[0].pageY - offset.y,
+ left : orig.changedTouches[0].pageX - offset.x
+ });
+ };
+
+ this.bind("touchstart", start).bind("touchmove", move);
+ };
+
+ dialogHeader.touchDraggable();
+ }
+
+ editormd.dialogZindex += 2;
+
+ return dialog;
+ };
+
+ /**
+ * 鼠标和触摸事件的判断/选择方法
+ * MouseEvent or TouchEvent type switch
+ *
+ * @param {String} [mouseEventType="click"] 供选择的鼠标事件
+ * @param {String} [touchEventType="touchend"] 供选择的触摸事件
+ * @returns {String} EventType 返回事件类型名称
+ */
+
+ editormd.mouseOrTouch = function(mouseEventType, touchEventType) {
+ mouseEventType = mouseEventType || "click";
+ touchEventType = touchEventType || "touchend";
+
+ var eventType = mouseEventType;
+
+ try {
+ document.createEvent("TouchEvent");
+ eventType = touchEventType;
+ } catch(e) {}
+
+ return eventType;
+ };
+
+ /**
+ * 日期时间的格式化方法
+ * Datetime format method
+ *
+ * @param {String} [format=""] 日期时间的格式,类似PHP的格式
+ * @returns {String} datefmt 返回格式化后的日期时间字符串
+ */
+
+ editormd.dateFormat = function(format) {
+ format = format || "";
+
+ var addZero = function(d) {
+ return (d < 10) ? "0" + d : d;
+ };
+
+ var date = new Date();
+ var year = date.getFullYear();
+ var year2 = year.toString().slice(2, 4);
+ var month = addZero(date.getMonth() + 1);
+ var day = addZero(date.getDate());
+ var weekDay = date.getDay();
+ var hour = addZero(date.getHours());
+ var min = addZero(date.getMinutes());
+ var second = addZero(date.getSeconds());
+ var ms = addZero(date.getMilliseconds());
+ var datefmt = "";
+
+ var ymd = year2 + "-" + month + "-" + day;
+ var fymd = year + "-" + month + "-" + day;
+ var hms = hour + ":" + min + ":" + second;
+
+ switch (format)
+ {
+ case "UNIX Time" :
+ datefmt = date.getTime();
+ break;
+
+ case "UTC" :
+ datefmt = date.toUTCString();
+ break;
+
+ case "yy" :
+ datefmt = year2;
+ break;
+
+ case "year" :
+ case "yyyy" :
+ datefmt = year;
+ break;
+
+ case "month" :
+ case "mm" :
+ datefmt = month;
+ break;
+
+ case "cn-week-day" :
+ case "cn-wd" :
+ var cnWeekDays = ["日", "一", "二", "三", "四", "五", "六"];
+ datefmt = "星期" + cnWeekDays[weekDay];
+ break;
+
+ case "week-day" :
+ case "wd" :
+ var weekDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
+ datefmt = weekDays[weekDay];
+ break;
+
+ case "day" :
+ case "dd" :
+ datefmt = day;
+ break;
+
+ case "hour" :
+ case "hh" :
+ datefmt = hour;
+ break;
+
+ case "min" :
+ case "ii" :
+ datefmt = min;
+ break;
+
+ case "second" :
+ case "ss" :
+ datefmt = second;
+ break;
+
+ case "ms" :
+ datefmt = ms;
+ break;
+
+ case "yy-mm-dd" :
+ datefmt = ymd;
+ break;
+
+ case "yyyy-mm-dd" :
+ datefmt = fymd;
+ break;
+
+ case "yyyy-mm-dd h:i:s ms" :
+ case "full + ms" :
+ datefmt = fymd + " " + hms + " " + ms;
+ break;
+
+ case "full" :
+ case "yyyy-mm-dd h:i:s" :
+ default:
+ datefmt = fymd + " " + hms;
+ break;
+ }
+
+ return datefmt;
+ };
+
+ return editormd;
+
+}));
diff --git a/vendor/assets/editormd/languages/en.js b/vendor/assets/editormd/languages/en.js
new file mode 100755
index 000000000..0120a46e1
--- /dev/null
+++ b/vendor/assets/editormd/languages/en.js
@@ -0,0 +1,127 @@
+(function(){
+ var factory = function (exports) {
+ var lang = {
+ name : "en",
+ description : "Open source online Markdown editor.",
+ tocTitle : "Table of Contents",
+ toolbar : {
+ undo : "Undo(Ctrl+Z)",
+ redo : "Redo(Ctrl+Y)",
+ bold : "Bold",
+ del : "Strikethrough",
+ italic : "Italic",
+ quote : "Block quote",
+ ucwords : "Words first letter convert to uppercase",
+ uppercase : "Selection text convert to uppercase",
+ lowercase : "Selection text convert to lowercase",
+ h1 : "Heading 1",
+ h2 : "Heading 2",
+ h3 : "Heading 3",
+ h4 : "Heading 4",
+ h5 : "Heading 5",
+ h6 : "Heading 6",
+ "list-ul" : "Unordered list",
+ "list-ol" : "Ordered list",
+ hr : "Horizontal rule",
+ link : "Link",
+ "reference-link" : "Reference link",
+ image : "Image",
+ code : "Code inline",
+ "preformatted-text" : "Preformatted text / Code block (Tab indent)",
+ "code-block" : "Code block (Multi-languages)",
+ table : "Tables",
+ datetime : "Datetime",
+ emoji : "Emoji",
+ "html-entities" : "HTML Entities",
+ pagebreak : "Page break",
+ watch : "Unwatch",
+ unwatch : "Watch",
+ preview : "HTML Preview (Press Shift + ESC exit)",
+ fullscreen : "Fullscreen (Press ESC exit)",
+ clear : "Clear",
+ search : "Search",
+ help : "Help",
+ info : "About " + exports.title
+ },
+ buttons : {
+ enter : "Enter",
+ cancel : "Cancel",
+ close : "Close"
+ },
+ dialog : {
+ link : {
+ title : "Link",
+ url : "Address",
+ urlTitle : "Title",
+ urlEmpty : "Error: Please fill in the link address."
+ },
+ referenceLink : {
+ title : "Reference link",
+ name : "Name",
+ url : "Address",
+ urlId : "ID",
+ urlTitle : "Title",
+ nameEmpty: "Error: Reference name can't be empty.",
+ idEmpty : "Error: Please fill in reference link id.",
+ urlEmpty : "Error: Please fill in reference link url address."
+ },
+ image : {
+ title : "Image",
+ url : "Address",
+ link : "Link",
+ alt : "Title",
+ uploadButton : "Upload",
+ imageURLEmpty : "Error: picture url address can't be empty.",
+ uploadFileEmpty : "Error: upload pictures cannot be empty!",
+ formatNotAllowed : "Error: only allows to upload pictures file, upload allowed image file format:"
+ },
+ preformattedText : {
+ title : "Preformatted text / Codes",
+ emptyAlert : "Error: Please fill in the Preformatted text or content of the codes."
+ },
+ codeBlock : {
+ title : "Code block",
+ selectLabel : "Languages: ",
+ selectDefaultText : "select a code language...",
+ otherLanguage : "Other languages",
+ unselectedLanguageAlert : "Error: Please select the code language.",
+ codeEmptyAlert : "Error: Please fill in the code content."
+ },
+ htmlEntities : {
+ title : "HTML Entities"
+ },
+ help : {
+ title : "Help"
+ }
+ }
+ };
+
+ exports.defaults.lang = lang;
+ };
+
+ // CommonJS/Node.js
+ if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
+ {
+ module.exports = factory;
+ }
+ else if (typeof define === "function") // AMD/CMD/Sea.js
+ {
+ if (define.amd) { // for Require.js
+
+ define(["editormd"], function(editormd) {
+ factory(editormd);
+ });
+
+ } else { // for Sea.js
+ define(function(require) {
+ var editormd = require("../editormd");
+ factory(editormd);
+ });
+ }
+ }
+ else
+ {
+ factory(window.editormd);
+ }
+
+})();
\ No newline at end of file
diff --git a/vendor/assets/editormd/languages/zh-tw.js b/vendor/assets/editormd/languages/zh-tw.js
new file mode 100755
index 000000000..c92d27111
--- /dev/null
+++ b/vendor/assets/editormd/languages/zh-tw.js
@@ -0,0 +1,127 @@
+(function(){
+ var factory = function (exports) {
+ var lang = {
+ name : "zh-tw",
+ description : "開源在線Markdown編輯器 Open source online Markdown editor.",
+ tocTitle : "目錄",
+ toolbar : {
+ undo : "撤銷(Ctrl+Z)",
+ redo : "重做(Ctrl+Y)",
+ bold : "粗體",
+ del : "刪除線",
+ italic : "斜體",
+ quote : "引用",
+ ucwords : "將所選的每個單詞首字母轉成大寫",
+ uppercase : "將所選文本轉成大寫",
+ lowercase : "將所選文本轉成小寫",
+ h1 : "標題1",
+ h2 : "標題2",
+ h3 : "標題3",
+ h4 : "標題4",
+ h5 : "標題5",
+ h6 : "標題6",
+ "list-ul" : "無序列表",
+ "list-ol" : "有序列表",
+ hr : "横线",
+ link : "链接",
+ "reference-link" : "引用鏈接",
+ image : "圖片",
+ code : "行內代碼",
+ "preformatted-text" : "預格式文本 / 代碼塊(縮進風格)",
+ "code-block" : "代碼塊(多語言風格)",
+ table : "添加表格",
+ datetime : "日期時間",
+ emoji : "Emoji 表情",
+ "html-entities" : "HTML 實體字符",
+ pagebreak : "插入分頁符",
+ watch : "關閉實時預覽",
+ unwatch : "開啟實時預覽",
+ preview : "全窗口預覽HTML(按 Shift + ESC 退出)",
+ fullscreen : "全屏(按 ESC 退出)",
+ clear : "清空",
+ search : "搜尋",
+ help : "使用幫助",
+ info : "關於" + exports.title
+ },
+ buttons : {
+ enter : "確定",
+ cancel : "取消",
+ close : "關閉"
+ },
+ dialog : {
+ link : {
+ title : "添加鏈接",
+ url : "鏈接地址",
+ urlTitle : "鏈接標題",
+ urlEmpty : "錯誤:請填寫鏈接地址。"
+ },
+ referenceLink : {
+ title : "添加引用鏈接",
+ name : "引用名稱",
+ url : "鏈接地址",
+ urlId : "鏈接ID",
+ urlTitle : "鏈接標題",
+ nameEmpty: "錯誤:引用鏈接的名稱不能為空。",
+ idEmpty : "錯誤:請填寫引用鏈接的ID。",
+ urlEmpty : "錯誤:請填寫引用鏈接的URL地址。"
+ },
+ image : {
+ title : "添加圖片",
+ url : "圖片地址",
+ link : "圖片鏈接",
+ alt : "圖片描述",
+ uploadButton : "本地上傳",
+ imageURLEmpty : "錯誤:圖片地址不能為空。",
+ uploadFileEmpty : "錯誤:上傳的圖片不能為空!",
+ formatNotAllowed : "錯誤:只允許上傳圖片文件,允許上傳的圖片文件格式有:"
+ },
+ preformattedText : {
+ title : "添加預格式文本或代碼塊",
+ emptyAlert : "錯誤:請填寫預格式文本或代碼的內容。"
+ },
+ codeBlock : {
+ title : "添加代碼塊",
+ selectLabel : "代碼語言:",
+ selectDefaultText : "請語言代碼語言",
+ otherLanguage : "其他語言",
+ unselectedLanguageAlert : "錯誤:請選擇代碼所屬的語言類型。",
+ codeEmptyAlert : "錯誤:請填寫代碼內容。"
+ },
+ htmlEntities : {
+ title : "HTML實體字符"
+ },
+ help : {
+ title : "使用幫助"
+ }
+ }
+ };
+
+ exports.defaults.lang = lang;
+ };
+
+ // CommonJS/Node.js
+ if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
+ {
+ module.exports = factory;
+ }
+ else if (typeof define === "function") // AMD/CMD/Sea.js
+ {
+ if (define.amd) { // for Require.js
+
+ define(["editormd"], function(editormd) {
+ factory(editormd);
+ });
+
+ } else { // for Sea.js
+ define(function(require) {
+ var editormd = require("../editormd");
+ factory(editormd);
+ });
+ }
+ }
+ else
+ {
+ factory(window.editormd);
+ }
+
+})();
\ No newline at end of file