浏览代码

分题阅轨迹还原相关功能扩展

1.引入jcrop插件,添加评卷图片拼接组件
2.修改评卷页面普通模式下图片拼接逻辑,支持按配置拼接
3.评卷任务增加pictureConfig字段,传递拼接配置,同时修改构造方法
4.新增PictureConfigItem模型,表示拼接配置单元,定义数据库保存方式,同时兼容现有保存方式
luoshi 6 年之前
父节点
当前提交
d5fd8d4696
共有 27 个文件被更改,包括 4438 次插入1173 次删除
  1. 241 241
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/MarkGroup.java
  2. 341 328
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/Task.java
  3. 433 432
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/TaskServiceImpl.java
  4. 85 85
      stmms-common/src/main/java/cn/com/qmth/stmms/common/utils/PictureUrlBuilder.java
  5. 131 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/PictureConfigItem.java
  6. 1 1
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ImageCheckThread.java
  7. 158 0
      stmms-web/src/main/webapp/WEB-INF/views/include/picConfig.jsp
  8. 1 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examIndex.jsp
  9. 二进制
      stmms-web/src/main/webapp/static/jcrop/css/Jcrop.gif
  10. 165 0
      stmms-web/src/main/webapp/static/jcrop/css/jquery.Jcrop.css
  11. 29 0
      stmms-web/src/main/webapp/static/jcrop/css/jquery.Jcrop.min.css
  12. 1694 0
      stmms-web/src/main/webapp/static/jcrop/js/jquery.Jcrop.js
  13. 22 0
      stmms-web/src/main/webapp/static/jcrop/js/jquery.Jcrop.min.js
  14. 661 0
      stmms-web/src/main/webapp/static/jcrop/js/jquery.color.js
  15. 103 86
      stmms-web/src/main/webapp/static/mark-new/js/modules/image-builder.js
  16. 323 0
      stmms-web/src/main/webapp/static/pic-config/css/style.css
  17. 二进制
      stmms-web/src/main/webapp/static/pic-config/img/00002_F.jpg
  18. 二进制
      stmms-web/src/main/webapp/static/pic-config/img/dt.jpg
  19. 二进制
      stmms-web/src/main/webapp/static/pic-config/img/preview.jpg
  20. 二进制
      stmms-web/src/main/webapp/static/pic-config/img/preview1.jpg
  21. 二进制
      stmms-web/src/main/webapp/static/pic-config/img/radio.png
  22. 二进制
      stmms-web/src/main/webapp/static/pic-config/img/up.png
  23. 二进制
      stmms-web/src/main/webapp/static/pic-config/img/zoom_fit.png
  24. 二进制
      stmms-web/src/main/webapp/static/pic-config/img/zoom_in.png
  25. 二进制
      stmms-web/src/main/webapp/static/pic-config/img/zoom_out.png
  26. 二进制
      stmms-web/src/main/webapp/static/pic-config/img/zoom_zero.png
  27. 50 0
      stmms-web/src/main/webapp/static/pic-config/index.html

+ 241 - 241
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/MarkGroup.java

