c.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. /**
  2. * Created by hn on 14-3-19.
  3. */
  4. ( function () {
  5. var c = {
  6. start: function ( editor ) {
  7. kfEditor = editor;
  8. window.kfEditor = kfEditor;
  9. vCursor = createVCursor( kfEditor );
  10. inp = document.getElementById( "hiddenInput" );
  11. initClick();
  12. }
  13. },
  14. isShowCursor = false,
  15. inp = null,
  16. isAllowInput = false,
  17. vCursor = null,
  18. lastCount = -1,
  19. cursorIndex = -1,
  20. currentStartOffset = -1,
  21. currentStartContainer = null,
  22. currentEndContainer = null,
  23. currentEndOffset = -1,
  24. ctrlStartContainer = null,
  25. ctrlStartOffset = null,
  26. currentGroup = null,
  27. isDrag = false,
  28. isMousedown = false,
  29. mousedownPoint = { x: 0, y: 0 },
  30. MAX_COUNT = 1000,
  31. CALL_COUNT = 0,
  32. CURSOR_BLINKS = null,
  33. // 移动阀值
  34. dragThreshold = 10,
  35. kfEditor = null;
  36. // init click
  37. function initClick () {
  38. var evt = kfEditor.request( "ui.canvas.container.event" );
  39. evt.on( "mousedown", function ( e ) {
  40. e.preventDefault();
  41. if (e.which !== 1 ) {
  42. return;
  43. }
  44. isMousedown = true;
  45. isDrag = false;
  46. mousedownPoint = { x: e.clientX, y: e.clientY };
  47. hideCursor();
  48. cursorIndex = 0;
  49. var target = e.target,
  50. group = kfEditor.requestService( "position.get.group", target ),
  51. parentGroup = kfEditor.requestService( "position.get.parent.group", target );
  52. if ( !group ) {
  53. group = kfEditor.requestService( "syntax.get.group.content", "_kf_editor_1_1" );
  54. }
  55. if ( !parentGroup ) {
  56. parentGroup = group;
  57. }
  58. currentGroup = group;
  59. currentStartContainer = group;
  60. ctrlStartContainer = currentStartContainer;
  61. currentStartOffset = getIndex( currentStartContainer, e.clientX );
  62. ctrlStartOffset = currentStartOffset;
  63. if ( group ) {
  64. kfEditor.requestService( "render.select.group.content", group );
  65. kfEditor.requestService( "syntax.update.record.cursor", group.id, currentStartOffset );
  66. // group的选中
  67. var cursorInfo = kfEditor.requestService( "syntax.get.record.cursor" );
  68. var result = kfEditor.requestService( "syntax.get.latex.info" );
  69. updateInput( result );
  70. if ( cursorInfo.startOffset === cursorInfo.endOffset ) {
  71. // 点击的是占位符, 则进行着色
  72. if ( kfEditor.requestService( "syntax.valid.placeholder", parentGroup.id ) ) {
  73. kfEditor.requestService( "render.select.group", parentGroup.id );
  74. // 否则, 绘制光标
  75. } else {
  76. drawCursor( group, cursorInfo.startOffset );
  77. }
  78. }
  79. } else {
  80. kfEditor.requestService( "render.clear.select" );
  81. }
  82. } );
  83. evt.on( "dblclick", function ( e ) {
  84. e.preventDefault();
  85. isMousedown = false;
  86. isDrag = false;
  87. hideCursor();
  88. var target = e.target,
  89. group = kfEditor.requestService( "position.get.parent.group", target );
  90. if ( group ) {
  91. kfEditor.requestService( "render.select.group.all", group );
  92. var result = kfEditor.requestService( "syntax.update.selection", group );
  93. updateInput( result );
  94. } else {
  95. kfEditor.requestService( "render.clear.select" );
  96. }
  97. } );
  98. evt.on( "mouseup", function ( e ) {
  99. e.preventDefault();
  100. var _isDrag = isDrag;
  101. isDrag = false;
  102. isMousedown = false;
  103. if ( _isDrag ) {
  104. var result = kfEditor.requestService( "syntax.get.latex.info" );
  105. updateInput( result );
  106. }
  107. } );
  108. inp.addEventListener( "keydown", function ( e ) {
  109. isMousedown = false;
  110. isDrag = false;
  111. hideCursor();
  112. switch ( e.keyCode ) {
  113. // left
  114. case 37:
  115. e.preventDefault();
  116. kfEditor.requestService( "syntax.cursor.move.left" );
  117. update();
  118. return;
  119. // right
  120. case 39:
  121. e.preventDefault();
  122. kfEditor.requestService( "syntax.cursor.move.right" );
  123. update();
  124. return;
  125. }
  126. }, false );
  127. kfEditor.registerService( "control.insert.group", null, {
  128. insertGroup: insertGroup
  129. } );
  130. function insertGroup ( val ) {
  131. var latexStr = inp.value,
  132. startOffset = inp.selectionStart,
  133. endOffset = inp.selectionEnd,
  134. latexInfo = null;
  135. kfEditor.requestService( "syntax.insert.group", val );
  136. latexInfo = kfEditor.requestService( "syntax.get.latex.info" );
  137. updateInput( latexInfo );
  138. kfEditor.requestService( "render.draw", latexInfo.str );
  139. drawCursorService();
  140. //
  141. // val = "{" + val + "}"
  142. //
  143. // startOffset = latexStr.substring( 0, startOffset );
  144. // endOffset = latexStr.substring( endOffset );
  145. //
  146. // latexStr = startOffset + val + endOffset;
  147. //
  148. // updateInput( {
  149. // str: latexStr,
  150. // startOffset: ( startOffset + val ).length + 1,
  151. // endOffset: ( startOffset + val ).length + 1
  152. // } );
  153. //
  154. // kfEditor.requestService( "render.draw", latexStr );
  155. //
  156. // drawCursorService();
  157. }
  158. function update () {
  159. var cursorInfo = kfEditor.requestService( "syntax.get.record.cursor" ),
  160. group = kfEditor.requestService( "syntax.get.group.content", cursorInfo.groupId );
  161. kfEditor.requestService( "render.select.group.content", group );
  162. var result = kfEditor.requestService( "syntax.get.latex.info" );
  163. updateInput( result );
  164. if ( cursorInfo.startOffset === cursorInfo.endOffset && !kfEditor.requestService( "syntax.valid.placeholder", cursorInfo.groupId ) ) {
  165. drawCursor( group, cursorInfo.startOffset );
  166. }
  167. return result;
  168. }
  169. function updateLatexValue ( latexResult ) {
  170. kfEditor.requestService( "render.reselect" );
  171. updateInput( latexResult );
  172. drawCursorService();
  173. }
  174. evt.on( "mousemove", function ( e ) {
  175. var group = null;
  176. e.preventDefault();
  177. if ( isMousedown && !isDrag && ( Math.abs( e.clientX - mousedownPoint.x ) > dragThreshold || Math.abs( e.clientY - mousedownPoint.y ) > dragThreshold ) ) {
  178. isDrag = true;
  179. }
  180. if ( !isDrag ) {
  181. return;
  182. }
  183. hideCursor();
  184. var group = kfEditor.requestService( "position.get.group", e.target );
  185. if ( !group ) {
  186. group = kfEditor.requestService( "syntax.get.group.content", "_kf_editor_1_1" );
  187. }
  188. currentEndContainer = kfEditor.requestService( "syntax.get.group.content", group.id );
  189. currentEndOffset = getMoveIndex( currentEndContainer, e.clientX );
  190. kfEditor.requestService( "syntax.update.record.cursor", currentEndContainer.id, currentStartOffset, currentEndOffset );
  191. kfEditor.requestService( "render.select.current.cursor" );
  192. if ( currentStartOffset === currentEndOffset ) {
  193. // 起点是占位符, 则选中占位符
  194. if ( kfEditor.requestService( "syntax.valid.placeholder", group.id ) ) {
  195. kfEditor.requestService( "render.select.group", group.id );
  196. // 否则, 绘制光标
  197. } else {
  198. drawCursor( currentEndContainer, currentEndOffset );
  199. }
  200. }
  201. } );
  202. inp.oninput = function ( e ) {
  203. cursorIndex += inp.value.length - lastCount;
  204. kfEditor.requestService( "render.draw", inp.value );
  205. kfEditor.requestService( "render.reselect" );
  206. drawCursorService();
  207. };
  208. function drawCursorService () {
  209. var cursorInfo = kfEditor.requestService( "syntax.get.record.cursor" ),
  210. group = kfEditor.requestService( "syntax.get.group.content", cursorInfo.groupId );
  211. drawCursor( group, cursorInfo.startOffset );
  212. kfEditor.requestService( "render.select.group", cursorInfo.groupId );
  213. }
  214. function updateInput ( result ) {
  215. inp.value = result.str;
  216. inp.selectionStart = result.startOffset;
  217. inp.selectionEnd = result.endOffset;
  218. inp.focus();
  219. isAllowInput = true;
  220. }
  221. inp.onblur = function () {
  222. isAllowInput = false;
  223. hideCursor();
  224. };
  225. // 修正起始容器和结束容器指向不统一的情况
  226. function updateContainer ( startContainer, endContainer, offset ) {
  227. var parent = null,
  228. oldChild = null,
  229. startIsParent = false,
  230. child = null;
  231. if ( startContainer.groupObj.contains( endContainer.groupObj ) ) {
  232. startIsParent = true;
  233. parent = startContainer;
  234. child = endContainer;
  235. } else if ( endContainer.groupObj.contains( startContainer.groupObj ) ) {
  236. // 结束区域更大
  237. parent = endContainer;
  238. child = startContainer;
  239. } else {
  240. parent = endContainer;
  241. clearCount();
  242. while ( parent = kfEditor.requestService( "position.get.group", parent.groupObj ) ) {
  243. updateCount();
  244. if ( parent.groupObj.contains( startContainer.groupObj ) ) {
  245. break;
  246. }
  247. }
  248. child = startContainer;
  249. }
  250. oldChild = child;
  251. clearCount();
  252. while ( child = kfEditor.requestService( "position.get.parent.group", child.groupObj ) ) {
  253. updateCount();
  254. if ( child.id === parent.id ) {
  255. child = oldChild;
  256. break;
  257. }
  258. oldChild = child;
  259. }
  260. currentStartContainer = parent;
  261. currentEndContainer = parent;
  262. // 起点在大的区域内
  263. if ( startIsParent ) {
  264. currentStartOffset = ctrlStartOffset;
  265. currentEndOffset = parent.content.indexOf( child.groupObj );
  266. if ( currentEndOffset >= currentStartOffset ) {
  267. currentEndOffset += 1;
  268. }
  269. // 起点在小的区域内部
  270. } else {
  271. currentStartOffset = parent.content.indexOf( child.groupObj );
  272. currentEndOffset = getOffset( parent, offset );
  273. if ( offset < mousedownPoint.x ) {
  274. currentStartOffset += 1;
  275. } else {
  276. currentEndOffset += 1;
  277. }
  278. }
  279. }
  280. // 返回在索引指定的位置插入光标,也就是说该索引的位置是在当前元素“之前”
  281. function getIndex ( group, offset ) {
  282. var index = getOffset( group, offset ),
  283. overflow = -1,
  284. // 点击是否在前半段
  285. box = null;
  286. box = group.content[ index ].getBoundingClientRect();
  287. overflow = offset - box.left;
  288. if ( overflow > box.width / 2 ) {
  289. index += 1;
  290. }
  291. return index;
  292. }
  293. function getOffset ( group, offset ) {
  294. var index = -1,
  295. box = null;
  296. kity.Utils.each( group.content, function ( child, i ) {
  297. index = i;
  298. box = child.getBoundingClientRect();
  299. if ( box.left + box.width > offset ) {
  300. return false;
  301. }
  302. } );
  303. return index;
  304. }
  305. function getMoveIndex ( group, offset ) {
  306. currentStartContainer = ctrlStartContainer;
  307. currentStartOffset = ctrlStartOffset;
  308. // 直接更新
  309. if ( ctrlStartContainer.id !== group.id ) {
  310. updateContainer( ctrlStartContainer, group, offset );
  311. return currentEndOffset;
  312. }
  313. var index = -1,
  314. box = null,
  315. overflow = -1;
  316. kity.Utils.each( group.content, function ( child, i ) {
  317. index = i;
  318. box = child.getBoundingClientRect();
  319. if ( box.left + box.width > offset ) {
  320. return false;
  321. }
  322. } );
  323. box = group.content[ index ].getBoundingClientRect();
  324. overflow = offset - box.left;
  325. // 向后走
  326. if ( index >= ctrlStartOffset ) {
  327. if ( overflow > box.width / 3 ) {
  328. index += 1;
  329. }
  330. // 向前走
  331. } else {
  332. // 光标还在默认边界范围内
  333. if ( overflow > box.width / 3 * 2 ) {
  334. index += 1;
  335. }
  336. }
  337. return index;
  338. }
  339. function hideCursor () {
  340. isShowCursor = false;
  341. stopCursorBlinks();
  342. vCursor.node.style.display = 'none';
  343. }
  344. function drawCursor ( group, index ) {
  345. var target = null,
  346. isBefore = true,
  347. prevBox = null,
  348. box = null,
  349. cursorTransform = null;
  350. if ( !isAllowInput ) {
  351. return;
  352. }
  353. var paper = kfEditor.requestService( "render.get.paper" ),
  354. offset = paper.getViewPort().offset,
  355. offsetLeft = 0,
  356. canvasZoom = kfEditor.requestService( "render.get.canvas.zoom" ),
  357. formulaZoom = paper.getZoom();
  358. // 定位到最后
  359. if ( index === group.content.length ) {
  360. index -= 1;
  361. isBefore = false;
  362. }
  363. target = group.content[ index ];
  364. box = target.getBoundingClientRect();
  365. prevBox = group.content[ index - 1 ] || target;
  366. prevBox = prevBox.getBoundingClientRect();
  367. // 更新transform
  368. cursorTransform = vCursor.getTransform();
  369. if ( isBefore ) {
  370. offsetLeft = box.left - paper.container.node.getBoundingClientRect().left;
  371. } else {
  372. offsetLeft = box.left - paper.container.node.getBoundingClientRect().left + box.width;
  373. }
  374. cursorTransform.m.e = offsetLeft / canvasZoom/ formulaZoom;
  375. cursorTransform.m.f = ( prevBox.top - paper.container.node.getBoundingClientRect().top ) / canvasZoom / formulaZoom;
  376. vCursor.setHeight( prevBox.height / canvasZoom / formulaZoom );
  377. vCursor.setTransform( cursorTransform );
  378. vCursor.node.style.display = "block";
  379. isShowCursor = true;
  380. startCursorBlinks();
  381. }
  382. }
  383. function clearCount () {
  384. CALL_COUNT = 0;
  385. }
  386. function updateCount () {
  387. CALL_COUNT++;
  388. if ( CALL_COUNT > MAX_COUNT ) {
  389. throw new Error("stack overflow");
  390. }
  391. }
  392. function createVCursor ( editor ) {
  393. var paper = editor.execCommand( "getPaper" ),
  394. vCursor = new kity.Rect( 1, 27, 0, 0 ).fill( "black" );
  395. vCursor.node.style.display = "none";
  396. paper.addShape( vCursor );
  397. return vCursor;
  398. }
  399. function startCursorBlinks () {
  400. if ( CURSOR_BLINKS ) {
  401. window.clearInterval( CURSOR_BLINKS );
  402. }
  403. CURSOR_BLINKS = window.setInterval( toggleCursor, 800 );
  404. }
  405. function stopCursorBlinks () {
  406. window.clearInterval( CURSOR_BLINKS );
  407. }
  408. function toggleCursor () {
  409. vCursor.node.style.display = isShowCursor ? "none" : "block";
  410. isShowCursor = !isShowCursor;
  411. }
  412. window.c = c;
  413. } )();