mark-control.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. function MarkControl(option) {
  2. this.option = option;
  3. this.maxPrefetchCount = parseInt(option.prefetchCount);
  4. this.maxPrefetchCount = this.maxPrefetchCount != undefined && this.maxPrefetchCount > 2 ? this.maxPrefetchCount : 2;
  5. this.container = option.container;
  6. this.context = {
  7. imageServer: option.imageServer,
  8. staticServer: option.staticServer,
  9. isFinish: false,
  10. prefetching: false,
  11. prefetchTask: []
  12. };
  13. // 初始化容器结构
  14. this.initContainer();
  15. // 初始化事件监听
  16. this.initTriggers(option);
  17. // 初始化功能模块
  18. this.initModules(option);
  19. // 初始化成功回调方法
  20. // console.log('MarkControl init success!');
  21. // if (option.success != undefined && typeof(option.success) == 'function')
  22. // {
  23. // option.success();
  24. // }
  25. }
  26. MarkControl.prototype.initContainer = function() {
  27. var height = this.option.height;
  28. if (height == undefined) {
  29. height = $(window).height();
  30. }
  31. this.container = getDom(this.main_row_dom, this).appendTo(this.container);
  32. if (this.option.enableSidear != false) {
  33. this.container.sidebar = getDom(this.sidebar_dom, this).appendTo(this.container);
  34. this.container.sidebar.css('max-height', height);
  35. }
  36. this.container.center = getDom(this.center_dom, this).appendTo(this.container);
  37. this.container.header = getDom(this.center_header_dom, this).appendTo(this.container.center).find('.header');
  38. if(this.option.switchCommonUrl!=undefined && this.option.switchCommonUrl.length>0){
  39. var switchButton = this.container.header.find('#switch-common-button');
  40. switchButton.attr('href', this.option.switchCommonUrl);
  41. switchButton.show();
  42. }
  43. this.container.centerContainer = getDom(this.center_content_dom, this).appendTo(this.container.center);
  44. this.container.centerContent = this.container.centerContainer.find('.image-content');
  45. this.container.centerNavbar = this.container.centerContent.find('nav');
  46. this.container.centerList = [];
  47. this.navNumber = 0;
  48. this.container.height(height);
  49. this.container.centerContent.css('height', height - this.container.header.parent().height());
  50. this.initHeaderAndAssistant();
  51. }
  52. MarkControl.prototype.initHeaderAndAssistant = function() {
  53. var self = this;
  54. this.container.header.find('#mark-user-name').html(this.option.userName);
  55. if (this.option.logoutTitle != undefined) {
  56. this.container.header.find('#logout-title').html(this.option.logoutTitle);
  57. }
  58. this.container.header.find('#logout-link').click(function() {
  59. self.trigger('logout.link.click');
  60. return true;
  61. })
  62. this.container.assistant = getDom(this.assistant_dom, this).appendTo(this.container);
  63. this.container.assistant.positionSet = false;
  64. this.container.assistantButton = this.container.header.find('#assistant-button');
  65. this.container.assistantButton.click(this, function(event) {
  66. if (self.container.assistant.positionSet == false) {
  67. self.container.assistant.offset({
  68. top: self.container.assistantButton.position().top + self.container.assistantButton.height() + 5,
  69. left: self.container.assistantButton.position().left - 85
  70. });
  71. self.container.assistant.positionSet = true;
  72. }
  73. self.container.assistant.toggle();
  74. });
  75. }
  76. MarkControl.prototype.initMarkFunction = function() {
  77. var functionList = this.container.assistant.functionList;
  78. if (functionList == undefined) {
  79. functionList = getDom(this.mark_function_dom, this).appendTo(this.container.assistant).find('#function-list');
  80. this.container.assistant.functionList = functionList;
  81. }
  82. }
  83. MarkControl.prototype.addNavGroup = function(title, content){
  84. var self = this;
  85. self.navNumber++;
  86. var nav = $('<a href="#"><span>'+title+'</span></a>').appendTo(self.container.centerNavbar);
  87. nav.attr('data-number', self.navNumber);
  88. content.attr('data-number', self.navNumber);
  89. nav.click(function(){
  90. self.container.centerNavbar.find('a').removeClass('selected');
  91. $(this).addClass('selected');
  92. for(var i in self.container.centerList){
  93. var dom = self.container.centerList[i];
  94. if(dom.attr('data-number')==$(this).attr('data-number')){
  95. dom.show();
  96. }else{
  97. dom.hide();
  98. }
  99. }
  100. });
  101. self.container.centerList.push(content);
  102. return nav;
  103. }
  104. // 增加某个事件的监听方法
  105. MarkControl.prototype.on = function(eventName, caller, callback, async) {
  106. if (eventName && callback && eventName.length > 0 && typeof(callback) == 'function') {
  107. if (async) {
  108. this.container.bind(eventName, callback);
  109. } else {
  110. if (this.triggers[eventName] == undefined) {
  111. this.triggers[eventName] = new Array();
  112. }
  113. this.triggers[eventName].push({
  114. caller: caller,
  115. callback: callback
  116. });
  117. }
  118. }
  119. }
  120. // 触发某个事件,并传递事件相关内容
  121. MarkControl.prototype.trigger = function(eventName, eventObject) {
  122. var result = true;
  123. if (eventName && eventName.length > 0) {
  124. var array = this.triggers[eventName];
  125. if (array != undefined && array.length > 0) {
  126. var event = {
  127. name: eventName
  128. }
  129. for (var i in array) {
  130. result = result & array[i].callback.call(array[i].caller, event, this.context, eventObject);
  131. }
  132. }
  133. this.container.trigger(eventName, this.context, eventObject);
  134. }
  135. return result;
  136. }
  137. // 初始化事件监听
  138. MarkControl.prototype.initTriggers = function(option) {
  139. if (this.triggers == undefined) {
  140. this.triggers = {};
  141. }
  142. if (option != undefined && option.on != undefined) {
  143. for (var name in option.on) {
  144. this.on(name, option.on[name]);
  145. }
  146. }
  147. var self = this;
  148. this.on('mark.sidebar.open', this, function(event, context, eventObject) {
  149. this.container.assistant.hide();
  150. if (this.container.center.hasClass('span12')) {
  151. this.container.center.removeClass('span12');
  152. this.container.center.addClass('span10');
  153. } else if (this.container.center.hasClass('span7')) {
  154. this.container.center.removeClass('span7');
  155. this.container.center.addClass('span5');
  156. }
  157. this.trigger('center.width.change');
  158. });
  159. this.on('mark.sidebar.close', this, function(event, context, eventObject) {
  160. this.container.assistant.hide();
  161. if (this.container.center.hasClass('span10')) {
  162. this.container.center.removeClass('span10');
  163. this.container.center.addClass('span12');
  164. } else if (this.container.center.hasClass('span5')) {
  165. this.container.center.removeClass('span5');
  166. this.container.center.addClass('span7');
  167. }
  168. this.trigger('center.width.change');
  169. });
  170. this.on('task.load.finish', this, function(event, context, eventObject) {
  171. self.container.centerNavbar.find('a:first').trigger('click');
  172. if(context.task!=undefined){
  173. context.task.spent = new Date().getTime();
  174. }
  175. });
  176. this.on('task.get.finish', this, function(event, context, eventObject) {
  177. context.prefetchCallback = undefined;
  178. });
  179. this.on('task.get.none', this, function(event, context, eventObject) {
  180. self.getStatus();
  181. if (context.task == undefined && self.option.clearUrl != undefined) {
  182. $.post(self.option.clearUrl, {}, function() {});
  183. }
  184. context.prefetchCallback = function() {
  185. context.prefetchCallback = undefined;
  186. if (self.context.task == undefined && self.context.prefetchTask.length > 0) {
  187. self.getTask();
  188. }
  189. }
  190. });
  191. this.on('task.prefetch.success', this, function(event, context, eventObject) {
  192. if (context.prefetchCallback != undefined) {
  193. context.prefetchCallback();
  194. }
  195. setTimeout(function() {
  196. self.prefetch();
  197. }, 500);
  198. });
  199. this.on('task.prefetch.error', this, function(event, context, eventObject) {
  200. setTimeout(function() {
  201. self.prefetch();
  202. }, 500);
  203. });
  204. this.on('task.prefetch.none', this, function(event, context, eventObject) {
  205. if (context.prefetchCallback != undefined) {
  206. context.prefetchCallback();
  207. }
  208. if (context.isFinish != true) {
  209. setTimeout(function() {
  210. self.prefetch();
  211. }, 1000);
  212. }
  213. });
  214. this.on('task.prefetch.finish', this, function(event, context, eventObject) {
  215. self.getTask();
  216. });
  217. this.on('task.submit.before', this, function(event, context, eventObject) {
  218. context.submitting = true;
  219. });
  220. this.on('task.submit.success', this, function(event, context, eventObject) {
  221. context.submitting = false;
  222. context.task = undefined;
  223. self.getTask();
  224. });
  225. this.on('task.submit.error', this, function(event, context, eventObject) {
  226. context.submitting = false;
  227. });
  228. this.on('task.pass.success', this, function(event, context, eventObject) {
  229. if (context.task != undefined && self.option.clearUrl != undefined) {
  230. $.post(self.option.clearUrl, {
  231. libraryId: context.task.libraryId
  232. });
  233. }
  234. context.task = undefined;
  235. self.getTask();
  236. });
  237. $(document).keypress(this, function(event) {
  238. if (self.context.listenKeyboard != false) {
  239. return self.trigger('key.press', event);
  240. }
  241. });
  242. $(document).keydown(this, function(event) {
  243. if (self.context.listenKeyboard != false) {
  244. return self.trigger('key.down', event);
  245. }
  246. });
  247. $(document).keyup(this, function(event) {
  248. if (self.context.listenKeyboard != false) {
  249. return self.trigger('key.up', event);
  250. }
  251. });
  252. window.onbeforeunload = function(e) {
  253. if (self.option.clearUrl != undefined) {
  254. $.post(self.option.clearUrl);
  255. }
  256. }
  257. }
  258. // 初始化功能模块
  259. MarkControl.prototype.initModules = function(option) {
  260. if (this.modules == undefined) {
  261. this.modules = {};
  262. }
  263. var names = [];
  264. var options = [];
  265. for (var name in this.defaultModules) {
  266. names.push(name);
  267. options[name] = this.defaultModules[name];
  268. }
  269. if (option.modules != undefined) {
  270. for (var name in option.modules) {
  271. if (options[name] == undefined) {
  272. names.push(name);
  273. options[name] = {};
  274. }
  275. $.extend(options[name], option.modules[name]);
  276. }
  277. }
  278. this.initModule(names, options, this.option.success);
  279. // initModule(this, names, 0, options);
  280. }
  281. // 指定初始化某个名称的模块
  282. MarkControl.prototype.initModule = function(names, options, success) {
  283. for (var i in names) {
  284. var name = names[i];
  285. var option = options[name];
  286. var moduleInit = name.replace(/-/g, '_');
  287. if (option == undefined || typeof(option) != 'object') {
  288. option = {};
  289. }
  290. option.markControl = this;
  291. eval('this.modules[name]=' + moduleInit + '(option, function(){})');
  292. }
  293. if (success != undefined && typeof(success) == 'function') {
  294. success();
  295. }
  296. }
  297. function initModuleAsync(markControl, names, index, option) {
  298. if (index < names.length) {
  299. var name = names[index];
  300. var moduleOption = option[name];
  301. var moduleUrl = 'modules/' + name + '.js';
  302. var moduleInit = name.replace(/-/g, '_');
  303. var modules = markControl.modules;
  304. if (modules[name] == undefined) {
  305. if (typeof(moduleOption) != 'object') {
  306. moduleOption = {};
  307. }
  308. moduleOption.markControl = markControl;
  309. $.getScript(moduleUrl, function() {
  310. var success = function() {
  311. initModule(markControl, names, index + 1, option);
  312. }
  313. eval('modules[name]=' + moduleInit + '(moduleOption, success)');
  314. });
  315. } else {
  316. initModule(markControl, names, index + 1, option);
  317. }
  318. } else {
  319. if (markControl.option.success != undefined && typeof(markControl.option.success) == 'function') {
  320. markControl.option.success();
  321. }
  322. }
  323. }
  324. MarkControl.prototype.start = function(taskOption) {
  325. taskOption.markControl = this;
  326. var markControl = this;
  327. taskOption.success = function() {
  328. markControl.context.prefetchCallback = function() {
  329. markControl.context.prefetchCallback = undefined;
  330. markControl.getTask();
  331. }
  332. markControl.context.statusCallback = function() {
  333. markControl.context.statusCallback = undefined;
  334. markControl.prefetch();
  335. }
  336. markControl.getStatus();
  337. };
  338. taskOption.error = function(message) {
  339. alert('初始化失败,请刷新页面重新加载');
  340. };
  341. this.taskControl = new TaskControl(taskOption);
  342. this.taskControl.init();
  343. }
  344. // task预加载
  345. MarkControl.prototype.prefetch = function() {
  346. var taskControl = this.taskControl;
  347. var markControl = this;
  348. var context = this.context;
  349. var imageBuilder = this.modules['image-builder'];
  350. if (context.isFinish != true) {
  351. if (taskControl.isFinish()) {
  352. context.isFinish = true;
  353. } else if (context.prefetchTask.length >= markControl.maxPrefetchCount) {
  354. markControl.trigger('task.prefetch.success');
  355. } else if (context.prefetching == false) {
  356. context.prefetching = true;
  357. markControl.trigger('task.prefetch.before');
  358. taskControl.fetch(function(task) {
  359. if (imageBuilder != undefined) {
  360. imageBuilder.build(task, function(error) {
  361. if (error) {
  362. context.prefetching = false;
  363. markControl.trigger('task.prefetch.error');
  364. } else {
  365. context.prefetchTask.push(task);
  366. context.prefetchStatus = undefined;
  367. context.prefetching = false;
  368. markControl.trigger('task.prefetch.success');
  369. }
  370. });
  371. } else {
  372. context.prefetchTask.push(task);
  373. context.prefetchStatus = undefined;
  374. context.prefetching = false;
  375. markControl.trigger('task.prefetch.success');
  376. }
  377. }, function(task) {
  378. context.prefetchStatus = task.message;
  379. context.prefetching = false;
  380. markControl.trigger('task.prefetch.none');
  381. }, function() {
  382. context.prefetching = false;
  383. markControl.trigger('task.prefetch.none');
  384. });
  385. }
  386. } else {
  387. markControl.trigger('task.prefetch.finish');
  388. }
  389. }
  390. MarkControl.prototype.getStatus = function() {
  391. if (this.taskControl == undefined) {
  392. return;
  393. }
  394. var self = this;
  395. this.taskControl.status(function(status) {
  396. self.context.status = status;
  397. if (status != undefined) {
  398. self.context.isFinish = status.blockTotalCount > 0 && status.blockTotalCount == (status.blockMarkedCount + status.blockExceptionCount);
  399. }
  400. if (self.context.statusCallback != undefined) {
  401. self.context.statusCallback();
  402. }
  403. self.trigger('mark.status.change', status);
  404. })
  405. }
  406. MarkControl.prototype.getTask = function() {
  407. if (this.taskControl == undefined) {
  408. return;
  409. }
  410. var markControl = this;
  411. var context = this.context;
  412. markControl.trigger('task.get.before');
  413. if (context.isFinish == true) {
  414. markControl.trigger('task.get.finish');
  415. return;
  416. } else if (context.task != undefined) {
  417. markControl.trigger('task.get.success');
  418. return;
  419. } else if (context.waitTask != undefined) {
  420. // 优先选择因回评等操作处于等待状态的任务
  421. context.task = context.waitTask;
  422. context.waitTask = undefined;
  423. markControl.trigger('task.get.success');
  424. } else if (context.prefetchTask.length > 0) {
  425. // 判断是否有任务已预加载完毕
  426. context.task = context.prefetchTask.shift();
  427. markControl.trigger('task.get.success');
  428. } else if (context.prefetchStatus != undefined) {
  429. // 判断是否有在无预加载任务的情况下的消息提示
  430. markControl.trigger('task.get.none', context.prefetchStatus);
  431. } else {
  432. markControl.trigger('task.get.none');
  433. }
  434. }
  435. MarkControl.prototype.getHistory = function(data) {
  436. if (this.taskControl == undefined) {
  437. return;
  438. }
  439. this.taskControl.history(data, function(result) {
  440. data.result = result;
  441. this.option.markControl.trigger('history.get.success', data);
  442. }, function(message) {
  443. data.message = message;
  444. this.option.markControl.trigger('history.get.error', data);
  445. });
  446. }
  447. MarkControl.prototype.setTask = function(task) {
  448. var imageBuilder = this.modules['image-builder'];
  449. var self = this;
  450. if (this.context.task != undefined && !this.context.task.previous && this.context.waitTask == undefined) {
  451. this.context.waitTask = this.context.task;
  452. }
  453. this.trigger('task.get.before');
  454. if (imageBuilder != undefined && task != undefined) {
  455. imageBuilder.build(task, function(error) {
  456. self.context.task = task;
  457. self.trigger('task.get.success');
  458. });
  459. } else {
  460. self.context.task = task;
  461. if (task != undefined) {
  462. this.trigger('task.get.success');
  463. }
  464. }
  465. }
  466. MarkControl.prototype.submitTask = function(submitUrl) {
  467. var task = this.context.task;
  468. var markControl = this;
  469. var submitUrl = submitUrl != undefined && submitUrl.length > 0 ? submitUrl : this.option.submitUrl;
  470. if (task != undefined && this.context.submitting != true) {
  471. //开启强制标记
  472. if(this.option.forceSpecialTag===true){
  473. var isTag = !(task.tagList==undefined ||task.tagList==null ||task.tagList.length <= 0);
  474. var isTrack = false;
  475. for(var i in task.trackList) {
  476. var track = task.trackList[i];
  477. if(track.positionX!=0 || track.positionY!=0 ){
  478. isTrack = true;
  479. }
  480. }
  481. if(!(isTag||isTrack)){
  482. markControl.trigger('task.submit.forceSpecialTag', '强制标记已开启,至少使用一个标记');
  483. return;
  484. }
  485. }
  486. var submitObj = {
  487. statusValue: task.statusValue,
  488. studentId: task.studentId,
  489. libraryId: task.libraryId,
  490. totalScore: task.totalScore,
  491. scoreList: task.scoreList,
  492. trackList: task.trackList,
  493. tagList: task.tagList,
  494. spent: new Date().getTime() - task.spent
  495. }
  496. this.trigger('task.submit.before');
  497. this.trigger('mark.specialTag.before');
  498. if (this.taskControl != undefined) {
  499. // 已定义任务引擎
  500. this.taskControl.submit(submitObj, function(status) {
  501. if (status != undefined && status.valid == true) {
  502. markControl.context.status = status;
  503. markControl.trigger('mark.status.change', status);
  504. }
  505. // markControl.context.task = undefined;
  506. markControl.trigger('task.submit.success');
  507. markControl.trigger('mark.specialTag.success');
  508. // markControl.getTask();
  509. }, function(message) {
  510. markControl.trigger('task.submit.error', message);
  511. });
  512. } else if (submitUrl != undefined && submitUrl.length > 0) {
  513. // 未定义任务引擎,依赖定义/传入的提交地址
  514. $.ajax({
  515. url: submitUrl,
  516. type: 'POST',
  517. data: submitObj,
  518. success: function(result) {
  519. if (result.success == true) {
  520. // markControl.context.task = undefined;
  521. markControl.trigger('task.submit.success');
  522. markControl.trigger('mark.specialTag.success');
  523. // markControl.getTask();
  524. } else {
  525. markControl.trigger('task.submit.error', result.message);
  526. }
  527. },
  528. error: function(message) {
  529. markControl.trigger('task.submit.error', message);
  530. }
  531. });
  532. } else {
  533. markControl.trigger('task.submit.success');
  534. markControl.trigger('mark.specialTag.success');
  535. // markControl.getTask();
  536. }
  537. }
  538. }
  539. // 默认要初始化的模块名称
  540. MarkControl.prototype.defaultModules = {
  541. 'image-builder': {}
  542. };
  543. MarkControl.prototype.main_row_dom = '<div class="row-fluid"></div>';
  544. MarkControl.prototype.sidebar_dom = '<div class="mark-sidebar span2 hide"></div>';
  545. MarkControl.prototype.center_dom = '<div class="center-content span12"></div>';
  546. MarkControl.prototype.center_header_dom = '<div class="row-fluid"><div class="header"><p class="tips">\
  547. <em><a href="##" class="btn" id="switch-common-button" style="display:none">切换到普通模式</a>\
  548. <a href="javascript:void(0)" id="assistant-button" class="btn"><i class="icon-wrench"></i> 小助手</a></em>\
  549. <a class="useinfo" href="#"><i class="icon-user icon-white"></i><i id="mark-user-name"></i></a>\
  550. <a class="logout" id="logout-link" href="{logoutUrl}"><i class="icon-off icon-white"></i> <i id="logout-title">退出</i></a>\
  551. </p></div></div>';
  552. MarkControl.prototype.center_content_dom = '<div class="row-fluid"><div class="image-content span9"><nav></nav></div></div>';
  553. MarkControl.prototype.assistant_dom = '<div class="popover bottom assistant"><div class="arrow"></div></div>';
  554. MarkControl.prototype.mark_function_dom = '<h3 class="popover-title">评卷功能</h3>\
  555. <div class="popover-content"><p id="function-list" class="popover-list">\
  556. </p></div>';
  557. // 其他通用方法
  558. String.prototype.startWith = function(prefix) {
  559. return this.indexOf(prefix) === 0;
  560. }
  561. String.prototype.endWith = function(suffix) {
  562. return this.match(suffix + "$") == suffix;
  563. };
  564. // 日期格式化
  565. Date.prototype.format = function(fmt) { // author: meizz
  566. var o = {
  567. "M+": this.getMonth() + 1, // 月份
  568. "d+": this.getDate(), // 日
  569. "h+": this.getHours(), // 小时
  570. "m+": this.getMinutes(), // 分
  571. "s+": this.getSeconds(), // 秒
  572. "q+": Math.floor((this.getMonth() + 3) / 3), // 季度
  573. "S": this.getMilliseconds() // 毫秒
  574. };
  575. if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
  576. for (var k in o) {
  577. if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
  578. }
  579. return fmt;
  580. }
  581. function getDom(content, markControl) {
  582. if (markControl != undefined && markControl.option.staticServer != undefined) {
  583. content = content.replace(/{staticServer}/g, markControl.option.staticServer);
  584. }
  585. if (markControl != undefined && markControl.option.logoutUrl != undefined) {
  586. content = content.replace(/{logoutUrl}/g, markControl.option.logoutUrl);
  587. }
  588. return $(content);
  589. }
  590. function isArray(obj) {
  591. return obj != undefined && Object.prototype.toString.call(obj) === '[object Array]';
  592. }