@@ -1,241 +1,241 @@
-package cn.com.qmth.stmms.biz.exam.model;
-
-import java.io.Serializable;
-import java.text.DecimalFormat;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-
-import javax.persistence.Column;
-import javax.persistence.EmbeddedId;
-import javax.persistence.Entity;
-import javax.persistence.Table;
-import javax.persistence.Temporal;
-import javax.persistence.TemporalType;
-import javax.persistence.Transient;
-
-import org.apache.commons.lang.StringUtils;
-
-@Entity
-@Table(name = "eb_mark_group")
-public class MarkGroup implements Serializable {
-
-    private static final long serialVersionUID = 5228601490523811224L;
-
-    public static final String PIC_SPLIT = ",";
-
-    @EmbeddedId
-    private MarkGroupPK pk;
-
-    @Column(name = "title", length = 128, nullable = false)
-    private String title;
-
-    @Column(name = "pic_list", length = 32, nullable = false)
-    private String picList;
-
-    @Column(name = "total_score", nullable = false)
-    private Double totalScore;
-
-    @Temporal(TemporalType.TIMESTAMP)
-    @Column(name = "build_time", nullable = true)
-    private Date buildTime;
-
-    @Column(name = "library_count")
-    private Integer libraryCount;
-
-    @Column(name = "marked_count")
-    private Integer markedCount;
-
-    @Column(name = "left_count")
-    private Integer leftCount;
-
-    @Transient
-    private int currentCount;
-
-    @Transient
-    private String scoreList;
-
-    @Transient
-    private List<ExamQuestion> questionList;
-
-    @Transient
-    private long markerCount;
-    
-    @Transient
-    private int percent;
-
-    public MarkGroup() {
-        this.pk = new MarkGroupPK();
-    }
-
-    public MarkGroup(Integer examId, String subjectCode, Integer number, String title, List<Integer> picList,
-            Double totalScore) {
-        this.pk = new MarkGroupPK();
-        this.pk.setExamId(examId);
-        this.pk.setNumber(number);
-        this.pk.setSubjectCode(subjectCode);
-        this.title = title;
-        this.picList = picList != null && picList.size() > 0 ? StringUtils.join(picList, PIC_SPLIT) : null;
-        this.totalScore = totalScore;
-        this.libraryCount = 0;
-        this.markedCount = 0;
-        this.leftCount = 0;
-    }
-
-    public Integer getExamId() {
-        return pk.getExamId();
-    }
-
-    public void setExamId(Integer examId) {
-        pk.setExamId(examId);
-    }
-
-    public String getSubjectCode() {
-        return pk.getSubjectCode();
-    }
-
-    public void setSubjectCode(String subjectCode) {
-        pk.setSubjectCode(subjectCode);
-    }
-
-    public Integer getNumber() {
-        return pk.getNumber();
-    }
-
-    public void setNumber(Integer number) {
-        pk.setNumber(number);
-    }
-
-    public String getPicList() {
-        return picList;
-    }
-
-    public void setPicList(String picList) {
-        this.picList = picList;
-    }
-
-    public void setPicList(List<Integer> picList) {
-        this.picList = picList != null && picList.size() > 0 ? StringUtils.join(picList, PIC_SPLIT) : null;
-    }
-
-    public List<String> getPicNumbers() {
-        List<String> list = new LinkedList<String>();
-        String[] values = StringUtils.split(StringUtils.trimToNull(picList), PIC_SPLIT);
-        if (values != null && values.length > 0) {
-            for (String pic : values) {
-                list.add(StringUtils.trim(pic));
-            }
-        }
-        return list;
-    }
-
-    public String[] getPicListArray() {
-        List<String> list = getPicNumbers();
-        int length = list.size();
-        String[] array = new String[length];
-        for (int i = 0; i < list.size(); i++) {
-            array[i] = list.get(i);
-        }
-        return array;
-    }
-
-    public Double getTotalScore() {
-        return totalScore;
-    }
-
-    public void setTotalScore(Double totalScore) {
-        this.totalScore = totalScore;
-    }
-
-    public Integer getLibraryCount() {
-        return libraryCount;
-    }
-
-    public void setLibraryCount(Integer libraryCount) {
-        this.libraryCount = libraryCount;
-    }
-
-    public Integer getMarkedCount() {
-        return markedCount;
-    }
-
-    public void setMarkedCount(Integer markedCount) {
-        this.markedCount = markedCount;
-    }
-
-    public Integer getLeftCount() {
-        return leftCount;
-    }
-
-    public void setLeftCount(Integer leftCount) {
-        this.leftCount = leftCount;
-    }
-
-    public String getScoreList() {
-        return scoreList;
-    }
-
-    public void setScoreList(String scoreList) {
-        this.scoreList = scoreList;
-    }
-
-    public void setScoreList(List<ExamQuestion> questionList) {
-        DecimalFormat format = new DecimalFormat("###.#");
-        StringBuilder score = new StringBuilder();
-        for (ExamQuestion question : questionList) {
-            if (score.length() > 0) {
-                score.append(",");
-            }
-            score.append(format.format(question.getTotalScore()));
-        }
-        this.scoreList = score.toString();
-    }
-
-    public long getMarkerCount() {
-        return markerCount;
-    }
-
-    public void setMarkerCount(long markerCount) {
-        this.markerCount = markerCount;
-    }
-
-    public Date getBuildTime() {
-        return buildTime;
-    }
-
-    public void setBuildTime(Date buildTime) {
-        this.buildTime = buildTime;
-    }
-
-    public String getTitle() {
-        return title;
-    }
-
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-    public List<ExamQuestion> getQuestionList() {
-        return questionList;
-    }
-
-    public void setQuestionList(List<ExamQuestion> questionList) {
-        this.questionList = questionList;
-    }
-
-    public int getCurrentCount() {
-        return currentCount;
-    }
-
-    public void setCurrentCount(int currentCount) {
-        this.currentCount = currentCount;
-    }
-
-	public int getPercent() {
-		return percent;
-	}
-
-	public void setPercent(int percent) {
-		this.percent = percent;
-	}
-}
+package cn.com.qmth.stmms.biz.exam.model;
+
+import java.io.Serializable;
+import java.text.DecimalFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.persistence.Column;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import javax.persistence.Transient;
+
+import org.apache.commons.lang.StringUtils;
+
+@Entity
+@Table(name = "eb_mark_group")
+public class MarkGroup implements Serializable {
+
+    private static final long serialVersionUID = 5228601490523811224L;
+
+    public static final String PIC_SPLIT = ",";
+
+    @EmbeddedId
+    private MarkGroupPK pk;
+
+    @Column(name = "title", length = 128, nullable = false)
+    private String title;
+
+    @Column(name = "pic_list", length = 255, nullable = false)
+    private String picList;
+
+    @Column(name = "total_score", nullable = false)
+    private Double totalScore;
+
+    @Temporal(TemporalType.TIMESTAMP)
+    @Column(name = "build_time", nullable = true)
+    private Date buildTime;
+
+    @Column(name = "library_count")
+    private Integer libraryCount;
+
+    @Column(name = "marked_count")
+    private Integer markedCount;
+
+    @Column(name = "left_count")
+    private Integer leftCount;
+
+    @Transient
+    private int currentCount;
+
+    @Transient
+    private String scoreList;
+
+    @Transient
+    private List<ExamQuestion> questionList;
+
+    @Transient
+    private long markerCount;
+
+    @Transient
+    private int percent;
+
+    public MarkGroup() {
+        this.pk = new MarkGroupPK();
+    }
+
+    public MarkGroup(Integer examId, String subjectCode, Integer number, String title, List<Integer> picList,
+            Double totalScore) {
+        this.pk = new MarkGroupPK();
+        this.pk.setExamId(examId);
+        this.pk.setNumber(number);
+        this.pk.setSubjectCode(subjectCode);
+        this.title = title;
+        this.picList = picList != null && picList.size() > 0 ? StringUtils.join(picList, PIC_SPLIT) : null;
+        this.totalScore = totalScore;
+        this.libraryCount = 0;
+        this.markedCount = 0;
+        this.leftCount = 0;
+    }
+
+    public Integer getExamId() {
+        return pk.getExamId();
+    }
+
+    public void setExamId(Integer examId) {
+        pk.setExamId(examId);
+    }
+
+    public String getSubjectCode() {
+        return pk.getSubjectCode();
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        pk.setSubjectCode(subjectCode);
+    }
+
+    public Integer getNumber() {
+        return pk.getNumber();
+    }
+
+    public void setNumber(Integer number) {
+        pk.setNumber(number);
+    }
+
+    public String getPicList() {
+        return picList;
+    }
+
+    public void setPicList(String picList) {
+        this.picList = picList;
+    }
+
+    public void setPicList(List<Integer> picList) {
+        this.picList = picList != null && picList.size() > 0 ? StringUtils.join(picList, PIC_SPLIT) : null;
+    }
+
+    public List<String> getPicNumbers() {
+        List<String> list = new LinkedList<String>();
+        String[] values = StringUtils.split(StringUtils.trimToNull(picList), PIC_SPLIT);
+        if (values != null && values.length > 0) {
+            for (String pic : values) {
+                list.add(StringUtils.trim(pic));
+            }
+        }
+        return list;
+    }
+
+    public String[] getPicListArray() {
+        List<String> list = getPicNumbers();
+        int length = list.size();
+        String[] array = new String[length];
+        for (int i = 0; i < list.size(); i++) {
+            array[i] = list.get(i);
+        }
+        return array;
+    }
+
+    public Double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(Double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public Integer getLibraryCount() {
+        return libraryCount;
+    }
+
+    public void setLibraryCount(Integer libraryCount) {
+        this.libraryCount = libraryCount;
+    }
+
+    public Integer getMarkedCount() {
+        return markedCount;
+    }
+
+    public void setMarkedCount(Integer markedCount) {
+        this.markedCount = markedCount;
+    }
+
+    public Integer getLeftCount() {
+        return leftCount;
+    }
+
+    public void setLeftCount(Integer leftCount) {
+        this.leftCount = leftCount;
+    }
+
+    public String getScoreList() {
+        return scoreList;
+    }
+
+    public void setScoreList(String scoreList) {
+        this.scoreList = scoreList;
+    }
+
+    public void setScoreList(List<ExamQuestion> questionList) {
+        DecimalFormat format = new DecimalFormat("###.#");
+        StringBuilder score = new StringBuilder();
+        for (ExamQuestion question : questionList) {
+            if (score.length() > 0) {
+                score.append(",");
+            }
+            score.append(format.format(question.getTotalScore()));
+        }
+        this.scoreList = score.toString();
+    }
+
+    public long getMarkerCount() {
+        return markerCount;
+    }
+
+    public void setMarkerCount(long markerCount) {
+        this.markerCount = markerCount;
+    }
+
+    public Date getBuildTime() {
+        return buildTime;
+    }
+
+    public void setBuildTime(Date buildTime) {
+        this.buildTime = buildTime;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public List<ExamQuestion> getQuestionList() {
+        return questionList;
+    }
+
+    public void setQuestionList(List<ExamQuestion> questionList) {
+        this.questionList = questionList;
+    }
+
+    public int getCurrentCount() {
+        return currentCount;
+    }
+
+    public void setCurrentCount(int currentCount) {
+        this.currentCount = currentCount;
+    }
+
+    public int getPercent() {
+        return percent;
+    }
+
+    public void setPercent(int percent) {
+        this.percent = percent;
+    }
+}

+ 341 - 328
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/Task.java

@@ -1,328 +1,341 @@
-package cn.com.qmth.stmms.biz.mark.model;
-
-import java.io.Serializable;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-public class Task implements Serializable {
-
-    private static final long serialVersionUID = 4912665442008033200L;
-
-    /**
-     * 考生编号
-     */
-    private String studentId;
-
-    /**
-     * 库ID
-     */
-    private int libraryId;
-
-    /**
-     * 评卷员Id
-     */
-    private int markId;
-
-    /**
-     * 题卡图片地址
-     */
-    private List<String> pictureUrls;
-
-    /**
-     * 题卡原图地址
-     */
-    private List<String> sheetUrls;
-
-    /**
-     * 答案文档地址
-     */
-    private String answerUrl;
-
-    /**
-     * 试卷文档地址
-     */
-    private String paperUrl;
-
-    /**
-     * 客观题总分
-     */
-    private Double objectiveScore;
-
-    /**
-     * 给分步骤
-     */
-    private List<MarkStepDTO> markStepList;
-
-    /**
-     * 是否自评
-     */
-    private boolean isSelf;
-
-    /**
-     * 是否回评
-     */
-    private boolean isPrevious;
-
-    /**
-     * 是否打回
-     */
-    private boolean isBack;
-
-    /**
-     * 是否问题卷
-     */
-    private boolean isProblem;
-
-    /**
-     * 总分
-     */
-    private double totalScore;
-
-    /**
-     * 分值列表
-     */
-    private String scoreList;
-
-    /**
-     * 阅卷轨迹列表
-     */
-    private TrackDTO[] trackList;
-
-    /**
-     * 所花时间
-     */
-    private long spent;
-
-    /**
-     * 问题类型
-     */
-    private int reason;
-
-    /**
-     * 评卷时间
-     */
-    private Date markTime;
-
-    /**
-     * 任务是否存在
-     */
-    private boolean isExist;
-
-    /**
-     * 试卷标记信息
-     */
-    private String tags;
-
-    private String message;
-    
-    //特殊标识:√ 或 ❌ 或 乄
-    private SpecialTagDTO [] tagList;
-
-    public Task() {
-
-    }
-
-    public String getStudentId() {
-        return studentId;
-    }
-
-    public void setStudentId(String studentId) {
-        this.studentId = studentId;
-    }
-
-    public int getLibraryId() {
-        return libraryId;
-    }
-
-    public void setLibraryId(int libraryId) {
-        this.libraryId = libraryId;
-    }
-
-    public int getMarkId() {
-        return markId;
-    }
-
-    public void setMarkId(int markId) {
-        this.markId = markId;
-    }
-
-    public List<String> getPictureUrls() {
-        return pictureUrls;
-    }
-
-    public void setPictureUrls(List<String> pictureUrls) {
-        this.pictureUrls = pictureUrls;
-    }
-
-    public String getAnswerUrl() {
-        return answerUrl;
-    }
-
-    public void setAnswerUrl(String answerUrl) {
-        this.answerUrl = answerUrl;
-    }
-
-    public String getPaperUrl() {
-        return paperUrl;
-    }
-
-    public void setPaperUrl(String paperUrl) {
-        this.paperUrl = paperUrl;
-    }
-
-    public List<MarkStepDTO> getMarkStepList() {
-        return markStepList;
-    }
-
-    public void setMarkStepList(List<MarkStepDTO> markStepList) {
-        this.markStepList = markStepList;
-    }
-
-    public double getTotalScore() {
-        return totalScore;
-    }
-
-    public void setTotalScore(double totalScore) {
-        this.totalScore = totalScore;
-    }
-
-    public String getScoreList() {
-        return scoreList;
-    }
-
-    public void setScoreList(String scoreList) {
-        this.scoreList = scoreList;
-    }
-
-    public long getSpent() {
-        return spent;
-    }
-
-    public void setSpent(long spent) {
-        this.spent = spent;
-    }
-
-    public int getReason() {
-        return reason;
-    }
-
-    public void setReason(int reason) {
-        this.reason = reason;
-    }
-
-    public Date getMarkTime() {
-        return markTime;
-    }
-
-    public void setMarkTime(Date markTime) {
-        this.markTime = markTime;
-    }
-
-    public String getMessage() {
-        return message;
-    }
-
-    public void setMessage(String message) {
-        this.message = message;
-    }
-
-    public Double getObjectiveScore() {
-        return objectiveScore;
-    }
-
-    public void setObjectiveScore(Double objectiveScore) {
-        this.objectiveScore = objectiveScore;
-    }
-
-    public List<String> getSheetUrls() {
-        return sheetUrls;
-    }
-
-    public void setSheetUrls(List<String> sheetUrls) {
-        this.sheetUrls = sheetUrls;
-    }
-
-    public String getTags() {
-        return tags;
-    }
-
-    public void setTags(String tags) {
-        this.tags = tags;
-    }
-
-    public boolean isSelf() {
-        return isSelf;
-    }
-
-    public void setSelf(boolean isSelf) {
-        this.isSelf = isSelf;
-    }
-
-    public boolean isPrevious() {
-        return isPrevious;
-    }
-
-    public void setPrevious(boolean isPrevious) {
-        this.isPrevious = isPrevious;
-    }
-
-    public boolean isBack() {
-        return isBack;
-    }
-
-    public void setBack(boolean isBack) {
-        this.isBack = isBack;
-    }
-
-    public boolean isProblem() {
-        return isProblem;
-    }
-
-    public void setProblem(boolean isProblem) {
-        this.isProblem = isProblem;
-    }
-
-    public boolean isExist() {
-        return isExist;
-    }
-
-    public void setExist(boolean isExist) {
-        this.isExist = isExist;
-    }
-
-    public TrackDTO[] getTrackList() {
-        return trackList;
-    }
-
-    public void setTrackList(TrackDTO[] trackList) {
-        this.trackList = trackList;
-    }
-
-    public Map<String, List<TrackDTO>> getTrackMap() {
-        Map<String, List<TrackDTO>> map = new HashMap<String, List<TrackDTO>>();
-        if (trackList != null) {
-            for (TrackDTO dto : trackList) {
-                String questionNumber = dto.getQuestionNumber();
-                List<TrackDTO> list = map.get(questionNumber);
-                if (list == null) {
-                    list = new LinkedList<TrackDTO>();
-                    map.put(questionNumber, list);
-                }
-                list.add(dto);
-            }
-        }
-        return map;
-    }
-
-	public SpecialTagDTO[] getTagList() {
-		return tagList;
-	}
-
-	public void setTagList(SpecialTagDTO[] tagList) {
-		this.tagList = tagList;
-	}
-
-}
+package cn.com.qmth.stmms.biz.mark.model;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class Task implements Serializable {
+
+    private static final long serialVersionUID = 4912665442008033200L;
+
+    /**
+     * 考生编号
+     */
+    private String studentId;
+
+    /**
+     * 库ID
+     */
+    private int libraryId;
+
+    /**
+     * 评卷员Id
+     */
+    private int markId;
+
+    /**
+     * 题卡图片地址
+     */
+    private List<String> pictureUrls;
+
+    /**
+     * 题卡图片组装配置
+     */
+    private String pictureConfig;
+
+    /**
+     * 题卡原图地址
+     */
+    private List<String> sheetUrls;
+
+    /**
+     * 答案文档地址
+     */
+    private String answerUrl;
+
+    /**
+     * 试卷文档地址
+     */
+    private String paperUrl;
+
+    /**
+     * 客观题总分
+     */
+    private Double objectiveScore;
+
+    /**
+     * 给分步骤
+     */
+    private List<MarkStepDTO> markStepList;
+
+    /**
+     * 是否自评
+     */
+    private boolean isSelf;
+
+    /**
+     * 是否回评
+     */
+    private boolean isPrevious;
+
+    /**
+     * 是否打回
+     */
+    private boolean isBack;
+
+    /**
+     * 是否问题卷
+     */
+    private boolean isProblem;
+
+    /**
+     * 总分
+     */
+    private double totalScore;
+
+    /**
+     * 分值列表
+     */
+    private String scoreList;
+
+    /**
+     * 阅卷轨迹列表
+     */
+    private TrackDTO[] trackList;
+
+    /**
+     * 所花时间
+     */
+    private long spent;
+
+    /**
+     * 问题类型
+     */
+    private int reason;
+
+    /**
+     * 评卷时间
+     */
+    private Date markTime;
+
+    /**
+     * 任务是否存在
+     */
+    private boolean isExist;
+
+    /**
+     * 试卷标记信息
+     */
+    private String tags;
+
+    private String message;
+
+    // 特殊标识:√ 或 ❌ 或 乄
+    private SpecialTagDTO[] tagList;
+
+    public Task() {
+
+    }
+
+    public String getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(String studentId) {
+        this.studentId = studentId;
+    }
+
+    public int getLibraryId() {
+        return libraryId;
+    }
+
+    public void setLibraryId(int libraryId) {
+        this.libraryId = libraryId;
+    }
+
+    public int getMarkId() {
+        return markId;
+    }
+
+    public void setMarkId(int markId) {
+        this.markId = markId;
+    }
+
+    public List<String> getPictureUrls() {
+        return pictureUrls;
+    }
+
+    public void setPictureUrls(List<String> pictureUrls) {
+        this.pictureUrls = pictureUrls;
+    }
+
+    public String getAnswerUrl() {
+        return answerUrl;
+    }
+
+    public void setAnswerUrl(String answerUrl) {
+        this.answerUrl = answerUrl;
+    }
+
+    public String getPaperUrl() {
+        return paperUrl;
+    }
+
+    public void setPaperUrl(String paperUrl) {
+        this.paperUrl = paperUrl;
+    }
+
+    public List<MarkStepDTO> getMarkStepList() {
+        return markStepList;
+    }
+
+    public void setMarkStepList(List<MarkStepDTO> markStepList) {
+        this.markStepList = markStepList;
+    }
+
+    public double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public String getScoreList() {
+        return scoreList;
+    }
+
+    public void setScoreList(String scoreList) {
+        this.scoreList = scoreList;
+    }
+
+    public long getSpent() {
+        return spent;
+    }
+
+    public void setSpent(long spent) {
+        this.spent = spent;
+    }
+
+    public int getReason() {
+        return reason;
+    }
+
+    public void setReason(int reason) {
+        this.reason = reason;
+    }
+
+    public Date getMarkTime() {
+        return markTime;
+    }
+
+    public void setMarkTime(Date markTime) {
+        this.markTime = markTime;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public Double getObjectiveScore() {
+        return objectiveScore;
+    }
+
+    public void setObjectiveScore(Double objectiveScore) {
+        this.objectiveScore = objectiveScore;
+    }
+
+    public List<String> getSheetUrls() {
+        return sheetUrls;
+    }
+
+    public void setSheetUrls(List<String> sheetUrls) {
+        this.sheetUrls = sheetUrls;
+    }
+
+    public String getTags() {
+        return tags;
+    }
+
+    public void setTags(String tags) {
+        this.tags = tags;
+    }
+
+    public boolean isSelf() {
+        return isSelf;
+    }
+
+    public void setSelf(boolean isSelf) {
+        this.isSelf = isSelf;
+    }
+
+    public boolean isPrevious() {
+        return isPrevious;
+    }
+
+    public void setPrevious(boolean isPrevious) {
+        this.isPrevious = isPrevious;
+    }
+
+    public boolean isBack() {
+        return isBack;
+    }
+
+    public void setBack(boolean isBack) {
+        this.isBack = isBack;
+    }
+
+    public boolean isProblem() {
+        return isProblem;
+    }
+
+    public void setProblem(boolean isProblem) {
+        this.isProblem = isProblem;
+    }
+
+    public boolean isExist() {
+        return isExist;
+    }
+
+    public void setExist(boolean isExist) {
+        this.isExist = isExist;
+    }
+
+    public TrackDTO[] getTrackList() {
+        return trackList;
+    }
+
+    public void setTrackList(TrackDTO[] trackList) {
+        this.trackList = trackList;
+    }
+
+    public Map<String, List<TrackDTO>> getTrackMap() {
+        Map<String, List<TrackDTO>> map = new HashMap<String, List<TrackDTO>>();
+        if (trackList != null) {
+            for (TrackDTO dto : trackList) {
+                String questionNumber = dto.getQuestionNumber();
+                List<TrackDTO> list = map.get(questionNumber);
+                if (list == null) {
+                    list = new LinkedList<TrackDTO>();
+                    map.put(questionNumber, list);
+                }
+                list.add(dto);
+            }
+        }
+        return map;
+    }
+
+    public SpecialTagDTO[] getTagList() {
+        return tagList;
+    }
+
+    public void setTagList(SpecialTagDTO[] tagList) {
+        this.tagList = tagList;
+    }
+
+    public String getPictureConfig() {
+        return pictureConfig;
+    }
+
+    public void setPictureConfig(String pictureConfig) {
+        this.pictureConfig = pictureConfig;
+    }
+
+}

+ 433 - 432
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/TaskServiceImpl.java

@@ -1,433 +1,434 @@
-package cn.com.qmth.stmms.biz.mark.service.Impl;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import cn.com.qmth.stmms.biz.utils.MarkRedisUtil;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import cn.com.qmth.stmms.biz.campus.model.Campus;
-import cn.com.qmth.stmms.biz.campus.service.CampusService;
-import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
-import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
-import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
-import cn.com.qmth.stmms.biz.exam.model.Marker;
-import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
-import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
-import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
-import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
-import cn.com.qmth.stmms.biz.mark.model.MarkSpecialTag;
-import cn.com.qmth.stmms.biz.mark.model.MarkStepDTO;
-import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
-import cn.com.qmth.stmms.biz.mark.model.SpecialTagDTO;
-import cn.com.qmth.stmms.biz.mark.model.Task;
-import cn.com.qmth.stmms.biz.mark.model.TrackDTO;
-import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
-import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
-import cn.com.qmth.stmms.biz.mark.service.MarkSpecialTagService;
-import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
-import cn.com.qmth.stmms.biz.mark.service.TaskService;
-import cn.com.qmth.stmms.biz.utils.CurrentTaskUtil;
-import cn.com.qmth.stmms.biz.utils.ScoreItem;
-import cn.com.qmth.stmms.common.enums.LibraryStatus;
-import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
-
-/**
- * 评卷任务服务实现
- * 
- * @author LS
- * 
- */
-@Service("taskService")
-public class TaskServiceImpl implements TaskService {
-
-    private static Logger log = LoggerFactory.getLogger(TaskServiceImpl.class);
-
-    @Autowired
-    private MarkLibraryService libraryService;
-
-    @Autowired
-    private ExamStudentService studentService;
-
-    @Autowired
-    private ExamQuestionService questionService;
-
-    @Autowired
-    private MarkTrackService trackService;
-
-    @Autowired
-    private MarkGroupService groupService;
-    
-    @Autowired
-    private MarkSpecialTagService markSpecialTagService;
-    
-    @Autowired
-    private CampusService campusService;
-    
-    @Override
-    public List<Task> findByQuery(MarkLibrarySearchQuery query) {
-        List<Task> list = new LinkedList<Task>();
-        query = libraryService.findByQuery(query);
-        if (query.getCurrentCount() > 0) {
-            for (MarkLibrary library : query.getResult()) {
-                list.add(build(library));
-            }
-        }
-        return list;
-    }
-
-    @Override
-    public Task build(MarkLibrary library) {
-        ExamStudent student = studentService.findByExamIdAndExamNumber(library.getExamId(), library.getExamNumber());
-        MarkGroup group = groupService.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
-        Task task = new Task();
-        task.setTagList(getMarkSpecialTagList(library.getId()));
-        task.setExist(true);
-        task.setStudentId(library.getExamNumber());
-        task.setLibraryId(library.getId());
-        task.setMarkStepList(buildMarkStep(library));
-        task.setPictureUrls(PictureUrlBuilder.getSliceUrls(library.getExamId(), library.getCampusId(),
-                library.getSubjectCode(), library.getExamNumber(), group.getPicNumbers()));
-        task.setSheetUrls(PictureUrlBuilder.getSheetUrls(library.getExamId(), library.getCampusId(),
-                library.getSubjectCode(), library.getExamNumber(), student.getSheetCount()));
-        task.setAnswerUrl(PictureUrlBuilder.getAnswerUrl(library.getExamId(), library.getSubjectCode()));
-        task.setPaperUrl(PictureUrlBuilder.getPaperUrl(library.getExamId(), library.getSubjectCode()));
-        task.setObjectiveScore(student != null ? student.getObjectiveScore() : 0);
-        task.setMarkTime(library.getMarkerTime());
-        task.setTags(library.getTags());
-        if (library.getMarkerId() != null) {
-            task.setMarkId(library.getMarkerId());
-        }
-        if (library.getMarkerScore() != null) {
-            task.setTotalScore(library.getMarkerScore());
-        }
-        if (library.getMarkerScoreList() != null) {
-            task.setScoreList(library.getMarkerScoreList());
-        }
-        if (library.getStatus() == LibraryStatus.BACKED && library.getMarkerId() != null) {
-            task.setBack(true);
-        }
-        return task;
-    }
-
-    private List<MarkStepDTO> buildMarkStep(MarkLibrary library) {
-        List<MarkStepDTO> list = new LinkedList<MarkStepDTO>();
-        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndMainNumber(library.getExamId(),
-                library.getSubjectCode(), false, library.getGroupNumber());
-        int number = 0;
-        for (ExamQuestion question : sList) {
-            number++;
-
-            MarkStepDTO step = new MarkStepDTO();
-            step.setQuestionNumber(question.getQuestionNumber());
-            step.setBlockId(question.getMainNumber());
-            step.setNumber(number);
-            step.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
-            step.setTotalScore(question.getTotalScore());
-            step.setDefaultScore(0d);
-            step.setHasLevel(false);
-            step.setMax(question.getTotalScore());
-            step.setMin(0d);
-            step.setInterval(question.getIntervalScore());
-            step.setScoreList(question.getScoreListArray());
-            // 增加阅卷轨迹列表获取
-            addTrack(step, question, trackService.findByStudentId(library.getStudentId()));
-            list.add(step);
-        }
-        return list;
-    }
-
-    private void addTrack(MarkStepDTO step, ExamQuestion question, List<MarkTrack> trackList) {
-        String questionNumber = question.getQuestionNumber();
-        for (MarkTrack track : trackList) {
-            if (track.getQuestionNumber().equals(questionNumber)) {
-                step.addTrack(new TrackDTO(track));
-            }
-        }
-    }
-
-    @Override
-    @Transactional
-    public boolean submit(Task task) {
-        if (task == null) {
-            return false;
-        }
-        try {
-            submitLibrary(task);
-            return true;
-        } catch (Exception e) {
-            log.error("task submit faile", e);
-            return false;
-        }
-    }
-
-    private void submitLibrary(Task task) {
-        MarkLibrary library = updateLibrary(task);
-        if (library == null) {
-            return;
-        }
-        // 同步操作
-        if (task.isProblem()) {
-            // ProblemLibrary library = new ProblemLibrary();
-            // library.setExamId(formallyLibrary.getExamId());
-            // library.setCampusCode(formallyLibrary.getCampusCode());
-            // library.setSubjectCode(formallyLibrary.getSubjectCode());
-            // library.setLibraryId(formallyLibrary.getId());
-            // library.setType(LibraryType.FORMALLY);
-            // library.setBlockId(formallyLibrary.getBlockId());
-            // library.setSecretNo(formallyLibrary.getSecretNo());
-            // library.setReason(task.getReason());
-            // library.setSubmitTime(new Date());
-            // library.setSubmitter(task.getMarkId());
-            // library.setStage(formallyLibrary.getMarkerCount());
-            // library.setStatus(LibraryStatus.WAITING);
-            // problemLibraryService.save(library);
-        } else {
-            // FormallyHistory history = new FormallyHistory();
-            // history.setLibraryId(task.getLibraryId());
-            // history.setScoreList(task.getScoreList());
-            // history.setScorer(task.getMarkId());
-            // history.setScoreTime(new Date());
-            // history.setSpent(task.getSpent());
-            // history.setStage(task.getMarkerCount());
-            // history.setTotalScore(task.getTotalScore());
-            // if (task.isSelf()) {
-            // history.setType(HistoryType.SELF);
-            // } else if (task.isBack()) {
-            // history.setType(HistoryType.BACK);
-            // } else {
-            // history.setType(HistoryType.COMMON);
-            // BlockTaskCountUtil.addBlockMarkedTask(stringRedisTemplate,
-            // task.getBlockId(), task.getLibraryId());
-            // }
-            // formallyHistoryService.save(history);
-
-            onTaskSubmit(library, task);
-        }
-    }
-
-    private void onTaskSubmit(MarkLibrary library, Task task) {
-        if (library.getStatus() == LibraryStatus.MARKED) {
-            // ExamStudent student =
-            // studentService.findById(library.getStudentId());
-            // if (student != null) {
-            // student.setSubjectiveScore(library.getMarkerScore());
-            // student.setScoreList(library.getScoreList(), false);
-            // studentService.save(student);
-            // }
-            List<MarkGroup> groups = groupService.findByExamAndSubject(library.getExamId(), library.getSubjectCode());
-            List<MarkLibrary> libraries = libraryService.findByStudentIdAndStatus(library.getStudentId(),
-                    LibraryStatus.MARKED);
-            if (libraries.size() == groups.size()) {
-                List<ScoreItem> scoreList = new ArrayList<ScoreItem>();
-                Double markerScore = 0.0;
-                for (MarkLibrary librarie : libraries) {
-                    scoreList.addAll(librarie.getScoreList());
-                    markerScore = markerScore + librarie.getMarkerScore();
-                }
-                studentService.updateSubjectiveScore(library.getStudentId(), markerScore,
-                        ExamStudent.buildScoreList(scoreList));
-            }
-
-            groupService.updateLibraryCount(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
-
-            Map<String, List<TrackDTO>> trackMap = task.getTrackMap();
-            for (String questionNumber : trackMap.keySet()) {
-                trackService.deleteByStudentIdAndQuestionNumber(library.getStudentId(), questionNumber);
-                List<TrackDTO> list = trackMap.get(questionNumber);
-                for (TrackDTO dto : list) {
-                    trackService.save(dto.transform(library));
-                }
-            }
-
-            SpecialTagDTO [] tagList = task.getTagList();
-            markSpecialTagService.deleteByLibraryId(library.getId());
-            if(tagList != null && tagList.length>0){
-                for(SpecialTagDTO s : tagList){
-                        MarkSpecialTag markSpecialTag = new MarkSpecialTag();
-                        markSpecialTag.setLibraryId(library.getId());
-                        markSpecialTag.setTagName(s.getTagName());
-                        markSpecialTag.setPositionX(s.getPositionX());
-                        markSpecialTag.setPositionY(s.getPositionY());
-                        markSpecialTagService.save(markSpecialTag);
-                }
-            }
-        }
-    }
-
-    /**
-     * 保存评卷任务
-     * 
-     * @param task
-     * @return
-     */
-    private MarkLibrary updateLibrary(Task task) {
-        MarkLibrary library = libraryService.findById(task.getLibraryId());
-        if (library != null) {
-            library.setMarkerId(task.getMarkId());
-            library.setMarkerTime(new Date());
-            library.setTags(StringUtils.trimToNull(task.getTags()));
-            library.setStatus(LibraryStatus.MARKED);
-            if (!task.isProblem()) {
-                library.setMarkerScore(task.getTotalScore());
-                library.setMarkerScoreList(task.getScoreList());
-            }
-            return libraryService.save(library);
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public boolean setCurrent(Marker marker, int libraryId) {
-        return CurrentTaskUtil.add(marker, libraryId);
-    }
-
-    @Override
-    public void removeCurrent(Marker marker, int libraryId) {
-        CurrentTaskUtil.remove(marker, libraryId);
-    }
-
-    @Override
-    public int countCurrent(int examId, String subjectCode, int number) {
-        return CurrentTaskUtil.count(examId, subjectCode, number);
-    }
-
-    @Override
-    public int countCurrent(Marker marker) {
-        return CurrentTaskUtil.count(marker);
-    }
-
-    @Override
-    public void clearCurrent(int examId, String subjectCode, int number) {
-        CurrentTaskUtil.clear(examId, subjectCode, number);
-    }
-
-    @Override
-    public void clearCurrent(Marker marker) {
-        CurrentTaskUtil.clear(marker);
+package cn.com.qmth.stmms.biz.mark.service.Impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import cn.com.qmth.stmms.biz.campus.model.Campus;
+import cn.com.qmth.stmms.biz.campus.service.CampusService;
+import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.model.Marker;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
+import cn.com.qmth.stmms.biz.mark.model.MarkSpecialTag;
+import cn.com.qmth.stmms.biz.mark.model.MarkStepDTO;
+import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
+import cn.com.qmth.stmms.biz.mark.model.SpecialTagDTO;
+import cn.com.qmth.stmms.biz.mark.model.Task;
+import cn.com.qmth.stmms.biz.mark.model.TrackDTO;
+import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
+import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
+import cn.com.qmth.stmms.biz.mark.service.MarkSpecialTagService;
+import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
+import cn.com.qmth.stmms.biz.mark.service.TaskService;
+import cn.com.qmth.stmms.biz.utils.CurrentTaskUtil;
+import cn.com.qmth.stmms.biz.utils.ScoreItem;
+import cn.com.qmth.stmms.common.enums.LibraryStatus;
+import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
+
+/**
+ * 评卷任务服务实现
+ * 
+ * @author LS
+ * 
+ */
+@Service("taskService")
+public class TaskServiceImpl implements TaskService {
+
+    private static Logger log = LoggerFactory.getLogger(TaskServiceImpl.class);
+
+    @Autowired
+    private MarkLibraryService libraryService;
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Autowired
+    private ExamQuestionService questionService;
+
+    @Autowired
+    private MarkTrackService trackService;
+
+    @Autowired
+    private MarkGroupService groupService;
+
+    @Autowired
+    private MarkSpecialTagService markSpecialTagService;
+
+    @Autowired
+    private CampusService campusService;
+
+    @Override
+    public List<Task> findByQuery(MarkLibrarySearchQuery query) {
+        List<Task> list = new LinkedList<Task>();
+        query = libraryService.findByQuery(query);
+        if (query.getCurrentCount() > 0) {
+            for (MarkLibrary library : query.getResult()) {
+                list.add(build(library));
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public Task build(MarkLibrary library) {
+        ExamStudent student = studentService.findByExamIdAndExamNumber(library.getExamId(), library.getExamNumber());
+        MarkGroup group = groupService.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
+        Task task = new Task();
+        task.setTagList(getMarkSpecialTagList(library.getId()));
+        task.setExist(true);
+        task.setStudentId(library.getExamNumber());
+        task.setLibraryId(library.getId());
+        task.setMarkStepList(buildMarkStep(library));
+        task.setPictureUrls(PictureUrlBuilder.getSliceUrls(library.getExamId(), library.getCampusId(),
+                library.getSubjectCode(), library.getExamNumber(), student.getSliceCount()));
+        task.setPictureConfig(group.getPicList());
+        task.setSheetUrls(PictureUrlBuilder.getSheetUrls(library.getExamId(), library.getCampusId(),
+                library.getSubjectCode(), library.getExamNumber(), student.getSheetCount()));
+        task.setAnswerUrl(PictureUrlBuilder.getAnswerUrl(library.getExamId(), library.getSubjectCode()));
+        task.setPaperUrl(PictureUrlBuilder.getPaperUrl(library.getExamId(), library.getSubjectCode()));
+        task.setObjectiveScore(student != null ? student.getObjectiveScore() : 0);
+        task.setMarkTime(library.getMarkerTime());
+        task.setTags(library.getTags());
+        if (library.getMarkerId() != null) {
+            task.setMarkId(library.getMarkerId());
+        }
+        if (library.getMarkerScore() != null) {
+            task.setTotalScore(library.getMarkerScore());
+        }
+        if (library.getMarkerScoreList() != null) {
+            task.setScoreList(library.getMarkerScoreList());
+        }
+        if (library.getStatus() == LibraryStatus.BACKED && library.getMarkerId() != null) {
+            task.setBack(true);
+        }
+        return task;
+    }
+
+    private List<MarkStepDTO> buildMarkStep(MarkLibrary library) {
+        List<MarkStepDTO> list = new LinkedList<MarkStepDTO>();
+        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndMainNumber(library.getExamId(),
+                library.getSubjectCode(), false, library.getGroupNumber());
+        int number = 0;
+        for (ExamQuestion question : sList) {
+            number++;
+
+            MarkStepDTO step = new MarkStepDTO();
+            step.setQuestionNumber(question.getQuestionNumber());
+            step.setBlockId(question.getMainNumber());
+            step.setNumber(number);
+            step.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
+            step.setTotalScore(question.getTotalScore());
+            step.setDefaultScore(0d);
+            step.setHasLevel(false);
+            step.setMax(question.getTotalScore());
+            step.setMin(0d);
+            step.setInterval(question.getIntervalScore());
+            step.setScoreList(question.getScoreListArray());
+            // 增加阅卷轨迹列表获取
+            addTrack(step, question, trackService.findByStudentId(library.getStudentId()));
+            list.add(step);
+        }
+        return list;
+    }
+
+    private void addTrack(MarkStepDTO step, ExamQuestion question, List<MarkTrack> trackList) {
+        String questionNumber = question.getQuestionNumber();
+        for (MarkTrack track : trackList) {
+            if (track.getQuestionNumber().equals(questionNumber)) {
+                step.addTrack(new TrackDTO(track));
+            }
+        }
+    }
+
+    @Override
+    @Transactional
+    public boolean submit(Task task) {
+        if (task == null) {
+            return false;
+        }
+        try {
+            submitLibrary(task);
+            return true;
+        } catch (Exception e) {
+            log.error("task submit faile", e);
+            return false;
+        }
+    }
+
+    private void submitLibrary(Task task) {
+        MarkLibrary library = updateLibrary(task);
+        if (library == null) {
+            return;
+        }
+        // 同步操作
+        if (task.isProblem()) {
+            // ProblemLibrary library = new ProblemLibrary();
+            // library.setExamId(formallyLibrary.getExamId());
+            // library.setCampusCode(formallyLibrary.getCampusCode());
+            // library.setSubjectCode(formallyLibrary.getSubjectCode());
+            // library.setLibraryId(formallyLibrary.getId());
+            // library.setType(LibraryType.FORMALLY);
+            // library.setBlockId(formallyLibrary.getBlockId());
+            // library.setSecretNo(formallyLibrary.getSecretNo());
+            // library.setReason(task.getReason());
+            // library.setSubmitTime(new Date());
+            // library.setSubmitter(task.getMarkId());
+            // library.setStage(formallyLibrary.getMarkerCount());
+            // library.setStatus(LibraryStatus.WAITING);
+            // problemLibraryService.save(library);
+        } else {
+            // FormallyHistory history = new FormallyHistory();
+            // history.setLibraryId(task.getLibraryId());
+            // history.setScoreList(task.getScoreList());
+            // history.setScorer(task.getMarkId());
+            // history.setScoreTime(new Date());
+            // history.setSpent(task.getSpent());
+            // history.setStage(task.getMarkerCount());
+            // history.setTotalScore(task.getTotalScore());
+            // if (task.isSelf()) {
+            // history.setType(HistoryType.SELF);
+            // } else if (task.isBack()) {
+            // history.setType(HistoryType.BACK);
+            // } else {
+            // history.setType(HistoryType.COMMON);
+            // BlockTaskCountUtil.addBlockMarkedTask(stringRedisTemplate,
+            // task.getBlockId(), task.getLibraryId());
+            // }
+            // formallyHistoryService.save(history);
+
+            onTaskSubmit(library, task);
+        }
+    }
+
+    private void onTaskSubmit(MarkLibrary library, Task task) {
+        if (library.getStatus() == LibraryStatus.MARKED) {
+            // ExamStudent student =
+            // studentService.findById(library.getStudentId());
+            // if (student != null) {
+            // student.setSubjectiveScore(library.getMarkerScore());
+            // student.setScoreList(library.getScoreList(), false);
+            // studentService.save(student);
+            // }
+            List<MarkGroup> groups = groupService.findByExamAndSubject(library.getExamId(), library.getSubjectCode());
+            List<MarkLibrary> libraries = libraryService.findByStudentIdAndStatus(library.getStudentId(),
+                    LibraryStatus.MARKED);
+            if (libraries.size() == groups.size()) {
+                List<ScoreItem> scoreList = new ArrayList<ScoreItem>();
+                Double markerScore = 0.0;
+                for (MarkLibrary librarie : libraries) {
+                    scoreList.addAll(librarie.getScoreList());
+                    markerScore = markerScore + librarie.getMarkerScore();
+                }
+                studentService.updateSubjectiveScore(library.getStudentId(), markerScore,
+                        ExamStudent.buildScoreList(scoreList));
+            }
+
+            groupService.updateLibraryCount(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
+
+            Map<String, List<TrackDTO>> trackMap = task.getTrackMap();
+            for (String questionNumber : trackMap.keySet()) {
+                trackService.deleteByStudentIdAndQuestionNumber(library.getStudentId(), questionNumber);
+                List<TrackDTO> list = trackMap.get(questionNumber);
+                for (TrackDTO dto : list) {
+                    trackService.save(dto.transform(library));
+                }
+            }
+
+            SpecialTagDTO[] tagList = task.getTagList();
+            markSpecialTagService.deleteByLibraryId(library.getId());
+            if (tagList != null && tagList.length > 0) {
+                for (SpecialTagDTO s : tagList) {
+                    MarkSpecialTag markSpecialTag = new MarkSpecialTag();
+                    markSpecialTag.setLibraryId(library.getId());
+                    markSpecialTag.setTagName(s.getTagName());
+                    markSpecialTag.setPositionX(s.getPositionX());
+                    markSpecialTag.setPositionY(s.getPositionY());
+                    markSpecialTagService.save(markSpecialTag);
+                }
+            }
+        }
+    }
+
+    /**
+     * 保存评卷任务
+     * 
+     * @param task
+     * @return
+     */
+    private MarkLibrary updateLibrary(Task task) {
+        MarkLibrary library = libraryService.findById(task.getLibraryId());
+        if (library != null) {
+            library.setMarkerId(task.getMarkId());
+            library.setMarkerTime(new Date());
+            library.setTags(StringUtils.trimToNull(task.getTags()));
+            library.setStatus(LibraryStatus.MARKED);
+            if (!task.isProblem()) {
+                library.setMarkerScore(task.getTotalScore());
+                library.setMarkerScoreList(task.getScoreList());
+            }
+            return libraryService.save(library);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public boolean setCurrent(Marker marker, int libraryId) {
+        return CurrentTaskUtil.add(marker, libraryId);
+    }
+
+    @Override
+    public void removeCurrent(Marker marker, int libraryId) {
+        CurrentTaskUtil.remove(marker, libraryId);
+    }
+
+    @Override
+    public int countCurrent(int examId, String subjectCode, int number) {
+        return CurrentTaskUtil.count(examId, subjectCode, number);
+    }
+
+    @Override
+    public int countCurrent(Marker marker) {
+        return CurrentTaskUtil.count(marker);
+    }
+
+    @Override
+    public void clearCurrent(int examId, String subjectCode, int number) {
+        CurrentTaskUtil.clear(examId, subjectCode, number);
     }
-    
-    public SpecialTagDTO [] getMarkSpecialTagList(Integer libraryId){
-        SpecialTagDTO [] specialTags = null;
-        List<MarkSpecialTag> list =  markSpecialTagService.findByLibraryId(libraryId);
-        if(!list.isEmpty()){
-            specialTags = new SpecialTagDTO[list.size()] ;
-            for(int i =0;i<list.size();i++){
-                SpecialTagDTO specialTagDTO = new SpecialTagDTO();
-                specialTagDTO.setTagName(list.get(i).getTagName());
-                specialTagDTO.setPositionX(list.get(i).getPositionX());
-                specialTagDTO.setPositionY(list.get(i).getPositionY());
-                specialTags[i] = specialTagDTO;
-            }
-        }
-        return  specialTags;
-    }
-
-	@Override
-	public Task build(Integer studentId) {
-        ExamStudent student = studentService.findById(studentId);
-        Task task = new Task();
-        task.setExist(true);
-        task.setStudentId(student.getExamNumber());
-        task.setLibraryId(studentId);
-        task.setMarkStepList(buildMarkStep(student));
-        Campus campus = campusService.findBySchoolAndName(student.getSchoolId(), student.getCampusName());
-        task.setPictureUrls(PictureUrlBuilder.getSliceUrls(student.getExamId(), campus.getId(),
-        		student.getSubjectCode(), student.getExamNumber(), 1, student.getSliceCount()-1));
-        task.setSheetUrls(PictureUrlBuilder.getSheetUrls(student.getExamId(), campus.getId(),
-        		student.getSubjectCode(), student.getExamNumber(), student.getSheetCount()));
-        task.setAnswerUrl(PictureUrlBuilder.getAnswerUrl(student.getExamId(), student.getSubjectCode()));
-        task.setPaperUrl(PictureUrlBuilder.getPaperUrl(student.getExamId(), student.getSubjectCode()));
-        task.setObjectiveScore(student != null ? student.getObjectiveScore() : 0);
-        task.setBack(true);
-        return task;
-    }
-	private List<MarkStepDTO> buildMarkStep(ExamStudent student) {
-        List<MarkStepDTO> list = new LinkedList<MarkStepDTO>();
-        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjective(student.getExamId(),
-        		student.getSubjectCode(), false);
-        int number = 0;
-        for (ExamQuestion question : sList) {
-            number++;
-
-            MarkStepDTO step = new MarkStepDTO();
-            step.setQuestionNumber(question.getQuestionNumber());
-            step.setBlockId(question.getMainNumber());
-            step.setNumber(number);
-            step.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
-            step.setTotalScore(question.getTotalScore());
-            step.setDefaultScore(0d);
-            step.setHasLevel(false);
-            step.setMax(question.getTotalScore());
-            step.setMin(0d);
-            step.setInterval(question.getIntervalScore());
-            step.setScoreList(question.getScoreListArray());
-            // 增加阅卷轨迹列表获取
-            addTrack(step, question, trackService.findByStudentId(student.getId()));
-            list.add(step);
-        }
-        return list;
-    }
-	
-	@Transactional
-	@Override
-	public boolean submitByStudent(Task task) {
-		if (task == null) {
-            return false;
-        }
-        try {
-        	List<MarkLibrary> libraries = libraryService.findByStudentId(task.getLibraryId());
-        	int start = 0 ;
-        	int end = 0;
-        	for (int i = 0; i < libraries.size(); i++) {
-        		MarkLibrary library = libraries.get(i);
-        		Task stepTask =new Task();
-        		stepTask.setProblem(task.isProblem());
-        		stepTask.setBack(task.isBack());
-        		stepTask.setLibraryId(library.getId());
-        		stepTask.setMarkId(task.getMarkId());
-        		stepTask.setTags(task.getTags());
-        		List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndMainNumber(library.getExamId(),
-                        library.getSubjectCode(), false, library.getGroupNumber());
-        		if(start==end ){
-        			end = end + sList.size()*2-1;
-        		}else{
-        			start = end+1;
-        			end = start + sList.size()*2-1;
-        		}
-        		String scoreList = task.getScoreList().substring(start, end);
-        		stepTask.setScoreList(scoreList);
-        		String[] scores = scoreList.split(",");
-        		double totalScore = 0;
-        		for (String score : scores) {
-        			totalScore = totalScore + Double.parseDouble(score);
-				}
-         		stepTask.setTotalScore(totalScore);
-        		submitLibrary(stepTask);
-			}
-            return true;
-        } catch (Exception e) {
-            log.error("task submit faile", e);
-            return false;
-        }
-	}
-
-    /*
-     * 客户端非正常情况下退出系统时,对内存中的没有及时清除的任务进行处理
-     *
-     */
-    @Override
-    public void clearTaskMap(long cleanMapinterval) throws Exception{
-            CurrentTaskUtil.QuartzClearMap(cleanMapinterval);
-    }
-
-}
+
+    @Override
+    public void clearCurrent(Marker marker) {
+        CurrentTaskUtil.clear(marker);
+    }
+
+    public SpecialTagDTO[] getMarkSpecialTagList(Integer libraryId) {
+        SpecialTagDTO[] specialTags = null;
+        List<MarkSpecialTag> list = markSpecialTagService.findByLibraryId(libraryId);
+        if (!list.isEmpty()) {
+            specialTags = new SpecialTagDTO[list.size()];
+            for (int i = 0; i < list.size(); i++) {
+                SpecialTagDTO specialTagDTO = new SpecialTagDTO();
+                specialTagDTO.setTagName(list.get(i).getTagName());
+                specialTagDTO.setPositionX(list.get(i).getPositionX());
+                specialTagDTO.setPositionY(list.get(i).getPositionY());
+                specialTags[i] = specialTagDTO;
+            }
+        }
+        return specialTags;
+    }
+
+    @Override
+    public Task build(Integer studentId) {
+        ExamStudent student = studentService.findById(studentId);
+        Task task = new Task();
+        task.setExist(true);
+        task.setStudentId(student.getExamNumber());
+        task.setLibraryId(studentId);
+        task.setMarkStepList(buildMarkStep(student));
+        Campus campus = campusService.findBySchoolAndName(student.getSchoolId(), student.getCampusName());
+        task.setPictureUrls(PictureUrlBuilder.getSliceUrls(student.getExamId(), campus.getId(),
+                student.getSubjectCode(), student.getExamNumber(), student.getSliceCount()));
+        task.setSheetUrls(PictureUrlBuilder.getSheetUrls(student.getExamId(), campus.getId(), student.getSubjectCode(),
+                student.getExamNumber(), student.getSheetCount()));
+        task.setAnswerUrl(PictureUrlBuilder.getAnswerUrl(student.getExamId(), student.getSubjectCode()));
+        task.setPaperUrl(PictureUrlBuilder.getPaperUrl(student.getExamId(), student.getSubjectCode()));
+        task.setObjectiveScore(student != null ? student.getObjectiveScore() : 0);
+        task.setBack(true);
+        return task;
+    }
+
+    private List<MarkStepDTO> buildMarkStep(ExamStudent student) {
+        List<MarkStepDTO> list = new LinkedList<MarkStepDTO>();
+        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjective(student.getExamId(),
+                student.getSubjectCode(), false);
+        int number = 0;
+        for (ExamQuestion question : sList) {
+            number++;
+
+            MarkStepDTO step = new MarkStepDTO();
+            step.setQuestionNumber(question.getQuestionNumber());
+            step.setBlockId(question.getMainNumber());
+            step.setNumber(number);
+            step.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
+            step.setTotalScore(question.getTotalScore());
+            step.setDefaultScore(0d);
+            step.setHasLevel(false);
+            step.setMax(question.getTotalScore());
+            step.setMin(0d);
+            step.setInterval(question.getIntervalScore());
+            step.setScoreList(question.getScoreListArray());
+            // 增加阅卷轨迹列表获取
+            addTrack(step, question, trackService.findByStudentId(student.getId()));
+            list.add(step);
+        }
+        return list;
+    }
+
+    @Transactional
+    @Override
+    public boolean submitByStudent(Task task) {
+        if (task == null) {
+            return false;
+        }
+        try {
+            List<MarkLibrary> libraries = libraryService.findByStudentId(task.getLibraryId());
+            int start = 0;
+            int end = 0;
+            for (int i = 0; i < libraries.size(); i++) {
+                MarkLibrary library = libraries.get(i);
+                Task stepTask = new Task();
+                stepTask.setProblem(task.isProblem());
+                stepTask.setBack(task.isBack());
+                stepTask.setLibraryId(library.getId());
+                stepTask.setMarkId(task.getMarkId());
+                stepTask.setTags(task.getTags());
+                List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndMainNumber(
+                        library.getExamId(), library.getSubjectCode(), false, library.getGroupNumber());
+                if (start == end) {
+                    end = end + sList.size() * 2 - 1;
+                } else {
+                    start = end + 1;
+                    end = start + sList.size() * 2 - 1;
+                }
+                String scoreList = task.getScoreList().substring(start, end);
+                stepTask.setScoreList(scoreList);
+                String[] scores = scoreList.split(",");
+                double totalScore = 0;
+                for (String score : scores) {
+                    totalScore = totalScore + Double.parseDouble(score);
+                }
+                stepTask.setTotalScore(totalScore);
+                submitLibrary(stepTask);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("task submit faile", e);
+            return false;
+        }
+    }
+
+    /*
+     * 客户端非正常情况下退出系统时,对内存中的没有及时清除的任务进行处理
+     *
+     */
+    @Override
+    public void clearTaskMap(long cleanMapinterval) throws Exception {
+        CurrentTaskUtil.QuartzClearMap(cleanMapinterval);
+    }
+
+}

+ 85 - 85
stmms-common/src/main/java/cn/com/qmth/stmms/common/utils/PictureUrlBuilder.java

@@ -1,85 +1,85 @@
-package cn.com.qmth.stmms.common.utils;
-
-import java.text.MessageFormat;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.commons.lang3.StringUtils;
-
-/**
- * 答题卡扫描大图URL构造工具类
- * 
- * @author LS
- * 
- */
-public class PictureUrlBuilder {
-
-    private static final String SHEET_URL_TEMPLATE = "/{0}-{1}/{2}/{3}-{4}.{5}";
-
-    private static final String SLICE_URL_TEMPLATE = "/{0}-{1}/{2}/{3}-{4}.{5}";
-
-    private static final String ANSWER_URL_TEMPLATE = "/{0}/answer/{1}.{2}";
-
-    private static final String PAPER_URL_TEMPLATE = "/{0}/paper/{1}.{2}";
-
-    private static final String PACKAGE_URL_TEMPLATE = "/{0}/{1}/{2}.{3}";
-
-    private static final String DEFAULT_SUFFIX = "jpg";
-
-    private static final String DOCUMENT_SUFFIX = "pdf";
-
-    public static List<String> getSheetUrls(int examId, int campusId, String subjectCode, String examNumber,
-            int count) {
-        List<String> list = new LinkedList<String>();
-        if (StringUtils.isNotEmpty(subjectCode) && count > 0) {
-            for (int i = 1; i <= count; i++) {
-                list.add(MessageFormat.format(SHEET_URL_TEMPLATE, String.valueOf(examId), String.valueOf(campusId),
-                        subjectCode, examNumber, String.valueOf(i), DEFAULT_SUFFIX));
-            }
-        }
-        return list;
-    }
-
-    public static List<String> getSliceUrls(int examId, int campusId, String subjectCode, String examNumber, int start,
-            int count) {
-        List<String> list = new LinkedList<String>();
-        if (StringUtils.isNotEmpty(subjectCode) && count > 0) {
-            for (int i = start; i < start + count; i++) {
-                list.add(MessageFormat.format(SLICE_URL_TEMPLATE, String.valueOf(examId), String.valueOf(campusId),
-                        subjectCode, examNumber, String.valueOf(i), DEFAULT_SUFFIX));
-            }
-        }
-        return list;
-    }
-
-    public static List<String> getSliceUrls(int examId, int campusId, String subjectCode, String examNumber,
-            List<String> picNumbers) {
-        List<String> list = new LinkedList<String>();
-        if (StringUtils.isNotEmpty(subjectCode) && picNumbers != null) {
-            for (String picNumber : picNumbers) {
-                list.add(MessageFormat.format(SLICE_URL_TEMPLATE, String.valueOf(examId), String.valueOf(campusId),
-                        subjectCode, examNumber, String.valueOf(picNumber), DEFAULT_SUFFIX));
-            }
-        }
-        return list;
-    }
-
-    public static String getAnswerUrl(int examId, String subjectCode) {
-        return MessageFormat.format(ANSWER_URL_TEMPLATE, String.valueOf(examId), subjectCode, DOCUMENT_SUFFIX);
-    }
-
-    public static String getPaperUrl(int examId, String subjectCode) {
-        return MessageFormat.format(PAPER_URL_TEMPLATE, String.valueOf(examId), subjectCode, DOCUMENT_SUFFIX);
-    }
-
-    public static List<String> getPackageUrls(int examId, String packageCode, int count) {
-        List<String> list = new LinkedList<String>();
-        if (StringUtils.isNotEmpty(packageCode) && count > 0) {
-            for (int i = 1; i <= count; i++) {
-                list.add(MessageFormat.format(PACKAGE_URL_TEMPLATE, String.valueOf(examId), packageCode,
-                        String.valueOf(i), DEFAULT_SUFFIX));
-            }
-        }
-        return list;
-    }
-}
+package cn.com.qmth.stmms.common.utils;
+
+import java.text.MessageFormat;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 答题卡扫描大图URL构造工具类
+ * 
+ * @author LS
+ * 
+ */
+public class PictureUrlBuilder {
+
+    private static final String SHEET_URL_TEMPLATE = "/{0}-{1}/{2}/{3}-{4}.{5}";
+
+    private static final String SLICE_URL_TEMPLATE = "/{0}-{1}/{2}/{3}-{4}.{5}";
+
+    private static final String ANSWER_URL_TEMPLATE = "/{0}/answer/{1}.{2}";
+
+    private static final String PAPER_URL_TEMPLATE = "/{0}/paper/{1}.{2}";
+
+    private static final String PACKAGE_URL_TEMPLATE = "/{0}/{1}/{2}.{3}";
+
+    private static final String DEFAULT_SUFFIX = "jpg";
+
+    private static final String DOCUMENT_SUFFIX = "pdf";
+
+    public static List<String> getSheetUrls(int examId, int campusId, String subjectCode, String examNumber,
+            int count) {
+        List<String> list = new LinkedList<String>();
+        if (StringUtils.isNotEmpty(subjectCode) && count > 0) {
+            for (int i = 1; i <= count; i++) {
+                list.add(MessageFormat.format(SHEET_URL_TEMPLATE, String.valueOf(examId), String.valueOf(campusId),
+                        subjectCode, examNumber, String.valueOf(i), DEFAULT_SUFFIX));
+            }
+        }
+        return list;
+    }
+
+    public static List<String> getSliceUrls(int examId, int campusId, String subjectCode, String examNumber,
+            int count) {
+        List<String> list = new LinkedList<String>();
+        if (StringUtils.isNotEmpty(subjectCode) && count > 0) {
+            for (int i = 1; i <= count; i++) {
+                list.add(MessageFormat.format(SLICE_URL_TEMPLATE, String.valueOf(examId), String.valueOf(campusId),
+                        subjectCode, examNumber, String.valueOf(i), DEFAULT_SUFFIX));
+            }
+        }
+        return list;
+    }
+
+    public static List<String> getSliceUrls(int examId, int campusId, String subjectCode, String examNumber,
+            List<String> picNumbers) {
+        List<String> list = new LinkedList<String>();
+        if (StringUtils.isNotEmpty(subjectCode) && picNumbers != null) {
+            for (String picNumber : picNumbers) {
+                list.add(MessageFormat.format(SLICE_URL_TEMPLATE, String.valueOf(examId), String.valueOf(campusId),
+                        subjectCode, examNumber, String.valueOf(picNumber), DEFAULT_SUFFIX));
+            }
+        }
+        return list;
+    }
+
+    public static String getAnswerUrl(int examId, String subjectCode) {
+        return MessageFormat.format(ANSWER_URL_TEMPLATE, String.valueOf(examId), subjectCode, DOCUMENT_SUFFIX);
+    }
+
+    public static String getPaperUrl(int examId, String subjectCode) {
+        return MessageFormat.format(PAPER_URL_TEMPLATE, String.valueOf(examId), subjectCode, DOCUMENT_SUFFIX);
+    }
+
+    public static List<String> getPackageUrls(int examId, String packageCode, int count) {
+        List<String> list = new LinkedList<String>();
+        if (StringUtils.isNotEmpty(packageCode) && count > 0) {
+            for (int i = 1; i <= count; i++) {
+                list.add(MessageFormat.format(PACKAGE_URL_TEMPLATE, String.valueOf(examId), packageCode,
+                        String.valueOf(i), DEFAULT_SUFFIX));
+            }
+        }
+        return list;
+    }
+}

+ 131 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/PictureConfigItem.java

@@ -0,0 +1,131 @@
+package cn.com.qmth.stmms.admin.dto;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 单个裁切区设置模型
+ * 
+ * @author luoshi
+ *
+ */
+public class PictureConfigItem {
+
+    // 数据库中多个单元紧凑保存的拼接符,兼容旧版本只保存图片序号的模式
+    public static final String DB_ITEM_JOINER = ",";
+
+    // 数据库中单个单元紧凑保存的拼接符,兼容旧版本只保存图片序号的模式
+    public static final String DB_FIELD_JOINER = ":";
+
+    // 裁切去所属的图片序号,从1开始
+    private int i;
+
+    // 裁切区左上角X坐标
+    private int x;
+
+    // 裁切区左上角Y坐标
+    private int y;
+
+    // 裁切区宽度
+    private int w;
+
+    // 裁切区高度
+    private int h;
+
+    public PictureConfigItem() {
+
+    }
+
+    public PictureConfigItem(String text) {
+        text = StringUtils.trimToEmpty(text);
+        String[] values = StringUtils.split(text, DB_FIELD_JOINER);
+        try {
+            i = Integer.valueOf(values[0]);
+            if (values.length > 1) {
+                x = Integer.valueOf(values[1]);
+            }
+            if (values.length > 2) {
+                y = Integer.valueOf(values[2]);
+            }
+            if (values.length > 3) {
+                w = Integer.valueOf(values[3]);
+            }
+            if (values.length > 4) {
+                h = Integer.valueOf(values[4]);
+            }
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Invalid PictureConfigItem init text:" + text);
+        }
+    }
+
+    public int getI() {
+        return i;
+    }
+
+    public void setI(int i) {
+        this.i = i;
+    }
+
+    public int getX() {
+        return x;
+    }
+
+    public void setX(int x) {
+        this.x = x;
+    }
+
+    public int getY() {
+        return y;
+    }
+
+    public void setY(int y) {
+        this.y = y;
+    }
+
+    public int getW() {
+        return w;
+    }
+
+    public void setW(int w) {
+        this.w = w;
+    }
+
+    public int getH() {
+        return h;
+    }
+
+    public void setH(int h) {
+        this.h = h;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(i).append(DB_FIELD_JOINER);
+        sb.append(x).append(DB_FIELD_JOINER);
+        sb.append(y).append(DB_FIELD_JOINER);
+        sb.append(w).append(DB_FIELD_JOINER);
+        sb.append(h);
+        return sb.toString();
+    }
+
+    public List<PictureConfigItem> parse(String text) {
+        List<PictureConfigItem> list = new LinkedList<PictureConfigItem>();
+        text = StringUtils.trimToEmpty(text);
+        String[] values = StringUtils.split(text, DB_ITEM_JOINER);
+        for (String value : values) {
+            try {
+                PictureConfigItem item = new PictureConfigItem(value);
+                if (item != null && item.i > 0) {
+                    list.add(item);
+                }
+            } catch (Exception e) {
+                continue;
+            }
+        }
+        return list;
+    }
+
+}

+ 1 - 1
stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ImageCheckThread.java

@@ -90,7 +90,7 @@ public class ImageCheckThread implements Runnable {
         }
 
         List<String> sliceUrls = PictureUrlBuilder.getSliceUrls(examId, campus.getId(), student.getSubjectCode(),
-                student.getExamNumber(), 1, student.getSliceCount());
+                student.getExamNumber(), student.getSliceCount());
         for (String url : sliceUrls) {
             if (!exist(url, sliceUpyun)) {
                 record(student, true);

+ 158 - 0
stmms-web/src/main/webapp/WEB-INF/views/include/picConfig.jsp

@@ -0,0 +1,158 @@
+<%@ page language="java" pageEncoding="utf-8"%>
+<link rel="stylesheet" href="${ctxStatic}/jcrop/css/jquery.Jcrop.min.css">
+<link rel="stylesheet" href="${ctxStatic}/pic-config/css/style.css">
+<script type="text/javascript" src="${ctxStatic}/jcrop/js/jquery.Jcrop.min.js"></script>
+<script type="text/javascript" src="${ctxStatic}/jquery/jquery-1.9.1.min.js"></script>
+
+<div class="c-box" style="display:none" id="pic-config-div">
+    <div class="cont-z">
+        <div class="c-top">
+            <!--
+            <div class="c-icon">
+                <div class="iviewer_zoom_in iviewer_common iviewer_button"></div>
+                <div class="iviewer_zoom_out iviewer_common iviewer_button"></div>
+                <div class="iviewer_zoom_zero iviewer_common iviewer_button"></div>
+                <div class="iviewer_zoom_fit iviewer_common iviewer_button"></div>
+            </div>
+            -->
+            <ul class="c-tab cl" id="pic-switch" data-index="">
+            </ul>
+        </div>
+        <div class="c-mn">
+            <div class="imgcont"><img id="pic-content" src=""/></div>
+            <canvas id="pic-canvas" style="display:none"></canvas>
+        </div>
+    </div>
+    <div class="cont-y">
+        <div class="c-mn">
+            <div class="c-preview">
+                <ul id="pic-config-preview">
+                </ul>
+            </div>
+        </div>
+        <div class="c-bottom cl">
+            <a class="c-btn close" id="pic-config-clear" href="javascript:void(0)">清空</a>
+            <a class="c-btn s" id="pic-config-save" href="javascript:void(0)">保存</a>
+            <a class="c-btn" id="pic-config-cancel" href="javascript:void(0)">取消</a>
+        </div>
+    </div>
+</div>
+
+<script type="text/javascript">
+var image_server = '${imageServer}';
+var saveCallback;
+var jcrop_canvas;
+var jcrop_api;
+var image_array = [];
+$(document).ready(function(e) {
+    $('#pic-config-div').css('z-index', 9999);
+    $('#pic-config-div').height($(window).height());
+    $('#pic-config-div').width($(window).width());
+    $('#pic-config-div').offset({top:0, left:0});
+    
+    $('#pic-config-clear').click(function(){
+        $('#pic-config-preview').empty();
+    });
+    $('#pic-config-save').click(function(){
+        var result = [];
+        $('#pic-config-preview').find('li').each(function(){
+            result.push(JSON.parse($(this).attr('data-config')));
+        })
+        if(result.length==0){
+            alert('请至少裁切一张小图后再保存');
+            return;
+        }
+        $('#pic-config-div').hide();
+        if(saveCallback!=undefined){
+            saveCallback(result);
+        }
+    });
+    $('#pic-config-cancel').click(function(){
+        $('#pic-config-div').hide();
+    });
+    $('#pic-content').Jcrop({
+        onDblClick: addPreview
+    }, function() {
+        jcrop_api = this;
+    });
+    jcrop_canvas = $('#pic-canvas')[0];
+});
+
+function initPicConfigPopup(urls, config, onSave) {
+    initImages(urls);
+    initConfig(config);
+    saveCallback = onSave;
+    $('#pic-config-div').show();
+}
+
+function initImages(urls) {
+    var container = $('#pic-switch');
+    container.empty();
+    container.attr('data-index','');
+    image_array = [];
+    for(var i=1;i<=urls.length;i++) {
+        var img = new Image();
+        img.src = image_server + urls[i-1];
+        image_array.push(img);
+        
+        var dom = $('<li data-index="'+i+'"><a href="javascript:void(0)">'+i+'</a></li>').appendTo(container);
+        dom.find('a').click(function(){
+            var index = parseInt($(this).parent().attr('data-index'));
+            jcrop_api.setImage(image_array[index-1].src);
+            container.attr('data-index', index);
+            container.find('li').removeClass('on');
+            $(this).parent().addClass('on');
+        })
+    }
+    container.find('li:first').trigger('click');
+}
+
+function initConfig(defaultConfig) {
+    $('#pic-config-preview').empty();
+
+    if(defaultConfig!=undefined){
+        for(var i=0;i<defaultConfig.length;i++){
+            addPreviewByConfig(defaultConfig[i]);
+        }
+    }
+}
+
+function getCropConfig(c) {
+    var scaleX = jcrop_api.getScaleFactor()[0];
+    var scaleY = jcrop_api.getScaleFactor()[1];
+    config = {
+        x: c.x * scaleX,
+        y: c.y * scaleY,
+        w: c.w * scaleX,
+        h: c.h * scaleY
+    }
+    return config;
+}
+
+function addPreview(c) {
+    if (parseInt(c.w) > 0) {
+        var config = getCropConfig(c);
+        config.i = $('#pic-switch').attr('data-index');
+        if(config.i!='') {
+            config.i = parseInt(config.i);
+            addPreviewByConfig(config);
+        }
+        jcrop_api.release();
+    }
+}
+
+function addPreviewByConfig(config){
+    var container = $('#pic-config-preview');
+    
+    jcrop_canvas.width=config.w;
+    jcrop_canvas.height=config.h;
+    jcrop_canvas.getContext("2d").drawImage(image_array[config.i-1], config.x, config.y, config.w, config.h);
+    
+    var dom = $('<li><span class="order">'+config.i+'</span><div class="m"><img src=""/></div><span class="close">&times;</span></li>').appendTo(container);
+    dom.attr('data-config', JSON.stringify(config));
+    dom.find('img').attr('src', jcrop_canvas.toDataURL());
+    dom.find('span.close').click(function(){
+        $(this).parent().remove();
+    });
+}
+</script>

+ 1 - 0
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examIndex.jsp

@@ -125,6 +125,7 @@
 					<iframe id="mainFrame" name="mainFrame" src="" style="overflow:auto;"
 						scrolling="yes" frameborder="no" width="100%" height="650"></iframe>
 				</div>
+			    <%@include file="/WEB-INF/views/include/picConfig.jsp" %>
 			</div>
 		    <div id="footer" class="row-fluid">
 	            Copyright &copy; 2012-2014 讯飞云阅卷(高校版) - Powered By <a href="http://www.qmth.com.cn" target="_blank">启明软件</a> V1.0

二进制
stmms-web/src/main/webapp/static/jcrop/css/Jcrop.gif


+ 165 - 0
stmms-web/src/main/webapp/static/jcrop/css/jquery.Jcrop.css

@@ -0,0 +1,165 @@
+/* jquery.Jcrop.css v0.9.12 - MIT License */
+/*
+  The outer-most container in a typical Jcrop instance
+  If you are having difficulty with formatting related to styles
+  on a parent element, place any fixes here or in a like selector
+
+  You can also style this element if you want to add a border, etc
+  A better method for styling can be seen below with .jcrop-light
+  (Add a class to the holder and style elements for that extended class)
+*/
+.jcrop-holder {
+  direction: ltr;
+  text-align: left;
+}
+/* Selection Border */
+.jcrop-vline,
+.jcrop-hline {
+  background: #ffffff url("Jcrop.gif");
+  font-size: 0;
+  position: absolute;
+}
+.jcrop-vline {
+  height: 100%;
+  width: 1px !important;
+}
+.jcrop-vline.right {
+  right: 0;
+}
+.jcrop-hline {
+  height: 1px !important;
+  width: 100%;
+}
+.jcrop-hline.bottom {
+  bottom: 0;
+}
+/* Invisible click targets */
+.jcrop-tracker {
+  height: 100%;
+  width: 100%;
+  /* "turn off" link highlight */
+  -webkit-tap-highlight-color: transparent;
+  /* disable callout, image save panel */
+  -webkit-touch-callout: none;
+  /* disable cut copy paste */
+  -webkit-user-select: none;
+}
+/* Selection Handles */
+.jcrop-handle {
+  background-color: #333333;
+  border: 1px #eeeeee solid;
+  width: 7px;
+  height: 7px;
+  font-size: 1px;
+}
+.jcrop-handle.ord-n {
+  left: 50%;
+  margin-left: -4px;
+  margin-top: -4px;
+  top: 0;
+}
+.jcrop-handle.ord-s {
+  bottom: 0;
+  left: 50%;
+  margin-bottom: -4px;
+  margin-left: -4px;
+}
+.jcrop-handle.ord-e {
+  margin-right: -4px;
+  margin-top: -4px;
+  right: 0;
+  top: 50%;
+}
+.jcrop-handle.ord-w {
+  left: 0;
+  margin-left: -4px;
+  margin-top: -4px;
+  top: 50%;
+}
+.jcrop-handle.ord-nw {
+  left: 0;
+  margin-left: -4px;
+  margin-top: -4px;
+  top: 0;
+}
+.jcrop-handle.ord-ne {
+  margin-right: -4px;
+  margin-top: -4px;
+  right: 0;
+  top: 0;
+}
+.jcrop-handle.ord-se {
+  bottom: 0;
+  margin-bottom: -4px;
+  margin-right: -4px;
+  right: 0;
+}
+.jcrop-handle.ord-sw {
+  bottom: 0;
+  left: 0;
+  margin-bottom: -4px;
+  margin-left: -4px;
+}
+/* Dragbars */
+.jcrop-dragbar.ord-n,
+.jcrop-dragbar.ord-s {
+  height: 7px;
+  width: 100%;
+}
+.jcrop-dragbar.ord-e,
+.jcrop-dragbar.ord-w {
+  height: 100%;
+  width: 7px;
+}
+.jcrop-dragbar.ord-n {
+  margin-top: -4px;
+}
+.jcrop-dragbar.ord-s {
+  bottom: 0;
+  margin-bottom: -4px;
+}
+.jcrop-dragbar.ord-e {
+  margin-right: -4px;
+  right: 0;
+}
+.jcrop-dragbar.ord-w {
+  margin-left: -4px;
+}
+/* The "jcrop-light" class/extension */
+.jcrop-light .jcrop-vline,
+.jcrop-light .jcrop-hline {
+  background: #ffffff;
+  filter: alpha(opacity=70) !important;
+  opacity: .70!important;
+}
+.jcrop-light .jcrop-handle {
+  -moz-border-radius: 3px;
+  -webkit-border-radius: 3px;
+  background-color: #000000;
+  border-color: #ffffff;
+  border-radius: 3px;
+}
+/* The "jcrop-dark" class/extension */
+.jcrop-dark .jcrop-vline,
+.jcrop-dark .jcrop-hline {
+  background: #000000;
+  filter: alpha(opacity=70) !important;
+  opacity: 0.7 !important;
+}
+.jcrop-dark .jcrop-handle {
+  -moz-border-radius: 3px;
+  -webkit-border-radius: 3px;
+  background-color: #ffffff;
+  border-color: #000000;
+  border-radius: 3px;
+}
+/* Simple macro to turn off the antlines */
+.solid-line .jcrop-vline,
+.solid-line .jcrop-hline {
+  background: #ffffff;
+}
+/* Fix for twitter bootstrap et al. */
+.jcrop-holder img,
+img.jcrop-preview {
+  max-width: none;
+}

+ 29 - 0
stmms-web/src/main/webapp/static/jcrop/css/jquery.Jcrop.min.css

@@ -0,0 +1,29 @@
+/* jquery.Jcrop.min.css v0.9.12 (build:20130126) */
+.jcrop-holder{direction:ltr;text-align:left;}
+.jcrop-vline,.jcrop-hline{background:#FFF url(Jcrop.gif);font-size:0;position:absolute;}
+.jcrop-vline{height:100%;width:1px!important;}
+.jcrop-vline.right{right:0;}
+.jcrop-hline{height:1px!important;width:100%;}
+.jcrop-hline.bottom{bottom:0;}
+.jcrop-tracker{-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-webkit-user-select:none;height:100%;width:100%;}
+.jcrop-handle{background-color:#333;border:1px #EEE solid;font-size:1px;height:7px;width:7px;}
+.jcrop-handle.ord-n{left:50%;margin-left:-4px;margin-top:-4px;top:0;}
+.jcrop-handle.ord-s{bottom:0;left:50%;margin-bottom:-4px;margin-left:-4px;}
+.jcrop-handle.ord-e{margin-right:-4px;margin-top:-4px;right:0;top:50%;}
+.jcrop-handle.ord-w{left:0;margin-left:-4px;margin-top:-4px;top:50%;}
+.jcrop-handle.ord-nw{left:0;margin-left:-4px;margin-top:-4px;top:0;}
+.jcrop-handle.ord-ne{margin-right:-4px;margin-top:-4px;right:0;top:0;}
+.jcrop-handle.ord-se{bottom:0;margin-bottom:-4px;margin-right:-4px;right:0;}
+.jcrop-handle.ord-sw{bottom:0;left:0;margin-bottom:-4px;margin-left:-4px;}
+.jcrop-dragbar.ord-n,.jcrop-dragbar.ord-s{height:7px;width:100%;}
+.jcrop-dragbar.ord-e,.jcrop-dragbar.ord-w{height:100%;width:7px;}
+.jcrop-dragbar.ord-n{margin-top:-4px;}
+.jcrop-dragbar.ord-s{bottom:0;margin-bottom:-4px;}
+.jcrop-dragbar.ord-e{margin-right:-4px;right:0;}
+.jcrop-dragbar.ord-w{margin-left:-4px;}
+.jcrop-light .jcrop-vline,.jcrop-light .jcrop-hline{background:#FFF;filter:alpha(opacity=70)!important;opacity:.70!important;}
+.jcrop-light .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#000;border-color:#FFF;border-radius:3px;}
+.jcrop-dark .jcrop-vline,.jcrop-dark .jcrop-hline{background:#000;filter:alpha(opacity=70)!important;opacity:.7!important;}
+.jcrop-dark .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#FFF;border-color:#000;border-radius:3px;}
+.solid-line .jcrop-vline,.solid-line .jcrop-hline{background:#FFF;}
+.jcrop-holder img,img.jcrop-preview{max-width:none;}

+ 1694 - 0
stmms-web/src/main/webapp/static/jcrop/js/jquery.Jcrop.js

@@ -0,0 +1,1694 @@
+/**
+ * jquery.Jcrop.js v0.9.12
+ * jQuery Image Cropping Plugin - released under MIT License 
+ * Author: Kelly Hallman <khallman@gmail.com>
+ * http://github.com/tapmodo/Jcrop
+ * Copyright (c) 2008-2013 Tapmodo Interactive LLC {{{
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * }}}
+ */
+
+(function ($) {
+
+  $.Jcrop = function (obj, opt) {
+    var options = $.extend({}, $.Jcrop.defaults),
+        docOffset,
+        _ua = navigator.userAgent.toLowerCase(),
+        is_msie = /msie/.test(_ua),
+        ie6mode = /msie [1-6]\./.test(_ua);
+
+    // Internal Methods {{{
+    function px(n) {
+      return Math.round(n) + 'px';
+    }
+    function cssClass(cl) {
+      return options.baseClass + '-' + cl;
+    }
+    function supportsColorFade() {
+      return $.fx.step.hasOwnProperty('backgroundColor');
+    }
+    function getPos(obj) //{{{
+    {
+      var pos = $(obj).offset();
+      return [pos.left, pos.top];
+    }
+    //}}}
+    function mouseAbs(e) //{{{
+    {
+      return [(e.pageX - docOffset[0]), (e.pageY - docOffset[1])];
+    }
+    //}}}
+    function setOptions(opt) //{{{
+    {
+      if (typeof(opt) !== 'object') opt = {};
+      options = $.extend(options, opt);
+
+      $.each(['onChange','onSelect','onRelease','onDblClick'],function(i,e) {
+        if (typeof(options[e]) !== 'function') options[e] = function () {};
+      });
+    }
+    //}}}
+    function startDragMode(mode, pos, touch) //{{{
+    {
+      docOffset = getPos($img);
+      Tracker.setCursor(mode === 'move' ? mode : mode + '-resize');
+
+      if (mode === 'move') {
+        return Tracker.activateHandlers(createMover(pos), doneSelect, touch);
+      }
+
+      var fc = Coords.getFixed();
+      var opp = oppLockCorner(mode);
+      var opc = Coords.getCorner(oppLockCorner(opp));
+
+      Coords.setPressed(Coords.getCorner(opp));
+      Coords.setCurrent(opc);
+
+      Tracker.activateHandlers(dragmodeHandler(mode, fc), doneSelect, touch);
+    }
+    //}}}
+    function dragmodeHandler(mode, f) //{{{
+    {
+      return function (pos) {
+        if (!options.aspectRatio) {
+          switch (mode) {
+          case 'e':
+            pos[1] = f.y2;
+            break;
+          case 'w':
+            pos[1] = f.y2;
+            break;
+          case 'n':
+            pos[0] = f.x2;
+            break;
+          case 's':
+            pos[0] = f.x2;
+            break;
+          }
+        } else {
+          switch (mode) {
+          case 'e':
+            pos[1] = f.y + 1;
+            break;
+          case 'w':
+            pos[1] = f.y + 1;
+            break;
+          case 'n':
+            pos[0] = f.x + 1;
+            break;
+          case 's':
+            pos[0] = f.x + 1;
+            break;
+          }
+        }
+        Coords.setCurrent(pos);
+        Selection.update();
+      };
+    }
+    //}}}
+    function createMover(pos) //{{{
+    {
+      var lloc = pos;
+      KeyManager.watchKeys();
+
+      return function (pos) {
+        Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]);
+        lloc = pos;
+
+        Selection.update();
+      };
+    }
+    //}}}
+    function oppLockCorner(ord) //{{{
+    {
+      switch (ord) {
+      case 'n':
+        return 'sw';
+      case 's':
+        return 'nw';
+      case 'e':
+        return 'nw';
+      case 'w':
+        return 'ne';
+      case 'ne':
+        return 'sw';
+      case 'nw':
+        return 'se';
+      case 'se':
+        return 'nw';
+      case 'sw':
+        return 'ne';
+      }
+    }
+    //}}}
+    function createDragger(ord) //{{{
+    {
+      return function (e) {
+        if (options.disabled) {
+          return false;
+        }
+        if ((ord === 'move') && !options.allowMove) {
+          return false;
+        }
+        
+        // Fix position of crop area when dragged the very first time.
+        // Necessary when crop image is in a hidden element when page is loaded.
+        docOffset = getPos($img);
+
+        btndown = true;
+        startDragMode(ord, mouseAbs(e));
+        e.stopPropagation();
+        e.preventDefault();
+        return false;
+      };
+    }
+    //}}}
+    function presize($obj, w, h) //{{{
+    {
+      var nw = $obj.width(),
+          nh = $obj.height();
+      if ((nw > w) && w > 0) {
+        nw = w;
+        nh = (w / $obj.width()) * $obj.height();
+      }
+      if ((nh > h) && h > 0) {
+        nh = h;
+        nw = (h / $obj.height()) * $obj.width();
+      }
+      xscale = $obj.width() / nw;
+      yscale = $obj.height() / nh;
+      $obj.width(nw).height(nh);
+    }
+    //}}}
+    function unscale(c) //{{{
+    {
+      return {
+        x: c.x * xscale,
+        y: c.y * yscale,
+        x2: c.x2 * xscale,
+        y2: c.y2 * yscale,
+        w: c.w * xscale,
+        h: c.h * yscale
+      };
+    }
+    //}}}
+    function doneSelect(pos) //{{{
+    {
+      var c = Coords.getFixed();
+      if ((c.w > options.minSelect[0]) && (c.h > options.minSelect[1])) {
+        Selection.enableHandles();
+        Selection.done();
+      } else {
+        Selection.release();
+      }
+      Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
+    }
+    //}}}
+    function newSelection(e) //{{{
+    {
+      if (options.disabled) {
+        return false;
+      }
+      if (!options.allowSelect) {
+        return false;
+      }
+      btndown = true;
+      docOffset = getPos($img);
+      Selection.disableHandles();
+      Tracker.setCursor('crosshair');
+      var pos = mouseAbs(e);
+      Coords.setPressed(pos);
+      Selection.update();
+      Tracker.activateHandlers(selectDrag, doneSelect, e.type.substring(0,5)==='touch');
+      KeyManager.watchKeys();
+
+      e.stopPropagation();
+      e.preventDefault();
+      return false;
+    }
+    //}}}
+    function selectDrag(pos) //{{{
+    {
+      Coords.setCurrent(pos);
+      Selection.update();
+    }
+    //}}}
+    function newTracker() //{{{
+    {
+      var trk = $('<div></div>').addClass(cssClass('tracker'));
+      if (is_msie) {
+        trk.css({
+          opacity: 0,
+          backgroundColor: 'white'
+        });
+      }
+      return trk;
+    }
+    //}}}
+
+    // }}}
+    // Initialization {{{
+    // Sanitize some options {{{
+    if (typeof(obj) !== 'object') {
+      obj = $(obj)[0];
+    }
+    if (typeof(opt) !== 'object') {
+      opt = {};
+    }
+    // }}}
+    setOptions(opt);
+    // Initialize some jQuery objects {{{
+    // The values are SET on the image(s) for the interface
+    // If the original image has any of these set, they will be reset
+    // However, if you destroy() the Jcrop instance the original image's
+    // character in the DOM will be as you left it.
+    var img_css = {
+      border: 'none',
+      visibility: 'visible',
+      margin: 0,
+      padding: 0,
+      position: 'absolute',
+      top: 0,
+      left: 0
+    };
+
+    var $origimg = $(obj),
+      img_mode = true;
+
+    if (obj.tagName == 'IMG') {
+      // Fix size of crop image.
+      // Necessary when crop image is within a hidden element when page is loaded.
+      if ($origimg[0].width != 0 && $origimg[0].height != 0) {
+        // Obtain dimensions from contained img element.
+        $origimg.width($origimg[0].width);
+        $origimg.height($origimg[0].height);
+      } else {
+        // Obtain dimensions from temporary image in case the original is not loaded yet (e.g. IE 7.0). 
+        var tempImage = new Image();
+        tempImage.src = $origimg[0].src;
+        $origimg.width(tempImage.width);
+        $origimg.height(tempImage.height);
+      } 
+
+      var $img = $origimg.clone().removeAttr('id').css(img_css).show();
+
+      $img.width($origimg.width());
+      $img.height($origimg.height());
+      $origimg.after($img).hide();
+
+    } else {
+      $img = $origimg.css(img_css).show();
+      img_mode = false;
+      if (options.shade === null) { options.shade = true; }
+    }
+
+    presize($img, options.boxWidth, options.boxHeight);
+
+    var boundx = $img.width(),
+        boundy = $img.height(),
+        
+        
+        $div = $('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({
+        position: 'relative',
+        backgroundColor: options.bgColor
+      }).insertAfter($origimg).append($img);
+
+    if (options.addClass) {
+      $div.addClass(options.addClass);
+    }
+
+    var $img2 = $('<div />'),
+
+        $img_holder = $('<div />') 
+        .width('100%').height('100%').css({
+          zIndex: 310,
+          position: 'absolute',
+          overflow: 'hidden'
+        }),
+
+        $hdl_holder = $('<div />') 
+        .width('100%').height('100%').css('zIndex', 320), 
+
+        $sel = $('<div />') 
+        .css({
+          position: 'absolute',
+          zIndex: 600
+        }).dblclick(function(){
+          var c = Coords.getFixed();
+          options.onDblClick.call(api,c);
+        }).insertBefore($img).append($img_holder, $hdl_holder); 
+
+    if (img_mode) {
+
+      $img2 = $('<img />')
+          .attr('src', $img.attr('src')).css(img_css).width(boundx).height(boundy),
+
+      $img_holder.append($img2);
+
+    }
+
+    if (ie6mode) {
+      $sel.css({
+        overflowY: 'hidden'
+      });
+    }
+
+    var bound = options.boundary;
+    var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({
+      position: 'absolute',
+      top: px(-bound),
+      left: px(-bound),
+      zIndex: 290
+    }).mousedown(newSelection);
+
+    /* }}} */
+    // Set more variables {{{
+    var bgcolor = options.bgColor,
+        bgopacity = options.bgOpacity,
+        xlimit, ylimit, xmin, ymin, xscale, yscale, enabled = true,
+        btndown, animating, shift_down;
+
+    docOffset = getPos($img);
+    // }}}
+    // }}}
+    // Internal Modules {{{
+    // Touch Module {{{ 
+    var Touch = (function () {
+      // Touch support detection function adapted (under MIT License)
+      // from code by Jeffrey Sambells - http://github.com/iamamused/
+      function hasTouchSupport() {
+        var support = {}, events = ['touchstart', 'touchmove', 'touchend'],
+            el = document.createElement('div'), i;
+
+        try {
+          for(i=0; i<events.length; i++) {
+            var eventName = events[i];
+            eventName = 'on' + eventName;
+            var isSupported = (eventName in el);
+            if (!isSupported) {
+              el.setAttribute(eventName, 'return;');
+              isSupported = typeof el[eventName] == 'function';
+            }
+            support[events[i]] = isSupported;
+          }
+          return support.touchstart && support.touchend && support.touchmove;
+        }
+        catch(err) {
+          return false;
+        }
+      }
+
+      function detectSupport() {
+        if ((options.touchSupport === true) || (options.touchSupport === false)) return options.touchSupport;
+          else return hasTouchSupport();
+      }
+      return {
+        createDragger: function (ord) {
+          return function (e) {
+            if (options.disabled) {
+              return false;
+            }
+            if ((ord === 'move') && !options.allowMove) {
+              return false;
+            }
+            docOffset = getPos($img);
+            btndown = true;
+            startDragMode(ord, mouseAbs(Touch.cfilter(e)), true);
+            e.stopPropagation();
+            e.preventDefault();
+            return false;
+          };
+        },
+        newSelection: function (e) {
+          return newSelection(Touch.cfilter(e));
+        },
+        cfilter: function (e){
+          e.pageX = e.originalEvent.changedTouches[0].pageX;
+          e.pageY = e.originalEvent.changedTouches[0].pageY;
+          return e;
+        },
+        isSupported: hasTouchSupport,
+        support: detectSupport()
+      };
+    }());
+    // }}}
+    // Coords Module {{{
+    var Coords = (function () {
+      var x1 = 0,
+          y1 = 0,
+          x2 = 0,
+          y2 = 0,
+          ox, oy;
+
+      function setPressed(pos) //{{{
+      {
+        pos = rebound(pos);
+        x2 = x1 = pos[0];
+        y2 = y1 = pos[1];
+      }
+      //}}}
+      function setCurrent(pos) //{{{
+      {
+        pos = rebound(pos);
+        ox = pos[0] - x2;
+        oy = pos[1] - y2;
+        x2 = pos[0];
+        y2 = pos[1];
+      }
+      //}}}
+      function getOffset() //{{{
+      {
+        return [ox, oy];
+      }
+      //}}}
+      function moveOffset(offset) //{{{
+      {
+        var ox = offset[0],
+            oy = offset[1];
+
+        if (0 > x1 + ox) {
+          ox -= ox + x1;
+        }
+        if (0 > y1 + oy) {
+          oy -= oy + y1;
+        }
+
+        if (boundy < y2 + oy) {
+          oy += boundy - (y2 + oy);
+        }
+        if (boundx < x2 + ox) {
+          ox += boundx - (x2 + ox);
+        }
+
+        x1 += ox;
+        x2 += ox;
+        y1 += oy;
+        y2 += oy;
+      }
+      //}}}
+      function getCorner(ord) //{{{
+      {
+        var c = getFixed();
+        switch (ord) {
+        case 'ne':
+          return [c.x2, c.y];
+        case 'nw':
+          return [c.x, c.y];
+        case 'se':
+          return [c.x2, c.y2];
+        case 'sw':
+          return [c.x, c.y2];
+        }
+      }
+      //}}}
+      function getFixed() //{{{
+      {
+        if (!options.aspectRatio) {
+          return getRect();
+        }
+        // This function could use some optimization I think...
+        var aspect = options.aspectRatio,
+            min_x = options.minSize[0] / xscale,
+            
+            
+            //min_y = options.minSize[1]/yscale,
+            max_x = options.maxSize[0] / xscale,
+            max_y = options.maxSize[1] / yscale,
+            rw = x2 - x1,
+            rh = y2 - y1,
+            rwa = Math.abs(rw),
+            rha = Math.abs(rh),
+            real_ratio = rwa / rha,
+            xx, yy, w, h;
+
+        if (max_x === 0) {
+          max_x = boundx * 10;
+        }
+        if (max_y === 0) {
+          max_y = boundy * 10;
+        }
+        if (real_ratio < aspect) {
+          yy = y2;
+          w = rha * aspect;
+          xx = rw < 0 ? x1 - w : w + x1;
+
+          if (xx < 0) {
+            xx = 0;
+            h = Math.abs((xx - x1) / aspect);
+            yy = rh < 0 ? y1 - h : h + y1;
+          } else if (xx > boundx) {
+            xx = boundx;
+            h = Math.abs((xx - x1) / aspect);
+            yy = rh < 0 ? y1 - h : h + y1;
+          }
+        } else {
+          xx = x2;
+          h = rwa / aspect;
+          yy = rh < 0 ? y1 - h : y1 + h;
+          if (yy < 0) {
+            yy = 0;
+            w = Math.abs((yy - y1) * aspect);
+            xx = rw < 0 ? x1 - w : w + x1;
+          } else if (yy > boundy) {
+            yy = boundy;
+            w = Math.abs(yy - y1) * aspect;
+            xx = rw < 0 ? x1 - w : w + x1;
+          }
+        }
+
+        // Magic %-)
+        if (xx > x1) { // right side
+          if (xx - x1 < min_x) {
+            xx = x1 + min_x;
+          } else if (xx - x1 > max_x) {
+            xx = x1 + max_x;
+          }
+          if (yy > y1) {
+            yy = y1 + (xx - x1) / aspect;
+          } else {
+            yy = y1 - (xx - x1) / aspect;
+          }
+        } else if (xx < x1) { // left side
+          if (x1 - xx < min_x) {
+            xx = x1 - min_x;
+          } else if (x1 - xx > max_x) {
+            xx = x1 - max_x;
+          }
+          if (yy > y1) {
+            yy = y1 + (x1 - xx) / aspect;
+          } else {
+            yy = y1 - (x1 - xx) / aspect;
+          }
+        }
+
+        if (xx < 0) {
+          x1 -= xx;
+          xx = 0;
+        } else if (xx > boundx) {
+          x1 -= xx - boundx;
+          xx = boundx;
+        }
+
+        if (yy < 0) {
+          y1 -= yy;
+          yy = 0;
+        } else if (yy > boundy) {
+          y1 -= yy - boundy;
+          yy = boundy;
+        }
+
+        return makeObj(flipCoords(x1, y1, xx, yy));
+      }
+      //}}}
+      function rebound(p) //{{{
+      {
+        if (p[0] < 0) p[0] = 0;
+        if (p[1] < 0) p[1] = 0;
+
+        if (p[0] > boundx) p[0] = boundx;
+        if (p[1] > boundy) p[1] = boundy;
+
+        return [Math.round(p[0]), Math.round(p[1])];
+      }
+      //}}}
+      function flipCoords(x1, y1, x2, y2) //{{{
+      {
+        var xa = x1,
+            xb = x2,
+            ya = y1,
+            yb = y2;
+        if (x2 < x1) {
+          xa = x2;
+          xb = x1;
+        }
+        if (y2 < y1) {
+          ya = y2;
+          yb = y1;
+        }
+        return [xa, ya, xb, yb];
+      }
+      //}}}
+      function getRect() //{{{
+      {
+        var xsize = x2 - x1,
+            ysize = y2 - y1,
+            delta;
+
+        if (xlimit && (Math.abs(xsize) > xlimit)) {
+          x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit);
+        }
+        if (ylimit && (Math.abs(ysize) > ylimit)) {
+          y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit);
+        }
+
+        if (ymin / yscale && (Math.abs(ysize) < ymin / yscale)) {
+          y2 = (ysize > 0) ? (y1 + ymin / yscale) : (y1 - ymin / yscale);
+        }
+        if (xmin / xscale && (Math.abs(xsize) < xmin / xscale)) {
+          x2 = (xsize > 0) ? (x1 + xmin / xscale) : (x1 - xmin / xscale);
+        }
+
+        if (x1 < 0) {
+          x2 -= x1;
+          x1 -= x1;
+        }
+        if (y1 < 0) {
+          y2 -= y1;
+          y1 -= y1;
+        }
+        if (x2 < 0) {
+          x1 -= x2;
+          x2 -= x2;
+        }
+        if (y2 < 0) {
+          y1 -= y2;
+          y2 -= y2;
+        }
+        if (x2 > boundx) {
+          delta = x2 - boundx;
+          x1 -= delta;
+          x2 -= delta;
+        }
+        if (y2 > boundy) {
+          delta = y2 - boundy;
+          y1 -= delta;
+          y2 -= delta;
+        }
+        if (x1 > boundx) {
+          delta = x1 - boundy;
+          y2 -= delta;
+          y1 -= delta;
+        }
+        if (y1 > boundy) {
+          delta = y1 - boundy;
+          y2 -= delta;
+          y1 -= delta;
+        }
+
+        return makeObj(flipCoords(x1, y1, x2, y2));
+      }
+      //}}}
+      function makeObj(a) //{{{
+      {
+        return {
+          x: a[0],
+          y: a[1],
+          x2: a[2],
+          y2: a[3],
+          w: a[2] - a[0],
+          h: a[3] - a[1]
+        };
+      }
+      //}}}
+
+      return {
+        flipCoords: flipCoords,
+        setPressed: setPressed,
+        setCurrent: setCurrent,
+        getOffset: getOffset,
+        moveOffset: moveOffset,
+        getCorner: getCorner,
+        getFixed: getFixed
+      };
+    }());
+
+    //}}}
+    // Shade Module {{{
+    var Shade = (function() {
+      var enabled = false,
+          holder = $('<div />').css({
+            position: 'absolute',
+            zIndex: 240,
+            opacity: 0
+          }),
+          shades = {
+            top: createShade(),
+            left: createShade().height(boundy),
+            right: createShade().height(boundy),
+            bottom: createShade()
+          };
+
+      function resizeShades(w,h) {
+        shades.left.css({ height: px(h) });
+        shades.right.css({ height: px(h) });
+      }
+      function updateAuto()
+      {
+        return updateShade(Coords.getFixed());
+      }
+      function updateShade(c)
+      {
+        shades.top.css({
+          left: px(c.x),
+          width: px(c.w),
+          height: px(c.y)
+        });
+        shades.bottom.css({
+          top: px(c.y2),
+          left: px(c.x),
+          width: px(c.w),
+          height: px(boundy-c.y2)
+        });
+        shades.right.css({
+          left: px(c.x2),
+          width: px(boundx-c.x2)
+        });
+        shades.left.css({
+          width: px(c.x)
+        });
+      }
+      function createShade() {
+        return $('<div />').css({
+          position: 'absolute',
+          backgroundColor: options.shadeColor||options.bgColor
+        }).appendTo(holder);
+      }
+      function enableShade() {
+        if (!enabled) {
+          enabled = true;
+          holder.insertBefore($img);
+          updateAuto();
+          Selection.setBgOpacity(1,0,1);
+          $img2.hide();
+
+          setBgColor(options.shadeColor||options.bgColor,1);
+          if (Selection.isAwake())
+          {
+            setOpacity(options.bgOpacity,1);
+          }
+            else setOpacity(1,1);
+        }
+      }
+      function setBgColor(color,now) {
+        colorChangeMacro(getShades(),color,now);
+      }
+      function disableShade() {
+        if (enabled) {
+          holder.remove();
+          $img2.show();
+          enabled = false;
+          if (Selection.isAwake()) {
+            Selection.setBgOpacity(options.bgOpacity,1,1);
+          } else {
+            Selection.setBgOpacity(1,1,1);
+            Selection.disableHandles();
+          }
+          colorChangeMacro($div,0,1);
+        }
+      }
+      function setOpacity(opacity,now) {
+        if (enabled) {
+          if (options.bgFade && !now) {
+            holder.animate({
+              opacity: 1-opacity
+            },{
+              queue: false,
+              duration: options.fadeTime
+            });
+          }
+          else holder.css({opacity:1-opacity});
+        }
+      }
+      function refreshAll() {
+        options.shade ? enableShade() : disableShade();
+        if (Selection.isAwake()) setOpacity(options.bgOpacity);
+      }
+      function getShades() {
+        return holder.children();
+      }
+
+      return {
+        update: updateAuto,
+        updateRaw: updateShade,
+        getShades: getShades,
+        setBgColor: setBgColor,
+        enable: enableShade,
+        disable: disableShade,
+        resize: resizeShades,
+        refresh: refreshAll,
+        opacity: setOpacity
+      };
+    }());
+    // }}}
+    // Selection Module {{{
+    var Selection = (function () {
+      var awake,
+          hdep = 370,
+          borders = {},
+          handle = {},
+          dragbar = {},
+          seehandles = false;
+
+      // Private Methods
+      function insertBorder(type) //{{{
+      {
+        var jq = $('<div />').css({
+          position: 'absolute',
+          opacity: options.borderOpacity
+        }).addClass(cssClass(type));
+        $img_holder.append(jq);
+        return jq;
+      }
+      //}}}
+      function dragDiv(ord, zi) //{{{
+      {
+        var jq = $('<div />').mousedown(createDragger(ord)).css({
+          cursor: ord + '-resize',
+          position: 'absolute',
+          zIndex: zi
+        }).addClass('ord-'+ord);
+
+        if (Touch.support) {
+          jq.bind('touchstart.jcrop', Touch.createDragger(ord));
+        }
+
+        $hdl_holder.append(jq);
+        return jq;
+      }
+      //}}}
+      function insertHandle(ord) //{{{
+      {
+        var hs = options.handleSize,
+
+          div = dragDiv(ord, hdep++).css({
+            opacity: options.handleOpacity
+          }).addClass(cssClass('handle'));
+
+        if (hs) { div.width(hs).height(hs); }
+
+        return div;
+      }
+      //}}}
+      function insertDragbar(ord) //{{{
+      {
+        return dragDiv(ord, hdep++).addClass('jcrop-dragbar');
+      }
+      //}}}
+      function createDragbars(li) //{{{
+      {
+        var i;
+        for (i = 0; i < li.length; i++) {
+          dragbar[li[i]] = insertDragbar(li[i]);
+        }
+      }
+      //}}}
+      function createBorders(li) //{{{
+      {
+        var cl,i;
+        for (i = 0; i < li.length; i++) {
+          switch(li[i]){
+            case'n': cl='hline'; break;
+            case's': cl='hline bottom'; break;
+            case'e': cl='vline right'; break;
+            case'w': cl='vline'; break;
+          }
+          borders[li[i]] = insertBorder(cl);
+        }
+      }
+      //}}}
+      function createHandles(li) //{{{
+      {
+        var i;
+        for (i = 0; i < li.length; i++) {
+          handle[li[i]] = insertHandle(li[i]);
+        }
+      }
+      //}}}
+      function moveto(x, y) //{{{
+      {
+        if (!options.shade) {
+          $img2.css({
+            top: px(-y),
+            left: px(-x)
+          });
+        }
+        $sel.css({
+          top: px(y),
+          left: px(x)
+        });
+      }
+      //}}}
+      function resize(w, h) //{{{
+      {
+        $sel.width(Math.round(w)).height(Math.round(h));
+      }
+      //}}}
+      function refresh() //{{{
+      {
+        var c = Coords.getFixed();
+
+        Coords.setPressed([c.x, c.y]);
+        Coords.setCurrent([c.x2, c.y2]);
+
+        updateVisible();
+      }
+      //}}}
+
+      // Internal Methods
+      function updateVisible(select) //{{{
+      {
+        if (awake) {
+          return update(select);
+        }
+      }
+      //}}}
+      function update(select) //{{{
+      {
+        var c = Coords.getFixed();
+
+        resize(c.w, c.h);
+        moveto(c.x, c.y);
+        if (options.shade) Shade.updateRaw(c);
+
+        awake || show();
+
+        if (select) {
+          options.onSelect.call(api, unscale(c));
+        } else {
+          options.onChange.call(api, unscale(c));
+        }
+      }
+      //}}}
+      function setBgOpacity(opacity,force,now) //{{{
+      {
+        if (!awake && !force) return;
+        if (options.bgFade && !now) {
+          $img.animate({
+            opacity: opacity
+          },{
+            queue: false,
+            duration: options.fadeTime
+          });
+        } else {
+          $img.css('opacity', opacity);
+        }
+      }
+      //}}}
+      function show() //{{{
+      {
+        $sel.show();
+
+        if (options.shade) Shade.opacity(bgopacity);
+          else setBgOpacity(bgopacity,true);
+
+        awake = true;
+      }
+      //}}}
+      function release() //{{{
+      {
+        disableHandles();
+        $sel.hide();
+
+        if (options.shade) Shade.opacity(1);
+          else setBgOpacity(1);
+
+        awake = false;
+        options.onRelease.call(api);
+      }
+      //}}}
+      function showHandles() //{{{
+      {
+        if (seehandles) {
+          $hdl_holder.show();
+        }
+      }
+      //}}}
+      function enableHandles() //{{{
+      {
+        seehandles = true;
+        if (options.allowResize) {
+          $hdl_holder.show();
+          return true;
+        }
+      }
+      //}}}
+      function disableHandles() //{{{
+      {
+        seehandles = false;
+        $hdl_holder.hide();
+      } 
+      //}}}
+      function animMode(v) //{{{
+      {
+        if (v) {
+          animating = true;
+          disableHandles();
+        } else {
+          animating = false;
+          enableHandles();
+        }
+      } 
+      //}}}
+      function done() //{{{
+      {
+        animMode(false);
+        refresh();
+      } 
+      //}}}
+      // Insert draggable elements {{{
+      // Insert border divs for outline
+
+      if (options.dragEdges && $.isArray(options.createDragbars))
+        createDragbars(options.createDragbars);
+
+      if ($.isArray(options.createHandles))
+        createHandles(options.createHandles);
+
+      if (options.drawBorders && $.isArray(options.createBorders))
+        createBorders(options.createBorders);
+
+      //}}}
+
+      // This is a hack for iOS5 to support drag/move touch functionality
+      $(document).bind('touchstart.jcrop-ios',function(e) {
+        if ($(e.currentTarget).hasClass('jcrop-tracker')) e.stopPropagation();
+      });
+
+      var $track = newTracker().mousedown(createDragger('move')).css({
+        cursor: 'move',
+        position: 'absolute',
+        zIndex: 360
+      });
+
+      if (Touch.support) {
+        $track.bind('touchstart.jcrop', Touch.createDragger('move'));
+      }
+
+      $img_holder.append($track);
+      disableHandles();
+
+      return {
+        updateVisible: updateVisible,
+        update: update,
+        release: release,
+        refresh: refresh,
+        isAwake: function () {
+          return awake;
+        },
+        setCursor: function (cursor) {
+          $track.css('cursor', cursor);
+        },
+        enableHandles: enableHandles,
+        enableOnly: function () {
+          seehandles = true;
+        },
+        showHandles: showHandles,
+        disableHandles: disableHandles,
+        animMode: animMode,
+        setBgOpacity: setBgOpacity,
+        done: done
+      };
+    }());
+    
+    //}}}
+    // Tracker Module {{{
+    var Tracker = (function () {
+      var onMove = function () {},
+          onDone = function () {},
+          trackDoc = options.trackDocument;
+
+      function toFront(touch) //{{{
+      {
+        $trk.css({
+          zIndex: 450
+        });
+
+        if (touch)
+          $(document)
+            .bind('touchmove.jcrop', trackTouchMove)
+            .bind('touchend.jcrop', trackTouchEnd);
+
+        else if (trackDoc)
+          $(document)
+            .bind('mousemove.jcrop',trackMove)
+            .bind('mouseup.jcrop',trackUp);
+      } 
+      //}}}
+      function toBack() //{{{
+      {
+        $trk.css({
+          zIndex: 290
+        });
+        $(document).unbind('.jcrop');
+      } 
+      //}}}
+      function trackMove(e) //{{{
+      {
+        onMove(mouseAbs(e));
+        return false;
+      } 
+      //}}}
+      function trackUp(e) //{{{
+      {
+        e.preventDefault();
+        e.stopPropagation();
+
+        if (btndown) {
+          btndown = false;
+
+          onDone(mouseAbs(e));
+
+          if (Selection.isAwake()) {
+            options.onSelect.call(api, unscale(Coords.getFixed()));
+          }
+
+          toBack();
+          onMove = function () {};
+          onDone = function () {};
+        }
+
+        return false;
+      }
+      //}}}
+      function activateHandlers(move, done, touch) //{{{
+      {
+        btndown = true;
+        onMove = move;
+        onDone = done;
+        toFront(touch);
+        return false;
+      }
+      //}}}
+      function trackTouchMove(e) //{{{
+      {
+        onMove(mouseAbs(Touch.cfilter(e)));
+        return false;
+      }
+      //}}}
+      function trackTouchEnd(e) //{{{
+      {
+        return trackUp(Touch.cfilter(e));
+      }
+      //}}}
+      function setCursor(t) //{{{
+      {
+        $trk.css('cursor', t);
+      }
+      //}}}
+
+      if (!trackDoc) {
+        $trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);
+      }
+
+      $img.before($trk);
+      return {
+        activateHandlers: activateHandlers,
+        setCursor: setCursor
+      };
+    }());
+    //}}}
+    // KeyManager Module {{{
+    var KeyManager = (function () {
+      var $keymgr = $('<input type="radio" />').css({
+        position: 'fixed',
+        left: '-120px',
+        width: '12px'
+      }).addClass('jcrop-keymgr'),
+
+        $keywrap = $('<div />').css({
+          position: 'absolute',
+          overflow: 'hidden'
+        }).append($keymgr);
+
+      function watchKeys() //{{{
+      {
+        if (options.keySupport) {
+          $keymgr.show();
+          $keymgr.focus();
+        }
+      }
+      //}}}
+      function onBlur(e) //{{{
+      {
+        $keymgr.hide();
+      }
+      //}}}
+      function doNudge(e, x, y) //{{{
+      {
+        if (options.allowMove) {
+          Coords.moveOffset([x, y]);
+          Selection.updateVisible(true);
+        }
+        e.preventDefault();
+        e.stopPropagation();
+      }
+      //}}}
+      function parseKey(e) //{{{
+      {
+        if (e.ctrlKey || e.metaKey) {
+          return true;
+        }
+        shift_down = e.shiftKey ? true : false;
+        var nudge = shift_down ? 10 : 1;
+
+        switch (e.keyCode) {
+        case 37:
+          doNudge(e, -nudge, 0);
+          break;
+        case 39:
+          doNudge(e, nudge, 0);
+          break;
+        case 38:
+          doNudge(e, 0, -nudge);
+          break;
+        case 40:
+          doNudge(e, 0, nudge);
+          break;
+        case 27:
+          if (options.allowSelect) Selection.release();
+          break;
+        case 9:
+          return true;
+        }
+
+        return false;
+      }
+      //}}}
+
+      if (options.keySupport) {
+        $keymgr.keydown(parseKey).blur(onBlur);
+        if (ie6mode || !options.fixedSupport) {
+          $keymgr.css({
+            position: 'absolute',
+            left: '-20px'
+          });
+          $keywrap.append($keymgr).insertBefore($img);
+        } else {
+          $keymgr.insertBefore($img);
+        }
+      }
+
+
+      return {
+        watchKeys: watchKeys
+      };
+    }());
+    //}}}
+    // }}}
+    // API methods {{{
+    function setClass(cname) //{{{
+    {
+      $div.removeClass().addClass(cssClass('holder')).addClass(cname);
+    }
+    //}}}
+    function animateTo(a, callback) //{{{
+    {
+      var x1 = a[0] / xscale,
+          y1 = a[1] / yscale,
+          x2 = a[2] / xscale,
+          y2 = a[3] / yscale;
+
+      if (animating) {
+        return;
+      }
+
+      var animto = Coords.flipCoords(x1, y1, x2, y2),
+          c = Coords.getFixed(),
+          initcr = [c.x, c.y, c.x2, c.y2],
+          animat = initcr,
+          interv = options.animationDelay,
+          ix1 = animto[0] - initcr[0],
+          iy1 = animto[1] - initcr[1],
+          ix2 = animto[2] - initcr[2],
+          iy2 = animto[3] - initcr[3],
+          pcent = 0,
+          velocity = options.swingSpeed;
+
+      x1 = animat[0];
+      y1 = animat[1];
+      x2 = animat[2];
+      y2 = animat[3];
+
+      Selection.animMode(true);
+      var anim_timer;
+
+      function queueAnimator() {
+        window.setTimeout(animator, interv);
+      }
+      var animator = (function () {
+        return function () {
+          pcent += (100 - pcent) / velocity;
+
+          animat[0] = Math.round(x1 + ((pcent / 100) * ix1));
+          animat[1] = Math.round(y1 + ((pcent / 100) * iy1));
+          animat[2] = Math.round(x2 + ((pcent / 100) * ix2));
+          animat[3] = Math.round(y2 + ((pcent / 100) * iy2));
+
+          if (pcent >= 99.8) {
+            pcent = 100;
+          }
+          if (pcent < 100) {
+            setSelectRaw(animat);
+            queueAnimator();
+          } else {
+            Selection.done();
+            Selection.animMode(false);
+            if (typeof(callback) === 'function') {
+              callback.call(api);
+            }
+          }
+        };
+      }());
+      queueAnimator();
+    }
+    //}}}
+    function setSelect(rect) //{{{
+    {
+      setSelectRaw([rect[0] / xscale, rect[1] / yscale, rect[2] / xscale, rect[3] / yscale]);
+      options.onSelect.call(api, unscale(Coords.getFixed()));
+      Selection.enableHandles();
+    }
+    //}}}
+    function setSelectRaw(l) //{{{
+    {
+      Coords.setPressed([l[0], l[1]]);
+      Coords.setCurrent([l[2], l[3]]);
+      Selection.update();
+    }
+    //}}}
+    function tellSelect() //{{{
+    {
+      return unscale(Coords.getFixed());
+    }
+    //}}}
+    function tellScaled() //{{{
+    {
+      return Coords.getFixed();
+    }
+    //}}}
+    function setOptionsNew(opt) //{{{
+    {
+      setOptions(opt);
+      interfaceUpdate();
+    }
+    //}}}
+    function disableCrop() //{{{
+    {
+      options.disabled = true;
+      Selection.disableHandles();
+      Selection.setCursor('default');
+      Tracker.setCursor('default');
+    }
+    //}}}
+    function enableCrop() //{{{
+    {
+      options.disabled = false;
+      interfaceUpdate();
+    }
+    //}}}
+    function cancelCrop() //{{{
+    {
+      Selection.done();
+      Tracker.activateHandlers(null, null);
+    }
+    //}}}
+    function destroy() //{{{
+    {
+      $div.remove();
+      $origimg.show();
+      $origimg.css('visibility','visible');
+      $(obj).removeData('Jcrop');
+    }
+    //}}}
+    function setImage(src, callback) //{{{
+    {
+      Selection.release();
+      disableCrop();
+      var img = new Image();
+      img.onload = function () {
+        var iw = img.width;
+        var ih = img.height;
+        var bw = options.boxWidth;
+        var bh = options.boxHeight;
+        $img.width(iw).height(ih);
+        $img.attr('src', src);
+        $img2.attr('src', src);
+        presize($img, bw, bh);
+        boundx = $img.width();
+        boundy = $img.height();
+        $img2.width(boundx).height(boundy);
+        $trk.width(boundx + (bound * 2)).height(boundy + (bound * 2));
+        $div.width(boundx).height(boundy);
+        Shade.resize(boundx,boundy);
+        enableCrop();
+
+        if (typeof(callback) === 'function') {
+          callback.call(api);
+        }
+      };
+      img.src = src;
+    }
+    //}}}
+    function colorChangeMacro($obj,color,now) {
+      var mycolor = color || options.bgColor;
+      if (options.bgFade && supportsColorFade() && options.fadeTime && !now) {
+        $obj.animate({
+          backgroundColor: mycolor
+        }, {
+          queue: false,
+          duration: options.fadeTime
+        });
+      } else {
+        $obj.css('backgroundColor', mycolor);
+      }
+    }
+    function interfaceUpdate(alt) //{{{
+    // This method tweaks the interface based on options object.
+    // Called when options are changed and at end of initialization.
+    {
+      if (options.allowResize) {
+        if (alt) {
+          Selection.enableOnly();
+        } else {
+          Selection.enableHandles();
+        }
+      } else {
+        Selection.disableHandles();
+      }
+
+      Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
+      Selection.setCursor(options.allowMove ? 'move' : 'default');
+
+      if (options.hasOwnProperty('trueSize')) {
+        xscale = options.trueSize[0] / boundx;
+        yscale = options.trueSize[1] / boundy;
+      }
+
+      if (options.hasOwnProperty('setSelect')) {
+        setSelect(options.setSelect);
+        Selection.done();
+        delete(options.setSelect);
+      }
+
+      Shade.refresh();
+
+      if (options.bgColor != bgcolor) {
+        colorChangeMacro(
+          options.shade? Shade.getShades(): $div,
+          options.shade?
+            (options.shadeColor || options.bgColor):
+            options.bgColor
+        );
+        bgcolor = options.bgColor;
+      }
+
+      if (bgopacity != options.bgOpacity) {
+        bgopacity = options.bgOpacity;
+        if (options.shade) Shade.refresh();
+          else Selection.setBgOpacity(bgopacity);
+      }
+
+      xlimit = options.maxSize[0] || 0;
+      ylimit = options.maxSize[1] || 0;
+      xmin = options.minSize[0] || 0;
+      ymin = options.minSize[1] || 0;
+
+      if (options.hasOwnProperty('outerImage')) {
+        $img.attr('src', options.outerImage);
+        delete(options.outerImage);
+      }
+
+      Selection.refresh();
+    }
+    //}}}
+    //}}}
+
+    if (Touch.support) $trk.bind('touchstart.jcrop', Touch.newSelection);
+
+    $hdl_holder.hide();
+    interfaceUpdate(true);
+
+    var api = {
+      setImage: setImage,
+      animateTo: animateTo,
+      setSelect: setSelect,
+      setOptions: setOptionsNew,
+      tellSelect: tellSelect,
+      tellScaled: tellScaled,
+      setClass: setClass,
+
+      disable: disableCrop,
+      enable: enableCrop,
+      cancel: cancelCrop,
+      release: Selection.release,
+      destroy: destroy,
+
+      focus: KeyManager.watchKeys,
+
+      getBounds: function () {
+        return [boundx * xscale, boundy * yscale];
+      },
+      getWidgetSize: function () {
+        return [boundx, boundy];
+      },
+      getScaleFactor: function () {
+        return [xscale, yscale];
+      },
+      getOptions: function() {
+        // careful: internal values are returned
+        return options;
+      },
+
+      ui: {
+        holder: $div,
+        selection: $sel
+      }
+    };
+
+    if (is_msie) $div.bind('selectstart', function () { return false; });
+
+    $origimg.data('Jcrop', api);
+    return api;
+  };
+  $.fn.Jcrop = function (options, callback) //{{{
+  {
+    var api;
+    // Iterate over each object, attach Jcrop
+    this.each(function () {
+      // If we've already attached to this object
+      if ($(this).data('Jcrop')) {
+        // The API can be requested this way (undocumented)
+        if (options === 'api') return $(this).data('Jcrop');
+        // Otherwise, we just reset the options...
+        else $(this).data('Jcrop').setOptions(options);
+      }
+      // If we haven't been attached, preload and attach
+      else {
+        if (this.tagName == 'IMG')
+          $.Jcrop.Loader(this,function(){
+            $(this).css({display:'block',visibility:'hidden'});
+            api = $.Jcrop(this, options);
+            if ($.isFunction(callback)) callback.call(api);
+          });
+        else {
+          $(this).css({display:'block',visibility:'hidden'});
+          api = $.Jcrop(this, options);
+          if ($.isFunction(callback)) callback.call(api);
+        }
+      }
+    });
+
+    // Return "this" so the object is chainable (jQuery-style)
+    return this;
+  };
+  //}}}
+  // $.Jcrop.Loader - basic image loader {{{
+
+  $.Jcrop.Loader = function(imgobj,success,error){
+    var $img = $(imgobj), img = $img[0];
+
+    function completeCheck(){
+      if (img.complete) {
+        $img.unbind('.jcloader');
+        if ($.isFunction(success)) success.call(img);
+      }
+      else window.setTimeout(completeCheck,50);
+    }
+
+    $img
+      .bind('load.jcloader',completeCheck)
+      .bind('error.jcloader',function(e){
+        $img.unbind('.jcloader');
+        if ($.isFunction(error)) error.call(img);
+      });
+
+    if (img.complete && $.isFunction(success)){
+      $img.unbind('.jcloader');
+      success.call(img);
+    }
+  };
+
+  //}}}
+  // Global Defaults {{{
+  $.Jcrop.defaults = {
+
+    // Basic Settings
+    allowSelect: true,
+    allowMove: true,
+    allowResize: true,
+
+    trackDocument: true,
+
+    // Styling Options
+    baseClass: 'jcrop',
+    addClass: null,
+    bgColor: 'black',
+    bgOpacity: 0.6,
+    bgFade: false,
+    borderOpacity: 0.4,
+    handleOpacity: 0.5,
+    handleSize: null,
+
+    aspectRatio: 0,
+    keySupport: true,
+    createHandles: ['n','s','e','w','nw','ne','se','sw'],
+    createDragbars: ['n','s','e','w'],
+    createBorders: ['n','s','e','w'],
+    drawBorders: true,
+    dragEdges: true,
+    fixedSupport: true,
+    touchSupport: null,
+
+    shade: null,
+
+    boxWidth: 0,
+    boxHeight: 0,
+    boundary: 2,
+    fadeTime: 400,
+    animationDelay: 20,
+    swingSpeed: 3,
+
+    minSelect: [0, 0],
+    maxSize: [0, 0],
+    minSize: [0, 0],
+
+    // Callbacks / Event Handlers
+    onChange: function () {},
+    onSelect: function () {},
+    onDblClick: function () {},
+    onRelease: function () {}
+  };
+
+  // }}}
+}(jQuery));

+ 22 - 0
stmms-web/src/main/webapp/static/jcrop/js/jquery.Jcrop.min.js

@@ -0,0 +1,22 @@
+/**
+ * jquery.Jcrop.min.js v0.9.12 (build:20130202)
+ * jQuery Image Cropping Plugin - released under MIT License
+ * Copyright (c) 2008-2013 Tapmodo Interactive LLC
+ * https://github.com/tapmodo/Jcrop
+ */
+(function(a){a.Jcrop=function(b,c){function i(a){return Math.round(a)+"px"}function j(a){return d.baseClass+"-"+a}function k(){return a.fx.step.hasOwnProperty("backgroundColor")}function l(b){var c=a(b).offset();return[c.left,c.top]}function m(a){return[a.pageX-e[0],a.pageY-e[1]]}function n(b){typeof b!="object"&&(b={}),d=a.extend(d,b),a.each(["onChange","onSelect","onRelease","onDblClick"],function(a,b){typeof d[b]!="function"&&(d[b]=function(){})})}function o(a,b,c){e=l(D),bc.setCursor(a==="move"?a:a+"-resize");if(a==="move")return bc.activateHandlers(q(b),v,c);var d=_.getFixed(),f=r(a),g=_.getCorner(r(f));_.setPressed(_.getCorner(f)),_.setCurrent(g),bc.activateHandlers(p(a,d),v,c)}function p(a,b){return function(c){if(!d.aspectRatio)switch(a){case"e":c[1]=b.y2;break;case"w":c[1]=b.y2;break;case"n":c[0]=b.x2;break;case"s":c[0]=b.x2}else switch(a){case"e":c[1]=b.y+1;break;case"w":c[1]=b.y+1;break;case"n":c[0]=b.x+1;break;case"s":c[0]=b.x+1}_.setCurrent(c),bb.update()}}function q(a){var b=a;return bd.watchKeys
+(),function(a){_.moveOffset([a[0]-b[0],a[1]-b[1]]),b=a,bb.update()}}function r(a){switch(a){case"n":return"sw";case"s":return"nw";case"e":return"nw";case"w":return"ne";case"ne":return"sw";case"nw":return"se";case"se":return"nw";case"sw":return"ne"}}function s(a){return function(b){return d.disabled?!1:a==="move"&&!d.allowMove?!1:(e=l(D),W=!0,o(a,m(b)),b.stopPropagation(),b.preventDefault(),!1)}}function t(a,b,c){var d=a.width(),e=a.height();d>b&&b>0&&(d=b,e=b/a.width()*a.height()),e>c&&c>0&&(e=c,d=c/a.height()*a.width()),T=a.width()/d,U=a.height()/e,a.width(d).height(e)}function u(a){return{x:a.x*T,y:a.y*U,x2:a.x2*T,y2:a.y2*U,w:a.w*T,h:a.h*U}}function v(a){var b=_.getFixed();b.w>d.minSelect[0]&&b.h>d.minSelect[1]?(bb.enableHandles(),bb.done()):bb.release(),bc.setCursor(d.allowSelect?"crosshair":"default")}function w(a){if(d.disabled)return!1;if(!d.allowSelect)return!1;W=!0,e=l(D),bb.disableHandles(),bc.setCursor("crosshair");var b=m(a);return _.setPressed(b),bb.update(),bc.activateHandlers(x,v,a.type.substring
+(0,5)==="touch"),bd.watchKeys(),a.stopPropagation(),a.preventDefault(),!1}function x(a){_.setCurrent(a),bb.update()}function y(){var b=a("<div></div>").addClass(j("tracker"));return g&&b.css({opacity:0,backgroundColor:"white"}),b}function be(a){G.removeClass().addClass(j("holder")).addClass(a)}function bf(a,b){function t(){window.setTimeout(u,l)}var c=a[0]/T,e=a[1]/U,f=a[2]/T,g=a[3]/U;if(X)return;var h=_.flipCoords(c,e,f,g),i=_.getFixed(),j=[i.x,i.y,i.x2,i.y2],k=j,l=d.animationDelay,m=h[0]-j[0],n=h[1]-j[1],o=h[2]-j[2],p=h[3]-j[3],q=0,r=d.swingSpeed;c=k[0],e=k[1],f=k[2],g=k[3],bb.animMode(!0);var s,u=function(){return function(){q+=(100-q)/r,k[0]=Math.round(c+q/100*m),k[1]=Math.round(e+q/100*n),k[2]=Math.round(f+q/100*o),k[3]=Math.round(g+q/100*p),q>=99.8&&(q=100),q<100?(bh(k),t()):(bb.done(),bb.animMode(!1),typeof b=="function"&&b.call(bs))}}();t()}function bg(a){bh([a[0]/T,a[1]/U,a[2]/T,a[3]/U]),d.onSelect.call(bs,u(_.getFixed())),bb.enableHandles()}function bh(a){_.setPressed([a[0],a[1]]),_.setCurrent([a[2],
+a[3]]),bb.update()}function bi(){return u(_.getFixed())}function bj(){return _.getFixed()}function bk(a){n(a),br()}function bl(){d.disabled=!0,bb.disableHandles(),bb.setCursor("default"),bc.setCursor("default")}function bm(){d.disabled=!1,br()}function bn(){bb.done(),bc.activateHandlers(null,null)}function bo(){G.remove(),A.show(),A.css("visibility","visible"),a(b).removeData("Jcrop")}function bp(a,b){bb.release(),bl();var c=new Image;c.onload=function(){var e=c.width,f=c.height,g=d.boxWidth,h=d.boxHeight;D.width(e).height(f),D.attr("src",a),H.attr("src",a),t(D,g,h),E=D.width(),F=D.height(),H.width(E).height(F),M.width(E+L*2).height(F+L*2),G.width(E).height(F),ba.resize(E,F),bm(),typeof b=="function"&&b.call(bs)},c.src=a}function bq(a,b,c){var e=b||d.bgColor;d.bgFade&&k()&&d.fadeTime&&!c?a.animate({backgroundColor:e},{queue:!1,duration:d.fadeTime}):a.css("backgroundColor",e)}function br(a){d.allowResize?a?bb.enableOnly():bb.enableHandles():bb.disableHandles(),bc.setCursor(d.allowSelect?"crosshair":"default"),bb
+.setCursor(d.allowMove?"move":"default"),d.hasOwnProperty("trueSize")&&(T=d.trueSize[0]/E,U=d.trueSize[1]/F),d.hasOwnProperty("setSelect")&&(bg(d.setSelect),bb.done(),delete d.setSelect),ba.refresh(),d.bgColor!=N&&(bq(d.shade?ba.getShades():G,d.shade?d.shadeColor||d.bgColor:d.bgColor),N=d.bgColor),O!=d.bgOpacity&&(O=d.bgOpacity,d.shade?ba.refresh():bb.setBgOpacity(O)),P=d.maxSize[0]||0,Q=d.maxSize[1]||0,R=d.minSize[0]||0,S=d.minSize[1]||0,d.hasOwnProperty("outerImage")&&(D.attr("src",d.outerImage),delete d.outerImage),bb.refresh()}var d=a.extend({},a.Jcrop.defaults),e,f=navigator.userAgent.toLowerCase(),g=/msie/.test(f),h=/msie [1-6]\./.test(f);typeof b!="object"&&(b=a(b)[0]),typeof c!="object"&&(c={}),n(c);var z={border:"none",visibility:"visible",margin:0,padding:0,position:"absolute",top:0,left:0},A=a(b),B=!0;if(b.tagName=="IMG"){if(A[0].width!=0&&A[0].height!=0)A.width(A[0].width),A.height(A[0].height);else{var C=new Image;C.src=A[0].src,A.width(C.width),A.height(C.height)}var D=A.clone().removeAttr("id").
+css(z).show();D.width(A.width()),D.height(A.height()),A.after(D).hide()}else D=A.css(z).show(),B=!1,d.shade===null&&(d.shade=!0);t(D,d.boxWidth,d.boxHeight);var E=D.width(),F=D.height(),G=a("<div />").width(E).height(F).addClass(j("holder")).css({position:"relative",backgroundColor:d.bgColor}).insertAfter(A).append(D);d.addClass&&G.addClass(d.addClass);var H=a("<div />"),I=a("<div />").width("100%").height("100%").css({zIndex:310,position:"absolute",overflow:"hidden"}),J=a("<div />").width("100%").height("100%").css("zIndex",320),K=a("<div />").css({position:"absolute",zIndex:600}).dblclick(function(){var a=_.getFixed();d.onDblClick.call(bs,a)}).insertBefore(D).append(I,J);B&&(H=a("<img />").attr("src",D.attr("src")).css(z).width(E).height(F),I.append(H)),h&&K.css({overflowY:"hidden"});var L=d.boundary,M=y().width(E+L*2).height(F+L*2).css({position:"absolute",top:i(-L),left:i(-L),zIndex:290}).mousedown(w),N=d.bgColor,O=d.bgOpacity,P,Q,R,S,T,U,V=!0,W,X,Y;e=l(D);var Z=function(){function a(){var a={},b=["touchstart"
+,"touchmove","touchend"],c=document.createElement("div"),d;try{for(d=0;d<b.length;d++){var e=b[d];e="on"+e;var f=e in c;f||(c.setAttribute(e,"return;"),f=typeof c[e]=="function"),a[b[d]]=f}return a.touchstart&&a.touchend&&a.touchmove}catch(g){return!1}}function b(){return d.touchSupport===!0||d.touchSupport===!1?d.touchSupport:a()}return{createDragger:function(a){return function(b){return d.disabled?!1:a==="move"&&!d.allowMove?!1:(e=l(D),W=!0,o(a,m(Z.cfilter(b)),!0),b.stopPropagation(),b.preventDefault(),!1)}},newSelection:function(a){return w(Z.cfilter(a))},cfilter:function(a){return a.pageX=a.originalEvent.changedTouches[0].pageX,a.pageY=a.originalEvent.changedTouches[0].pageY,a},isSupported:a,support:b()}}(),_=function(){function h(d){d=n(d),c=a=d[0],e=b=d[1]}function i(a){a=n(a),f=a[0]-c,g=a[1]-e,c=a[0],e=a[1]}function j(){return[f,g]}function k(d){var f=d[0],g=d[1];0>a+f&&(f-=f+a),0>b+g&&(g-=g+b),F<e+g&&(g+=F-(e+g)),E<c+f&&(f+=E-(c+f)),a+=f,c+=f,b+=g,e+=g}function l(a){var b=m();switch(a){case"ne":return[
+b.x2,b.y];case"nw":return[b.x,b.y];case"se":return[b.x2,b.y2];case"sw":return[b.x,b.y2]}}function m(){if(!d.aspectRatio)return p();var f=d.aspectRatio,g=d.minSize[0]/T,h=d.maxSize[0]/T,i=d.maxSize[1]/U,j=c-a,k=e-b,l=Math.abs(j),m=Math.abs(k),n=l/m,r,s,t,u;return h===0&&(h=E*10),i===0&&(i=F*10),n<f?(s=e,t=m*f,r=j<0?a-t:t+a,r<0?(r=0,u=Math.abs((r-a)/f),s=k<0?b-u:u+b):r>E&&(r=E,u=Math.abs((r-a)/f),s=k<0?b-u:u+b)):(r=c,u=l/f,s=k<0?b-u:b+u,s<0?(s=0,t=Math.abs((s-b)*f),r=j<0?a-t:t+a):s>F&&(s=F,t=Math.abs(s-b)*f,r=j<0?a-t:t+a)),r>a?(r-a<g?r=a+g:r-a>h&&(r=a+h),s>b?s=b+(r-a)/f:s=b-(r-a)/f):r<a&&(a-r<g?r=a-g:a-r>h&&(r=a-h),s>b?s=b+(a-r)/f:s=b-(a-r)/f),r<0?(a-=r,r=0):r>E&&(a-=r-E,r=E),s<0?(b-=s,s=0):s>F&&(b-=s-F,s=F),q(o(a,b,r,s))}function n(a){return a[0]<0&&(a[0]=0),a[1]<0&&(a[1]=0),a[0]>E&&(a[0]=E),a[1]>F&&(a[1]=F),[Math.round(a[0]),Math.round(a[1])]}function o(a,b,c,d){var e=a,f=c,g=b,h=d;return c<a&&(e=c,f=a),d<b&&(g=d,h=b),[e,g,f,h]}function p(){var d=c-a,f=e-b,g;return P&&Math.abs(d)>P&&(c=d>0?a+P:a-P),Q&&Math.abs
+(f)>Q&&(e=f>0?b+Q:b-Q),S/U&&Math.abs(f)<S/U&&(e=f>0?b+S/U:b-S/U),R/T&&Math.abs(d)<R/T&&(c=d>0?a+R/T:a-R/T),a<0&&(c-=a,a-=a),b<0&&(e-=b,b-=b),c<0&&(a-=c,c-=c),e<0&&(b-=e,e-=e),c>E&&(g=c-E,a-=g,c-=g),e>F&&(g=e-F,b-=g,e-=g),a>E&&(g=a-F,e-=g,b-=g),b>F&&(g=b-F,e-=g,b-=g),q(o(a,b,c,e))}function q(a){return{x:a[0],y:a[1],x2:a[2],y2:a[3],w:a[2]-a[0],h:a[3]-a[1]}}var a=0,b=0,c=0,e=0,f,g;return{flipCoords:o,setPressed:h,setCurrent:i,getOffset:j,moveOffset:k,getCorner:l,getFixed:m}}(),ba=function(){function f(a,b){e.left.css({height:i(b)}),e.right.css({height:i(b)})}function g(){return h(_.getFixed())}function h(a){e.top.css({left:i(a.x),width:i(a.w),height:i(a.y)}),e.bottom.css({top:i(a.y2),left:i(a.x),width:i(a.w),height:i(F-a.y2)}),e.right.css({left:i(a.x2),width:i(E-a.x2)}),e.left.css({width:i(a.x)})}function j(){return a("<div />").css({position:"absolute",backgroundColor:d.shadeColor||d.bgColor}).appendTo(c)}function k(){b||(b=!0,c.insertBefore(D),g(),bb.setBgOpacity(1,0,1),H.hide(),l(d.shadeColor||d.bgColor,1),bb.
+isAwake()?n(d.bgOpacity,1):n(1,1))}function l(a,b){bq(p(),a,b)}function m(){b&&(c.remove(),H.show(),b=!1,bb.isAwake()?bb.setBgOpacity(d.bgOpacity,1,1):(bb.setBgOpacity(1,1,1),bb.disableHandles()),bq(G,0,1))}function n(a,e){b&&(d.bgFade&&!e?c.animate({opacity:1-a},{queue:!1,duration:d.fadeTime}):c.css({opacity:1-a}))}function o(){d.shade?k():m(),bb.isAwake()&&n(d.bgOpacity)}function p(){return c.children()}var b=!1,c=a("<div />").css({position:"absolute",zIndex:240,opacity:0}),e={top:j(),left:j().height(F),right:j().height(F),bottom:j()};return{update:g,updateRaw:h,getShades:p,setBgColor:l,enable:k,disable:m,resize:f,refresh:o,opacity:n}}(),bb=function(){function k(b){var c=a("<div />").css({position:"absolute",opacity:d.borderOpacity}).addClass(j(b));return I.append(c),c}function l(b,c){var d=a("<div />").mousedown(s(b)).css({cursor:b+"-resize",position:"absolute",zIndex:c}).addClass("ord-"+b);return Z.support&&d.bind("touchstart.jcrop",Z.createDragger(b)),J.append(d),d}function m(a){var b=d.handleSize,e=l(a,c++
+).css({opacity:d.handleOpacity}).addClass(j("handle"));return b&&e.width(b).height(b),e}function n(a){return l(a,c++).addClass("jcrop-dragbar")}function o(a){var b;for(b=0;b<a.length;b++)g[a[b]]=n(a[b])}function p(a){var b,c;for(c=0;c<a.length;c++){switch(a[c]){case"n":b="hline";break;case"s":b="hline bottom";break;case"e":b="vline right";break;case"w":b="vline"}e[a[c]]=k(b)}}function q(a){var b;for(b=0;b<a.length;b++)f[a[b]]=m(a[b])}function r(a,b){d.shade||H.css({top:i(-b),left:i(-a)}),K.css({top:i(b),left:i(a)})}function t(a,b){K.width(Math.round(a)).height(Math.round(b))}function v(){var a=_.getFixed();_.setPressed([a.x,a.y]),_.setCurrent([a.x2,a.y2]),w()}function w(a){if(b)return x(a)}function x(a){var c=_.getFixed();t(c.w,c.h),r(c.x,c.y),d.shade&&ba.updateRaw(c),b||A(),a?d.onSelect.call(bs,u(c)):d.onChange.call(bs,u(c))}function z(a,c,e){if(!b&&!c)return;d.bgFade&&!e?D.animate({opacity:a},{queue:!1,duration:d.fadeTime}):D.css("opacity",a)}function A(){K.show(),d.shade?ba.opacity(O):z(O,!0),b=!0}function B
+(){F(),K.hide(),d.shade?ba.opacity(1):z(1),b=!1,d.onRelease.call(bs)}function C(){h&&J.show()}function E(){h=!0;if(d.allowResize)return J.show(),!0}function F(){h=!1,J.hide()}function G(a){a?(X=!0,F()):(X=!1,E())}function L(){G(!1),v()}var b,c=370,e={},f={},g={},h=!1;d.dragEdges&&a.isArray(d.createDragbars)&&o(d.createDragbars),a.isArray(d.createHandles)&&q(d.createHandles),d.drawBorders&&a.isArray(d.createBorders)&&p(d.createBorders),a(document).bind("touchstart.jcrop-ios",function(b){a(b.currentTarget).hasClass("jcrop-tracker")&&b.stopPropagation()});var M=y().mousedown(s("move")).css({cursor:"move",position:"absolute",zIndex:360});return Z.support&&M.bind("touchstart.jcrop",Z.createDragger("move")),I.append(M),F(),{updateVisible:w,update:x,release:B,refresh:v,isAwake:function(){return b},setCursor:function(a){M.css("cursor",a)},enableHandles:E,enableOnly:function(){h=!0},showHandles:C,disableHandles:F,animMode:G,setBgOpacity:z,done:L}}(),bc=function(){function f(b){M.css({zIndex:450}),b?a(document).bind("touchmove.jcrop"
+,k).bind("touchend.jcrop",l):e&&a(document).bind("mousemove.jcrop",h).bind("mouseup.jcrop",i)}function g(){M.css({zIndex:290}),a(document).unbind(".jcrop")}function h(a){return b(m(a)),!1}function i(a){return a.preventDefault(),a.stopPropagation(),W&&(W=!1,c(m(a)),bb.isAwake()&&d.onSelect.call(bs,u(_.getFixed())),g(),b=function(){},c=function(){}),!1}function j(a,d,e){return W=!0,b=a,c=d,f(e),!1}function k(a){return b(m(Z.cfilter(a))),!1}function l(a){return i(Z.cfilter(a))}function n(a){M.css("cursor",a)}var b=function(){},c=function(){},e=d.trackDocument;return e||M.mousemove(h).mouseup(i).mouseout(i),D.before(M),{activateHandlers:j,setCursor:n}}(),bd=function(){function e(){d.keySupport&&(b.show(),b.focus())}function f(a){b.hide()}function g(a,b,c){d.allowMove&&(_.moveOffset([b,c]),bb.updateVisible(!0)),a.preventDefault(),a.stopPropagation()}function i(a){if(a.ctrlKey||a.metaKey)return!0;Y=a.shiftKey?!0:!1;var b=Y?10:1;switch(a.keyCode){case 37:g(a,-b,0);break;case 39:g(a,b,0);break;case 38:g(a,0,-b);break;
+case 40:g(a,0,b);break;case 27:d.allowSelect&&bb.release();break;case 9:return!0}return!1}var b=a('<input type="radio" />').css({position:"fixed",left:"-120px",width:"12px"}).addClass("jcrop-keymgr"),c=a("<div />").css({position:"absolute",overflow:"hidden"}).append(b);return d.keySupport&&(b.keydown(i).blur(f),h||!d.fixedSupport?(b.css({position:"absolute",left:"-20px"}),c.append(b).insertBefore(D)):b.insertBefore(D)),{watchKeys:e}}();Z.support&&M.bind("touchstart.jcrop",Z.newSelection),J.hide(),br(!0);var bs={setImage:bp,animateTo:bf,setSelect:bg,setOptions:bk,tellSelect:bi,tellScaled:bj,setClass:be,disable:bl,enable:bm,cancel:bn,release:bb.release,destroy:bo,focus:bd.watchKeys,getBounds:function(){return[E*T,F*U]},getWidgetSize:function(){return[E,F]},getScaleFactor:function(){return[T,U]},getOptions:function(){return d},ui:{holder:G,selection:K}};return g&&G.bind("selectstart",function(){return!1}),A.data("Jcrop",bs),bs},a.fn.Jcrop=function(b,c){var d;return this.each(function(){if(a(this).data("Jcrop")){if(
+b==="api")return a(this).data("Jcrop");a(this).data("Jcrop").setOptions(b)}else this.tagName=="IMG"?a.Jcrop.Loader(this,function(){a(this).css({display:"block",visibility:"hidden"}),d=a.Jcrop(this,b),a.isFunction(c)&&c.call(d)}):(a(this).css({display:"block",visibility:"hidden"}),d=a.Jcrop(this,b),a.isFunction(c)&&c.call(d))}),this},a.Jcrop.Loader=function(b,c,d){function g(){f.complete?(e.unbind(".jcloader"),a.isFunction(c)&&c.call(f)):window.setTimeout(g,50)}var e=a(b),f=e[0];e.bind("load.jcloader",g).bind("error.jcloader",function(b){e.unbind(".jcloader"),a.isFunction(d)&&d.call(f)}),f.complete&&a.isFunction(c)&&(e.unbind(".jcloader"),c.call(f))},a.Jcrop.defaults={allowSelect:!0,allowMove:!0,allowResize:!0,trackDocument:!0,baseClass:"jcrop",addClass:null,bgColor:"black",bgOpacity:.6,bgFade:!1,borderOpacity:.4,handleOpacity:.5,handleSize:null,aspectRatio:0,keySupport:!0,createHandles:["n","s","e","w","nw","ne","se","sw"],createDragbars:["n","s","e","w"],createBorders:["n","s","e","w"],drawBorders:!0,dragEdges
+:!0,fixedSupport:!0,touchSupport:null,shade:null,boxWidth:0,boxHeight:0,boundary:2,fadeTime:400,animationDelay:20,swingSpeed:3,minSelect:[0,0],maxSize:[0,0],minSize:[0,0],onChange:function(){},onSelect:function(){},onDblClick:function(){},onRelease:function(){}}})(jQuery);

+ 661 - 0
stmms-web/src/main/webapp/static/jcrop/js/jquery.color.js

@@ -0,0 +1,661 @@
+/*!
+ * jQuery Color Animations v2.0pre
+ * http://jquery.org/
+ *
+ * Copyright 2011 John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+
+(function( jQuery, undefined ){
+	var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color outlineColor".split(" "),
+
+		// plusequals test for += 100 -= 100
+		rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
+		// a set of RE's that can match strings and generate color tuples.
+		stringParsers = [{
+				re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
+				parse: function( execResult ) {
+					return [
+						execResult[ 1 ],
+						execResult[ 2 ],
+						execResult[ 3 ],
+						execResult[ 4 ]
+					];
+				}
+			}, {
+				re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
+				parse: function( execResult ) {
+					return [
+						2.55 * execResult[1],
+						2.55 * execResult[2],
+						2.55 * execResult[3],
+						execResult[ 4 ]
+					];
+				}
+			}, {
+				re: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,
+				parse: function( execResult ) {
+					return [
+						parseInt( execResult[ 1 ], 16 ),
+						parseInt( execResult[ 2 ], 16 ),
+						parseInt( execResult[ 3 ], 16 )
+					];
+				}
+			}, {
+				re: /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,
+				parse: function( execResult ) {
+					return [
+						parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
+						parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
+						parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
+					];
+				}
+			}, {
+				re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
+				space: "hsla",
+				parse: function( execResult ) {
+					return [
+						execResult[1],
+						execResult[2] / 100,
+						execResult[3] / 100,
+						execResult[4]
+					];
+				}
+			}],
+
+		// jQuery.Color( )
+		color = jQuery.Color = function( color, green, blue, alpha ) {
+			return new jQuery.Color.fn.parse( color, green, blue, alpha );
+		},
+		spaces = {
+			rgba: {
+				cache: "_rgba",
+				props: {
+					red: {
+						idx: 0,
+						type: "byte",
+						empty: true
+					},
+					green: {
+						idx: 1,
+						type: "byte",
+						empty: true
+					},
+					blue: {
+						idx: 2,
+						type: "byte",
+						empty: true
+					},
+					alpha: {
+						idx: 3,
+						type: "percent",
+						def: 1
+					}
+				}
+			},
+			hsla: {
+				cache: "_hsla",
+				props: {
+					hue: {
+						idx: 0,
+						type: "degrees",
+						empty: true
+					},
+					saturation: {
+						idx: 1,
+						type: "percent",
+						empty: true
+					},
+					lightness: {
+						idx: 2,
+						type: "percent",
+						empty: true
+					}
+				}
+			}
+		},
+		propTypes = {
+			"byte": {
+				floor: true,
+				min: 0,
+				max: 255
+			},
+			"percent": {
+				min: 0,
+				max: 1
+			},
+			"degrees": {
+				mod: 360,
+				floor: true
+			}
+		},
+		rgbaspace = spaces.rgba.props,
+		support = color.support = {},
+
+		// colors = jQuery.Color.names
+		colors,
+
+		// local aliases of functions called often
+		each = jQuery.each;
+
+	spaces.hsla.props.alpha = rgbaspace.alpha;
+
+	function clamp( value, prop, alwaysAllowEmpty ) {
+		var type = propTypes[ prop.type ] || {},
+			allowEmpty = prop.empty || alwaysAllowEmpty;
+
+		if ( allowEmpty && value == null ) {
+			return null;
+		}
+		if ( prop.def && value == null ) {
+			return prop.def;
+		}
+		if ( type.floor ) {
+			value = ~~value;
+		} else {
+			value = parseFloat( value );
+		}
+		if ( value == null || isNaN( value ) ) {
+			return prop.def;
+		}
+		if ( type.mod ) {
+			value = value % type.mod;
+			// -10 -> 350
+			return value < 0 ? type.mod + value : value;
+		}
+
+		// for now all property types without mod have min and max
+		return type.min > value ? type.min : type.max < value ? type.max : value;
+	}
+
+	function stringParse( string ) {
+		var inst = color(),
+			rgba = inst._rgba = [];
+
+		string = string.toLowerCase();
+
+		each( stringParsers, function( i, parser ) {
+			var match = parser.re.exec( string ),
+				values = match && parser.parse( match ),
+				parsed,
+				spaceName = parser.space || "rgba",
+				cache = spaces[ spaceName ].cache;
+
+
+			if ( values ) {
+				parsed = inst[ spaceName ]( values );
+
+				// if this was an rgba parse the assignment might happen twice
+				// oh well....
+				inst[ cache ] = parsed[ cache ];
+				rgba = inst._rgba = parsed._rgba;
+
+				// exit each( stringParsers ) here because we matched
+				return false;
+			}
+		});
+
+		// Found a stringParser that handled it
+		if ( rgba.length !== 0 ) {
+
+			// if this came from a parsed string, force "transparent" when alpha is 0
+			// chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
+			if ( Math.max.apply( Math, rgba ) === 0 ) {
+				jQuery.extend( rgba, colors.transparent );
+			}
+			return inst;
+		}
+
+		// named colors / default - filter back through parse function
+		if ( string = colors[ string ] ) {
+			return string;
+		}
+	}
+
+	color.fn = color.prototype = {
+		constructor: color,
+		parse: function( red, green, blue, alpha ) {
+			if ( red === undefined ) {
+				this._rgba = [ null, null, null, null ];
+				return this;
+			}
+			if ( red instanceof jQuery || red.nodeType ) {
+				red = red instanceof jQuery ? red.css( green ) : jQuery( red ).css( green );
+				green = undefined;
+			}
+
+			var inst = this,
+				type = jQuery.type( red ),
+				rgba = this._rgba = [],
+				source;
+
+			// more than 1 argument specified - assume ( red, green, blue, alpha )
+			if ( green !== undefined ) {
+				red = [ red, green, blue, alpha ];
+				type = "array";
+			}
+
+			if ( type === "string" ) {
+				return this.parse( stringParse( red ) || colors._default );
+			}
+
+			if ( type === "array" ) {
+				each( rgbaspace, function( key, prop ) {
+					rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
+				});
+				return this;
+			}
+
+			if ( type === "object" ) {
+				if ( red instanceof color ) {
+					each( spaces, function( spaceName, space ) {
+						if ( red[ space.cache ] ) {
+							inst[ space.cache ] = red[ space.cache ].slice();
+						}
+					});
+				} else {
+					each( spaces, function( spaceName, space ) {
+						each( space.props, function( key, prop ) {
+							var cache = space.cache;
+
+							// if the cache doesn't exist, and we know how to convert
+							if ( !inst[ cache ] && space.to ) {
+
+								// if the value was null, we don't need to copy it
+								// if the key was alpha, we don't need to copy it either
+								if ( red[ key ] == null || key === "alpha") {
+									return;
+								}
+								inst[ cache ] = space.to( inst._rgba );
+							}
+
+							// this is the only case where we allow nulls for ALL properties.
+							// call clamp with alwaysAllowEmpty
+							inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
+						});
+					});
+				}
+				return this;
+			}
+		},
+		is: function( compare ) {
+			var is = color( compare ),
+				same = true,
+				myself = this;
+
+			each( spaces, function( _, space ) {
+				var isCache = is[ space.cache ],
+					localCache;
+				if (isCache) {
+					localCache = myself[ space.cache ] || space.to && space.to( myself._rgba ) || [];
+					each( space.props, function( _, prop ) {
+						if ( isCache[ prop.idx ] != null ) {
+							same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
+							return same;
+						}
+					});
+				}
+				return same;
+			});
+			return same;
+		},
+		_space: function() {
+			var used = [],
+				inst = this;
+			each( spaces, function( spaceName, space ) {
+				if ( inst[ space.cache ] ) {
+					used.push( spaceName );
+				}
+			});
+			return used.pop();
+		},
+		transition: function( other, distance ) {
+			var end = color( other ),
+				spaceName = end._space(),
+				space = spaces[ spaceName ],
+				start = this[ space.cache ] || space.to( this._rgba ),
+				result = start.slice();
+
+			end = end[ space.cache ];
+			each( space.props, function( key, prop ) {
+				var index = prop.idx,
+					startValue = start[ index ],
+					endValue = end[ index ],
+					type = propTypes[ prop.type ] || {};
+
+				// if null, don't override start value
+				if ( endValue === null ) {
+					return;
+				}
+				// if null - use end
+				if ( startValue === null ) {
+					result[ index ] = endValue;
+				} else {
+					if ( type.mod ) {
+						if ( endValue - startValue > type.mod / 2 ) {
+							startValue += type.mod;
+						} else if ( startValue - endValue > type.mod / 2 ) {
+							startValue -= type.mod;
+						}
+					}
+					result[ prop.idx ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
+				}
+			});
+			return this[ spaceName ]( result );
+		},
+		blend: function( opaque ) {
+			// if we are already opaque - return ourself
+			if ( this._rgba[ 3 ] === 1 ) {
+				return this;
+			}
+
+			var rgb = this._rgba.slice(),
+				a = rgb.pop(),
+				blend = color( opaque )._rgba;
+
+			return color( jQuery.map( rgb, function( v, i ) {
+				return ( 1 - a ) * blend[ i ] + a * v;
+			}));
+		},
+		toRgbaString: function() {
+			var prefix = "rgba(",
+				rgba = jQuery.map( this._rgba, function( v, i ) {
+					return v == null ? ( i > 2 ? 1 : 0 ) : v;
+				});
+
+			if ( rgba[ 3 ] === 1 ) {
+				rgba.pop();
+				prefix = "rgb(";
+			}
+
+			return prefix + rgba.join(",") + ")";
+		},
+		toHslaString: function() {
+			var prefix = "hsla(",
+				hsla = jQuery.map( this.hsla(), function( v, i ) {
+					if ( v == null ) {
+						v = i > 2 ? 1 : 0;
+					}
+
+					// catch 1 and 2
+					if ( i && i < 3 ) {
+						v = Math.round( v * 100 ) + "%";
+					}
+					return v;
+				});
+
+			if ( hsla[ 3 ] === 1 ) {
+				hsla.pop();
+				prefix = "hsl(";
+			}
+			return prefix + hsla.join(",") + ")";
+		},
+		toHexString: function( includeAlpha ) {
+			var rgba = this._rgba.slice(),
+				alpha = rgba.pop();
+
+			if ( includeAlpha ) {
+				rgba.push( ~~( alpha * 255 ) );
+			}
+
+			return "#" + jQuery.map( rgba, function( v, i ) {
+
+				// default to 0 when nulls exist
+				v = ( v || 0 ).toString( 16 );
+				return v.length === 1 ? "0" + v : v;
+			}).join("");
+		},
+		toString: function() {
+			return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
+		}
+	};
+	color.fn.parse.prototype = color.fn;
+
+	// hsla conversions adapted from:
+	// http://www.google.com/codesearch/p#OAMlx_jo-ck/src/third_party/WebKit/Source/WebCore/inspector/front-end/Color.js&d=7&l=193
+
+	function hue2rgb( p, q, h ) {
+		h = ( h + 1 ) % 1;
+		if ( h * 6 < 1 ) {
+			return p + (q - p) * 6 * h;
+		}
+		if ( h * 2 < 1) {
+			return q;
+		}
+		if ( h * 3 < 2 ) {
+			return p + (q - p) * ((2/3) - h) * 6;
+		}
+		return p;
+	}
+
+	spaces.hsla.to = function ( rgba ) {
+		if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
+			return [ null, null, null, rgba[ 3 ] ];
+		}
+		var r = rgba[ 0 ] / 255,
+			g = rgba[ 1 ] / 255,
+			b = rgba[ 2 ] / 255,
+			a = rgba[ 3 ],
+			max = Math.max( r, g, b ),
+			min = Math.min( r, g, b ),
+			diff = max - min,
+			add = max + min,
+			l = add * 0.5,
+			h, s;
+
+		if ( min === max ) {
+			h = 0;
+		} else if ( r === max ) {
+			h = ( 60 * ( g - b ) / diff ) + 360;
+		} else if ( g === max ) {
+			h = ( 60 * ( b - r ) / diff ) + 120;
+		} else {
+			h = ( 60 * ( r - g ) / diff ) + 240;
+		}
+
+		if ( l === 0 || l === 1 ) {
+			s = l;
+		} else if ( l <= 0.5 ) {
+			s = diff / add;
+		} else {
+			s = diff / ( 2 - add );
+		}
+		return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
+	};
+
+	spaces.hsla.from = function ( hsla ) {
+		if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
+			return [ null, null, null, hsla[ 3 ] ];
+		}
+		var h = hsla[ 0 ] / 360,
+			s = hsla[ 1 ],
+			l = hsla[ 2 ],
+			a = hsla[ 3 ],
+			q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
+			p = 2 * l - q,
+			r, g, b;
+
+		return [
+			Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
+			Math.round( hue2rgb( p, q, h ) * 255 ),
+			Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
+			a
+		];
+	};
+
+
+	each( spaces, function( spaceName, space ) {
+		var props = space.props,
+			cache = space.cache,
+			to = space.to,
+			from = space.from;
+
+		// makes rgba() and hsla()
+		color.fn[ spaceName ] = function( value ) {
+
+			// generate a cache for this space if it doesn't exist
+			if ( to && !this[ cache ] ) {
+				this[ cache ] = to( this._rgba );
+			}
+			if ( value === undefined ) {
+				return this[ cache ].slice();
+			}
+
+			var type = jQuery.type( value ),
+				arr = ( type === "array" || type === "object" ) ? value : arguments,
+				local = this[ cache ].slice(),
+				ret;
+
+			each( props, function( key, prop ) {
+				var val = arr[ type === "object" ? key : prop.idx ];
+				if ( val == null ) {
+					val = local[ prop.idx ];
+				}
+				local[ prop.idx ] = clamp( val, prop );
+			});
+
+			if ( from ) {
+				ret = color( from( local ) );
+				ret[ cache ] = local;
+				return ret;
+			} else {
+				return color( local );
+			}
+		};
+
+		// makes red() green() blue() alpha() hue() saturation() lightness()
+		each( props, function( key, prop ) {
+			// alpha is included in more than one space
+			if ( color.fn[ key ] ) {
+				return;
+			}
+			color.fn[ key ] = function( value ) {
+				var vtype = jQuery.type( value ),
+					fn = ( key === 'alpha' ? ( this._hsla ? 'hsla' : 'rgba' ) : spaceName ),
+					local = this[ fn ](),
+					cur = local[ prop.idx ],
+					match;
+
+				if ( vtype === "undefined" ) {
+					return cur;
+				}
+
+				if ( vtype === "function" ) {
+					value = value.call( this, cur );
+					vtype = jQuery.type( value );
+				}
+				if ( value == null && prop.empty ) {
+					return this;
+				}
+				if ( vtype === "string" ) {
+					match = rplusequals.exec( value );
+					if ( match ) {
+						value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
+					}
+				}
+				local[ prop.idx ] = value;
+				return this[ fn ]( local );
+			};
+		});
+	});
+
+	// add .fx.step functions
+	each( stepHooks, function( i, hook ) {
+		jQuery.cssHooks[ hook ] = {
+			set: function( elem, value ) {
+				var parsed, backgroundColor, curElem;
+
+				if ( jQuery.type( value ) !== 'string' || ( parsed = stringParse( value ) ) )
+				{
+					value = color( parsed || value );
+					if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
+						curElem = hook === "backgroundColor" ? elem.parentNode : elem;
+						do {
+							backgroundColor = jQuery.curCSS( curElem, "backgroundColor" );
+						} while (
+							( backgroundColor === "" || backgroundColor === "transparent" ) &&
+							( curElem = curElem.parentNode ) &&
+							curElem.style
+						);
+
+						value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
+							backgroundColor :
+							"_default" );
+					}
+
+					value = value.toRgbaString();
+				}
+				elem.style[ hook ] = value;
+			}
+		};
+		jQuery.fx.step[ hook ] = function( fx ) {
+			if ( !fx.colorInit ) {
+				fx.start = color( fx.elem, hook );
+				fx.end = color( fx.end );
+				fx.colorInit = true;
+			}
+			jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
+		};
+	});
+
+	// detect rgba support
+	jQuery(function() {
+		var div = document.createElement( "div" ),
+			div_style = div.style;
+
+		div_style.cssText = "background-color:rgba(1,1,1,.5)";
+		support.rgba = div_style.backgroundColor.indexOf( "rgba" ) > -1;
+	});
+
+	// Some named colors to work with
+	// From Interface by Stefan Petre
+	// http://interface.eyecon.ro/
+	colors = jQuery.Color.names = {
+		aqua: "#00ffff",
+		azure: "#f0ffff",
+		beige: "#f5f5dc",
+		black: "#000000",
+		blue: "#0000ff",
+		brown: "#a52a2a",
+		cyan: "#00ffff",
+		darkblue: "#00008b",
+		darkcyan: "#008b8b",
+		darkgrey: "#a9a9a9",
+		darkgreen: "#006400",
+		darkkhaki: "#bdb76b",
+		darkmagenta: "#8b008b",
+		darkolivegreen: "#556b2f",
+		darkorange: "#ff8c00",
+		darkorchid: "#9932cc",
+		darkred: "#8b0000",
+		darksalmon: "#e9967a",
+		darkviolet: "#9400d3",
+		fuchsia: "#ff00ff",
+		gold: "#ffd700",
+		green: "#008000",
+		indigo: "#4b0082",
+		khaki: "#f0e68c",
+		lightblue: "#add8e6",
+		lightcyan: "#e0ffff",
+		lightgreen: "#90ee90",
+		lightgrey: "#d3d3d3",
+		lightpink: "#ffb6c1",
+		lightyellow: "#ffffe0",
+		lime: "#00ff00",
+		magenta: "#ff00ff",
+		maroon: "#800000",
+		navy: "#000080",
+		olive: "#808000",
+		orange: "#ffa500",
+		pink: "#ffc0cb",
+		purple: "#800080",
+		violet: "#800080",
+		red: "#ff0000",
+		silver: "#c0c0c0",
+		white: "#ffffff",
+		yellow: "#ffff00",
+		transparent: [ null, null, null, 0 ],
+		_default: "#ffffff"
+	};
+})( jQuery );

+ 103 - 86
stmms-web/src/main/webapp/static/mark-new/js/modules/image-builder.js

@@ -1,87 +1,104 @@
-//简单多张图片排列显示模块
-var image_builder = function(option, success) {
-    var object = new ImageBuilder(option);
-    success();
-    return object;
-}
-
-function ImageBuilder(option) {
-    this.markControl = option.markControl;
-    this.init();
-}
-
-ImageBuilder.prototype.init = function() {
-    this.container = getDom(this.canvas_dom, this.markControl).appendTo(this.markControl.container);
-    this.container.hide();
-    this.canvas = document.getElementById('image-builder-canvas');
-    this.ctx = this.canvas.getContext("2d");
-}
-
-ImageBuilder.prototype.build = function(task, callback) {
-    var self = this;
-    //调用图片预加载函数,实现回调函数
-    var imageObjects = [];
-    this.loadImages(imageObjects, task.pictureUrls, 0, function(images) {
-        var maxWidth = 0;
-        var totalHeight = 0;
-        for (var i = 0; i < images.length; i++) {
-            //计算最大宽度与合计高度
-            var image = images[i];
-            maxWidth = Math.max(maxWidth, image.width);
-            totalHeight += image.height;
-        }
-        if (maxWidth > 0 && totalHeight > 0) {
-            //设置画布大小及背景颜色
-            self.canvas.width = maxWidth;
-            self.canvas.height = totalHeight;
-            self.ctx.fillStyle = "#FFFFFF";
-            self.ctx.fillRect(0, 0, maxWidth, totalHeight);
-            //绘画到画布
-            var height = 0;
-            for (var i = 0; i < images.length; i++) {
-                var image = images[i];
-                self.ctx.drawImage(image, 0, 0, image.width, image.height, 0, height, image.width, image.height);
-                height += image.height;
-            }
-            //生成合并后的图像数据
-            task.imageData = this.canvas.toDataURL("image/jpeg");
-            self.ctx.clearRect(0, 0, self.canvas.width, self.canvas.height);
-        }
-        callback();
-    }, function(error) {
-        if (error) {
-            console.log(error);
-        }
-        callback('image load error');
-    });
-}
-
-/*
-实现一系列图片的预加载
-参数urls:图片实际URL数组
-参数number:array数组遍历下标
-参数callback:回调函数——图片预加载完成后立即执行此函数
-*/
-ImageBuilder.prototype.loadImages = function(images, urls, number, callback, exception) {
-    var self = this;
-    var imageServer = this.markControl.context.imageServer;
-    var defaultServer = this.markControl.option.imageServer;
-    if (urls != undefined && number < urls.length) {
-        var img = new Image();
-        img.onload = function() {
-            images.push(img);
-            self.loadImages(images, urls, number + 1, callback, exception);
-        }
-        img.onerror = function() {
-            img.onerror = undefined;
-            //self.loadImages(images, urls, number + 1, callback);
-            exception('load error for: ' + img.src);
-        }
-        img.crossOrigin = '';
-        img.src = imageServer + urls[number] + '?' + new Date().getTime();
-    } else {
-        callback.call(this, images);
-    }
-}
-
+//简单多张图片排列显示模块
+var image_builder = function(option, success) {
+    var object = new ImageBuilder(option);
+    success();
+    return object;
+}
+
+function ImageBuilder(option) {
+    this.markControl = option.markControl;
+    this.init();
+}
+
+ImageBuilder.prototype.init = function() {
+    this.container = getDom(this.canvas_dom, this.markControl).appendTo(this.markControl.container);
+    this.container.hide();
+    this.canvas = document.getElementById('image-builder-canvas');
+    this.ctx = this.canvas.getContext("2d");
+}
+
+ImageBuilder.prototype.build = function(task, callback) {
+    var self = this;
+    //初始化图片拼接配置,过滤实际需要加载的图片
+    var config = JSON.parse(task.pictureConfig);
+    var indexSet = {};
+    for(var i=0;i<config.length;i++){
+    	indexSet[config[i].i-1] = true;
+    }
+    //调用图片预加载函数,实现回调函数
+    var imageObjects = [];
+    this.loadImages(imageObjects, indexSet, task.pictureUrls, 0, function(images) {
+        var maxWidth = 0;
+        var totalHeight = 0;
+        for (var i = 0; i < config.length; i++) {
+            //计算最大宽度与合计高度
+            if(config[i].w<=0){
+            	config[i].w=images[config[i].i-1].width;
+            }
+            if(config[i].h<=0){
+            	config[i].h=images[config[i].i-1].height;
+            }
+            maxWidth = Math.max(maxWidth, config[i].w);
+            totalHeight += config[i].h;
+        }
+        if (maxWidth > 0 && totalHeight > 0) {
+            //设置画布大小及背景颜色
+            self.canvas.width = maxWidth;
+            self.canvas.height = totalHeight;
+            self.ctx.fillStyle = "#FFFFFF";
+            self.ctx.fillRect(0, 0, maxWidth, totalHeight);
+            //绘画到画布
+            var height = 0;
+            for (var i = 0; i < config.length; i++) {
+                var image = images[config[i].i-1];
+                self.ctx.drawImage(image, config[i].x, config[i].y, config[i].w, config[i].h, 0, height, config[i].w, config[i].h);
+                height += config[i].h;
+            }
+            //生成合并后的图像数据
+            task.imageData = this.canvas.toDataURL("image/jpeg");
+            self.ctx.clearRect(0, 0, self.canvas.width, self.canvas.height);
+        }
+        callback();
+    }, function(error) {
+        if (error) {
+            console.log(error);
+        }
+        callback('image load error');
+    });
+}
+
+/*
+实现一系列图片的预加载
+参数indexSet:实际需要的图片序号集合
+参数urls:图片URL数组
+参数number:array数组遍历下标
+参数callback:回调函数——所有图片加载完成后才执行此函数
+参数exception:回调函数——任一图片加载失败后立即执行此函数
+*/
+ImageBuilder.prototype.loadImages = function(images, indexSet, urls, number, callback, exception) {
+    var self = this;
+    var imageServer = this.markControl.context.imageServer;
+    var defaultServer = this.markControl.option.imageServer;
+    if (urls != undefined && number < urls.length) {
+    	if(indexSet[number]==true){
+    		var img = new Image();
+        	img.onload = function() {
+            	images.push(img);
+            	self.loadImages(images, indexSet, urls, number + 1, callback, exception);
+        	}
+        	img.onerror = function() {
+            	img.onerror = undefined;
+            	exception('load error for: ' + img.src);
+        	}
+        	img.crossOrigin = '';
+        	img.src = imageServer + urls[number] + '?' + new Date().getTime();
+    	}else{
+    		images.push({});
+    		self.loadImages(images, indexSet, urls, number + 1, callback, exception);
+    	}
+    } else {
+        callback.call(this, images);
+    }
+}
+
 ImageBuilder.prototype.canvas_dom = '<div><canvas id="image-builder-canvas"></canvas></div>';

+ 323 - 0
stmms-web/src/main/webapp/static/pic-config/css/style.css

@@ -0,0 +1,323 @@
+/*======common======*/
+* {
+	font-family: "微软雅黑";
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	outline: none;
+	-webkit-tap-highlight-color: transparent;
+	-webkit-box-sizing: border-box;
+	box-sizing: border-box;
+}
+html, body {
+	height: 100%;
+	background: #F2F3F4;
+	color: #011627;
+}
+a {
+	color: #FFF;
+	text-decoration: none;
+}
+input {
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+    margin: 0;
+    padding: 0;
+    list-style: none;
+    position: relative;
+    display: inline-block;
+    color: rgba(0, 0, 0, 0.65);
+    background-color: #F5F7F8;
+    background-image: none;
+    border: 1px solid #E0E2E4;
+	-moz-border-radius: 6px;
+	-webkit-border-radius: 6px;
+    border-radius: 6px;
+    -webkit-transition: all .3s;
+    transition: all .3s;
+	text-align:center;
+}
+input[type="text"] {
+	background-color: #F5F7F8;
+    border: 1px solid #E0E2E4;
+    -webkit-transition: border linear .2s, box-shadow linear .2s;
+    -moz-transition: border linear .2s, box-shadow linear .2s;
+    -o-transition: border linear .2s, box-shadow linear .2s;
+    transition: border linear .2s, box-shadow linear .2s;
+}
+input[type="text"]:focus {
+	background: #FFF;
+	border-color: rgba(69,152,255,0.8);
+	outline: 0;
+	-webkit-box-shadow: 0 0 8px rgba(125,168,237,.6);
+	-moz-box-shadow: 0 0 8px rgba(125,168,237,.6);
+	box-shadow: 0 0 8px rgba(125,168,237,.6);
+}
+.ellipsis {
+	text-overflow: ellipsis;
+	overflow: hidden;
+	white-space: nowrap;
+}
+.cl:after {
+	content: ".";
+	display: block;
+	height: 0;
+	clear: both;
+	visibility: hidden;
+}
+.cl {
+	zoom: 1;
+}
+/*c-box*/
+.c-box {
+	position: absolute;
+	left: 0;
+	top: 0;
+	width: 100%;
+	height: 100%;
+	background: #FFF;
+    display: -webkit-box;
+    display: -webkit-flex;
+    display: -ms-flexbox;
+    display: flex;
+}
+.c-box .cont-z {
+	position: relative;
+	width: 70%;
+	height: 100%;
+	background: #FFF;
+	overflow: hidden;
+}
+.c-box .cont-y {
+	position: relative;
+	width: 30%;
+	height: 100%;
+	background: #EEE;
+	-moz-box-shadow: 0 0 30px rgba(0,0,0,.3);
+	-webkit-box-shadow: 0 0 30px rgba(0,0,0,.3);
+	box-shadow: 0 0 30px rgba(0,0,0,.3);
+}
+.c-box .c-bottom {
+	position: absolute;
+	bottom: 0;
+	left: 0;
+	width: 100%;
+	height: 70px;
+	overflow: hidden;
+	background: #FFF;
+	-moz-box-shadow: 0 -5px 10px rgba(0,0,0,.05);
+	-webkit-box-shadow: 0 -5px 10px rgba(0,0,0,.05);
+	box-shadow: 0 -5px 10px rgba(0,0,0,.05);
+	padding: 15px 20px;
+}
+.c-box .c-mn {
+	width: 100%;
+	height: -moz-calc(100% - 70px);
+	height: -webkit-calc(100% - 70px);
+	height: calc(100% - 70px);
+	overflow: hidden;
+}
+/*imgcont*/
+.imgcont {
+	width: 100%;
+	height: 100%;
+	overflow: scroll;
+}
+.imgcont::-webkit-scrollbar {
+	width: 10px;
+	height: 10px;
+}
+.imgcont::-webkit-scrollbar-thumb {
+	background: #C1C1C1;
+}
+.imgcont::-webkit-scrollbar-track {
+	border-radius: 0;
+	background: transparent;
+}
+/*c-top*/
+.c-top {
+	width: 100%;
+	height: 70px;
+	background: #EEE;
+	padding: 18px 200px 18px 30px;
+}
+/*c-icon*/
+.c-icon {
+	position: absolute;
+	top: 15px;
+	right: 30px;
+	height: 34px;
+	overflow: hidden;
+}
+.c-icon div {
+	display: inline-block;
+	width: 34px; 
+	height: 34px;
+	overflow: hidden;
+	background: url(../img/zoom_in.png) no-repeat 0 0;
+	border: 2px solid rgba(108,128,147,.85);
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+	border-radius: 10px;
+	cursor: pointer;
+	margin-left: 5px;
+}
+.c-icon div.iviewer_zoom_out {
+	background: url(../img/zoom_out.png) no-repeat 0 0;
+}
+.c-icon div.iviewer_zoom_zero {
+	background: url(../img/zoom_zero.png) no-repeat 0 0;
+}
+.c-icon div.iviewer_zoom_fit {
+	background: url(../img/zoom_fit.png) no-repeat 0 0;
+}
+.c-icon div:hover {
+	background-color: rgba(108,128,147,.85);
+	background-position: 0 -30px;
+}
+/*c-tab*/
+.c-tab {
+	height: 34px;
+	overflow: hidden;
+}
+.c-tab li {
+	display: inline;
+}
+.c-tab li a {
+	display: inline-block;
+	width: 34px;
+	height: 34px;
+	line-height: 32px;
+	overflow: hidden;
+	border: 2px solid rgba(108,128,147,.85);
+	text-align: center;
+	font-family: "Times New Roman", Times, serif;
+	font-size: 14px;
+	font-weight: bold;
+	color: #5D6D7D;
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+	border-radius: 10px;
+}
+.c-tab li.on a , .c-tab li a:hover {
+	background-color: rgba(108,128,147,.85);
+	color: #FFF;
+}
+/*c-btn*/
+.c-btn {
+	float: left;
+	width: calc((100% - 30px) / 3 );
+	height: 40px;
+	line-height: 36px;
+	overflow: hidden;
+	text-align: center;
+	font-size: 15px;
+	font-weight: 700;
+	color: #8796A5;
+	border: 2px solid #E0E2E4;
+	-moz-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	border-radius: 6px;
+}
+.c-btn:hover {
+	border-color: #6C8093;
+}
+.c-btn.s {
+	border-color: #5D6D7D;
+	background: #5D6D7D;
+	color: #FFF;
+	margin: 0 15px;
+}
+.c-btn.s:hover {
+	background: #6C8093;
+}
+.c-btn.close {
+	border-color: rgba(255,51,51,1);
+	background: rgba(255,51,51,1);
+	color: #FFF;
+}
+.c-btn.close:hover {
+	background: rgba(255,51,51,.7);
+}
+
+/*c-preview*/
+.c-preview {
+	width: 100%;
+	height: 100%;
+	padding: 10px 20px;
+	overflow-y: scroll;
+}
+.c-preview::-webkit-scrollbar {
+	width: 10px;
+	height: 10px;
+}
+.c-preview::-webkit-scrollbar-thumb {
+	background: #C1C1C1;
+}
+.c-preview::-webkit-scrollbar-track {
+	border-radius: 0;
+	background: transparent;
+}
+.c-preview li {
+	position: relative;
+	width: 100%;
+	overflow: hidden;
+	background: #FFF;
+	-moz-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	border-radius: 6px;
+	padding: 10px;
+	margin-top: 10px;
+}
+.c-preview li:nth-child(1) {
+	margin-top: 0;
+}
+.c-preview li div , .c-preview li div img {
+	width: 100%;
+}
+.c-preview li span {
+	position: absolute;
+	display: block;
+	width: 30px;
+	height: 30px;
+	overflow: hidden;
+}
+.c-preview li span.order {
+	left: 10px;
+	top: 10px;
+	width: 24px;
+	height: 24px;
+	line-height: 24px;
+	border: 1px solid #5D6D7D;
+	-moz-border-radius: 20px;
+	-webkit-border-radius: 20px;
+	border-radius: 20px;
+	text-align: center;
+	font-family: "Times New Roman", Times, serif;
+	font-size: 12px;
+	font-weight: bold;
+	color: #5D6D7D;
+}
+.c-preview li span.close {
+	right: 0;
+	top: 0;
+	background: rgba(0,0,0,.3);
+	line-height: 30px;
+	text-align: center;
+	font-size: 14px;
+	font-weight: bold;
+	color: #FFF;
+}
+.c-preview li:hover {
+	-moz-box-shadow: 0 0 20px rgba(0,0,0,.1);
+	-webkit-box-shadow: 0 0 20px rgba(0,0,0,.1);
+	box-shadow: 0 0 20px rgba(0,0,0,.1);
+}
+.c-preview li:hover span.order {
+	background-color: rgba(108,128,147,.85);
+	color: #FFF;
+}
+.c-preview li:hover span.close {
+	background:rgba(255,51,51,.7);
+	
+}

二进制
stmms-web/src/main/webapp/static/pic-config/img/00002_F.jpg


二进制
stmms-web/src/main/webapp/static/pic-config/img/dt.jpg


二进制
stmms-web/src/main/webapp/static/pic-config/img/preview.jpg


二进制
stmms-web/src/main/webapp/static/pic-config/img/preview1.jpg


二进制
stmms-web/src/main/webapp/static/pic-config/img/radio.png


二进制
stmms-web/src/main/webapp/static/pic-config/img/up.png


二进制
stmms-web/src/main/webapp/static/pic-config/img/zoom_fit.png


二进制
stmms-web/src/main/webapp/static/pic-config/img/zoom_in.png


二进制
stmms-web/src/main/webapp/static/pic-config/img/zoom_out.png


二进制
stmms-web/src/main/webapp/static/pic-config/img/zoom_zero.png


+ 50 - 0
stmms-web/src/main/webapp/static/pic-config/index.html

@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>云阅卷</title>
+<meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/>
+<link rel="stylesheet" href="css/style.css">
+</head>
+<body>
+<div class="c-box">
+  <div class="cont-z">
+    <div class="c-top">
+      <div class="c-icon">
+        <div class="iviewer_zoom_in iviewer_common iviewer_button"></div>
+        <div class="iviewer_zoom_out iviewer_common iviewer_button"></div>
+        <div class="iviewer_zoom_zero iviewer_common iviewer_button"></div>
+        <div class="iviewer_zoom_fit iviewer_common iviewer_button"></div>
+      </div>
+      <ul class="c-tab cl">
+        <li class="on"><a href="">1</a></li>
+        <li><a href="">2</a></li>
+        <li><a href="">3</a></li>
+        <li><a href="">4</a></li>
+      </ul>
+    </div>
+    <div class="c-mn">
+    <div class="imgcont"><img src="img/00002_F.jpg"/></div>
+    </div>
+  </div>
+  <div class="cont-y">
+    <div class="c-mn">
+      <div class="c-preview">
+        <ul>
+          <li> <span class="order">1</span>
+            <div class="m"><img src="img/preview.jpg"/></div>
+            <span class="close">&times;</span> </li>
+          <li> <span class="order">2</span>
+            <div class="m"><img src="img/preview1.jpg"/></div>
+            <span class="close">&times;</span> </li>
+          <li> <span class="order">3</span>
+            <div class="m"><img src="img/preview.jpg"/></div>
+            <span class="close">&times;</span> </li>
+        </ul>
+      </div>
+    </div>
+    <div class="c-bottom cl"><a class="c-btn close" href="">关闭全部</a><a class="c-btn s" href="">保存</a><a class="c-btn" href="">取消</a> </div>
+  </div>
+</div>
+</body>
+</html>