3 'modules/documentCanvas/canvas/utils'
 
   4 ], function($, utils) {
 
  20 var scroll = function(place, textElement) {
 
  21     var rect = textElement.getBoundingClientRect(),
 
  22         scroll = $('#rng-module-documentCanvas-contentWrapper'),
 
  23         border = rect.bottom - (place === 'top' ? rect.height : 0) - scroll.offset().top + scroll[0].scrollTop,
 
  24         visible = scroll[0].scrollTop + {top: 0, bottom: scroll.height()}[place],
 
  28     if(place === 'top' && (border - padding < visible)) {
 
  29         toScroll =  border - visible - padding;
 
  30     } else if(place === 'bottom' && (border + padding > visible))  {
 
  31         toScroll = border - visible + padding;
 
  34         scroll[0].scrollTop = scroll[0].scrollTop + toScroll;
 
  39 var getLastRectAbove = function(node, y) {
 
  40     var rects = node.getClientRects(),
 
  43     while((rect = rects[idx])) {
 
  54 var getFirstRectBelow = function(node, y) {
 
  55     var rects = node.getClientRects(),
 
  58     while((rect = rects[idx])) {
 
  68 var handleKeyEvent = function(e, s) {
 
  69     keyEventHandlers.some(function(handler) {
 
  70         if(handler.applies(e, s)) {
 
  76 // todo: whileRemoveWholetext
 
  77 var keyEventHandlers = [
 
  79         applies: function(e, s) {
 
  82                 s.type === 'textSelection' &&
 
  83                 s.startsAtBeginning() &&
 
  92         applies: function(e, s) {
 
  93             return e.key === KEYS.ARROW_UP && s.type === 'caret';
 
  97             var caretRect = window.getSelection().getRangeAt(0).getClientRects()[0],
 
  98                 frameRects = s.element.dom[0].getClientRects(),
 
  99                 caretTop = caretRect.bottom - caretRect.height,
 
 100                 position, target,rect, scrolled;
 
 103             if((frameRects[0].bottom === caretRect.bottom) || (caretRect.left < frameRects[0].left)) {
 
 105                 s.canvas.rootWrapper.find('[document-text-element]').each(function() {
 
 106                     var test = getLastRectAbove(this, caretTop);
 
 115                     scrolled = scroll('top', target);
 
 116                     position = utils.caretPositionFromPoint(caretRect.left, rect.bottom - 1 - scrolled);
 
 117                     s.canvas.setCurrentElement(s.canvas.getDocumentElement(position.textNode), {caretTo: position.offset});
 
 121                 scrolled = scroll('top', target);
 
 122                 var left = caretRect.left;
 
 123                 if(left > rect.left + rect.width) {
 
 124                     left = rect.left + rect.width;
 
 125                 } else if(left < rect.left ) {
 
 128                 position = utils.caretPositionFromPoint(left, rect.bottom - 1 - scrolled);
 
 129                 s.canvas.setCurrentElement(s.canvas.getDocumentElement(position.textNode), {caretTo: position.offset});
 
 134         applies: function(e, s) {
 
 135             return e.key === KEYS.ARROW_DOWN && s.type === 'caret';
 
 137         run: function(e, s) {
 
 139             var caretRect = window.getSelection().getRangeAt(0).getClientRects()[0],
 
 140                 frameRects = s.element.dom[0].getClientRects(),
 
 141                 lastRect = frameRects[frameRects.length-1],
 
 142                 position, target,rect, scrolled;
 
 144             if(lastRect.bottom === caretRect.bottom || (caretRect.left > lastRect.left + lastRect.width)) {
 
 146                 s.canvas.rootWrapper.find('[document-text-element]').each(function() {
 
 147                     var test = getFirstRectBelow(this, caretRect.bottom);
 
 155                     scrolled = scroll('bottom', target);
 
 156                     position = utils.caretPositionFromPoint(caretRect.left, rect.top +1 - scrolled);
 
 157                     s.canvas.setCurrentElement(s.canvas.getDocumentElement(position.textNode), {caretTo: position.offset});
 
 161                 scrolled = scroll('bottom', target);
 
 162                 var left = caretRect.left;
 
 163                 if(left > rect.left + rect.width) {
 
 164                     left = rect.left + rect.width;
 
 165                 } else if(left < rect.left ) {
 
 168                 position = utils.caretPositionFromPoint(left, rect.top +1 - scrolled);
 
 169                 s.canvas.setCurrentElement(s.canvas.getDocumentElement(position.textNode), {caretTo: position.offset});
 
 174         applies: function(e, s) {
 
 175             return e.key === KEYS.ARROW_LEFT && s.type === 'caret';
 
 177         run: function(e, s) {
 
 183                 prev = s.canvas.getPreviousTextElement(s.element);
 
 185                     scroll('top', prev.dom[0]);
 
 186                     s.canvas.setCurrentElement(s.canvas.getDocumentElement(prev.dom.contents()[0]), {caretTo: 'end'});
 
 192         applies: function(e, s) {
 
 193             return e.key === KEYS.ARROW_RIGHT && s.type === 'caret';
 
 195         run: function(e, s) {
 
 200                 next = s.canvas.getNextTextElement(s.element);
 
 202                     scroll('bottom', next.dom[0]);
 
 203                     s.canvas.setCurrentElement(s.canvas.getDocumentElement(next.dom.contents()[0]), {caretTo: 0});
 
 206                 var secondToLast = (s.offset === s.element.wlxmlNode.getText().length -1);
 
 208                     // Only Flying Spaghetti Monster knows why this is need for FF (for versions at least 26 to 31)
 
 210                     s.canvas.setCurrentElement(s.element, {caretTo: 'end'});
 
 216         applies: function(e, s) {
 
 217             return s.type === 'caret' &&
 
 218                 s.element.wlxmlNode.parent().is({tagName: 'span'}) &&
 
 219                 s.element.wlxmlNode.getText().length === 1 &&
 
 221                 (e.key === KEYS.BACKSPACE);
 
 223         run: function(e, s) {
 
 225                 prevTextNode = s.element.canvas.getPreviousTextElement(s.element).wlxmlNode;
 
 227             s.element.wlxmlNode.parent().detach(params);
 
 228             s.canvas.setCurrentElement(
 
 229                 (params.ret && params.ret.mergedTo) || prevTextNode,
 
 230                 {caretTo: params.ret ? params.ret.previousLen : (prevTextNode ? prevTextNode.getText().length : 0)});
 
 234         applies: function(e, s) {
 
 235             return s.type === 'caret' && (
 
 236                 (s.isAtBeginning() && e.key === KEYS.BACKSPACE) ||
 
 237                 (s.isAtEnd() && e.key === KEYS.DELETE)
 
 241             var direction, caretTo, cursorAtOperationEdge, goto, element;
 
 243             if(e.key === KEYS.BACKSPACE) {
 
 246                 cursorAtOperationEdge = s.isAtBeginning();
 
 252                 cursorAtOperationEdge = s.isAtEnd();
 
 253                 element = cursorAtOperationEdge && s.canvas.getNearestTextElement(direction, s.element);
 
 256             if(!cursorAtOperationEdge || !element) {
 
 261             var parent = element.wlxmlNode.parent();
 
 262             if(element.wlxmlNode.getIndex() === 0 && parent.isContextRoot() && (!parent.is('item') || parent.getIndex() === 0)) {
 
 263                 // Don't even try to do anything at the edge of a context root, except for non-first items
 
 264                 // - this is a temporary solution until key events handling get refactored into something more sane.
 
 270             s.canvas.wlxmlDocument.transaction(function() {
 
 271                 if(element.wlxmlNode.getIndex() === 0) {
 
 272                     goto = element.wlxmlNode.parent().moveUp();
 
 274                     goto = element.wlxmlNode.moveUp();
 
 277                    s.canvas.setCurrentElement(goto.node, {caretTo: goto.offset});
 
 281                     description: gettext('Remove text')
 
 288         applies: function(e,s) {
 
 289             return s.type === 'caret' && s.element.getText().length === 1 && (e.key === KEYS.BACKSPACE || e.key === KEYS.DELETE);
 
 293             s.element.wlxmlNode.setText('');
 
 294             s.canvas.setCurrentElement(s.element, {caretTo: 0});
 
 299         applies: function(e, s) {
 
 300             return s.type === 'textSelection' && (e.key === KEYS.BACKSPACE || e.key === KEYS.DELETE);
 
 302         run: function(e, s) {
 
 303             var direction = 'above',
 
 307             if(e.key === KEYS.DELETE) {
 
 314             if(s.startsAtBeginning && s.endsAtEnd && s.startElement.sameNode(s.endElement)) {
 
 315                 goto = s.startElement;
 
 316                 caretTo = s.startOffset;
 
 317             } else if(direction === 'above') {
 
 318                 if(s.startsAtBeginning()) {
 
 319                     goto = s.canvas.getNearestTextElement('above', s.startElement);
 
 322                     goto = s.startElement;
 
 323                     caretTo = s.startOffset;
 
 327                     goto = s.canvas.getNearestTextElement('below', s.startElement);
 
 335             var doc = s.canvas.wlxmlDocument;
 
 336             doc.transaction(function() {
 
 340                         node: s.startElement.wlxmlNode,
 
 341                         offset: s.startOffset
 
 344                         node: s.endElement.wlxmlNode,
 
 350                 success: function() {
 
 352                         s.canvas.setCurrentElement(goto, {caretTo: caretTo});
 
 360         applies: function(e, s) {
 
 361             var parent = s.element && s.element.wlxmlNode.parent(),
 
 362                 parentIsItem = parent && parent.is('item'),
 
 363                 itemIsOnList = parent && parent.parent() && parent.parent().is('list');
 
 364             return s.type === 'caret' && e.key === KEYS.ENTER && s.element.isEmpty() && parentIsItem && itemIsOnList;
 
 366         run: function(e, s) {
 
 367             var item = s.element.wlxmlNode.parent(),
 
 368                 list = item.parent();
 
 370             s.canvas.wlxmlDocument.transaction(function() {
 
 371                 var p = list.after({tagName: 'div', attrs: {'class': 'p'}});
 
 372                 p.append({text: ''});
 
 376                 success: function(p) {
 
 377                     s.canvas.setCurrentElement(p);
 
 383         applies: function(e, s) {
 
 384             return s.type === 'caret' && e.key === KEYS.ENTER && !s.element.parent().isRootElement();
 
 386         run: function(e, s) {
 
 387             var parent = s.element.parent(),
 
 388                 children = parent.children(),
 
 389                 result, goto, gotoOptions;
 
 393             if(children.length === 1 && s.element.isEmpty()) {
 
 397             s.canvas.wlxmlDocument.transaction(function() {
 
 398                 result = s.element.wlxmlNode.breakContent({offset: s.offset});
 
 401                     description: gettext('Splitting text'),
 
 402                     fragment: s.toDocumentFragment()
 
 406             if(result.emptyText) {
 
 407                 goto = result.emptyText;
 
 410                 goto = result.second;
 
 411                 gotoOptions = {caretTo: 'start'};
 
 414             s.canvas.setCurrentElement(utils.getElementForNode(goto), gotoOptions);
 
 418         applies: function (e, s) {
 
 419             return s.type === 'nodeSelection' && e.key === KEYS.ENTER && !s.element.isRootElement();
 
 421         run: function (e, s) {
 
 422             var parent = s.element.parent(),
 
 423                 children = parent.children(),
 
 424                 result, goto, gotoOptions;
 
 427             s.canvas.wlxmlDocument.transaction(function() {
 
 428                 result = s.element.wlxmlNode.insertNewNode();
 
 431                     description: gettext('Inserting node'),
 
 432                     fragment: s.toDocumentFragment()
 
 436             s.canvas.setCurrentElement(utils.getElementForNode(result), {caretTo: 'start'});
 
 442     handleKeyEvent: handleKeyEvent,