]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/debian/missing-sources/framework/Web/Javascripts/source/tinymce-405/classes/EnterKey.js
baculum: Add missing-sources directory in debian metadata structure
[bacula/bacula] / gui / baculum / debian / missing-sources / framework / Web / Javascripts / source / tinymce-405 / classes / EnterKey.js
1 /**
2  * EnterKey.js
3  *
4  * Copyright, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://www.tinymce.com/license
8  * Contributing: http://www.tinymce.com/contributing
9  */
10
11 /**
12  * Contains logic for handling the enter key to split/generate block elements.
13  */
14 define("tinymce/EnterKey", [
15         "tinymce/dom/TreeWalker",
16         "tinymce/Env"
17 ], function(TreeWalker, Env) {
18         var isIE = Env.ie && Env.ie < 11;
19
20         return function(editor) {
21                 var dom = editor.dom, selection = editor.selection, settings = editor.settings;
22                 var undoManager = editor.undoManager, schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements();
23
24                 function handleEnterKey(evt) {
25                         var rng = selection.getRng(true), tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey,
26                                 newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;
27
28                         // Returns true if the block can be split into two blocks or not
29                         function canSplitBlock(node) {
30                                 return node &&
31                                         dom.isBlock(node) &&
32                                         !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
33                                         !/^(fixed|absolute)/i.test(node.style.position) &&
34                                         dom.getContentEditable(node) !== "true";
35                         }
36
37                         // Renders empty block on IE
38                         function renderBlockOnIE(block) {
39                                 var oldRng;
40
41                                 if (dom.isBlock(block)) {
42                                         oldRng = selection.getRng();
43                                         block.appendChild(dom.create('span', null, '\u00a0'));
44                                         selection.select(block);
45                                         block.lastChild.outerHTML = '';
46                                         selection.setRng(oldRng);
47                                 }
48                         }
49
50                         // Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p>
51                         function trimInlineElementsOnLeftSideOfBlock(block) {
52                                 var node = block, firstChilds = [], i;
53
54                                 // Find inner most first child ex: <p><i><b>*</b></i></p>
55                                 while ((node = node.firstChild)) {
56                                         if (dom.isBlock(node)) {
57                                                 return;
58                                         }
59
60                                         if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
61                                                 firstChilds.push(node);
62                                         }
63                                 }
64
65                                 i = firstChilds.length;
66                                 while (i--) {
67                                         node = firstChilds[i];
68                                         if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
69                                                 dom.remove(node);
70                                         } else {
71                                                 // Remove <a> </a> see #5381
72                                                 if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') {
73                                                         dom.remove(node);
74                                                 }
75                                         }
76                                 }
77                         }
78
79                         // Moves the caret to a suitable position within the root for example in the first non
80                         // pure whitespace text node or before an image
81                         function moveToCaretPosition(root) {
82                                 var walker, node, rng, lastNode = root, tempElm;
83
84                                 rng = dom.createRng();
85
86                                 if (root.hasChildNodes()) {
87                                         walker = new TreeWalker(root, root);
88
89                                         while ((node = walker.current())) {
90                                                 if (node.nodeType == 3) {
91                                                         rng.setStart(node, 0);
92                                                         rng.setEnd(node, 0);
93                                                         break;
94                                                 }
95
96                                                 if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
97                                                         rng.setStartBefore(node);
98                                                         rng.setEndBefore(node);
99                                                         break;
100                                                 }
101
102                                                 lastNode = node;
103                                                 node = walker.next();
104                                         }
105
106                                         if (!node) {
107                                                 rng.setStart(lastNode, 0);
108                                                 rng.setEnd(lastNode, 0);
109                                         }
110                                 } else {
111                                         if (root.nodeName == 'BR') {
112                                                 if (root.nextSibling && dom.isBlock(root.nextSibling)) {
113                                                         // Trick on older IE versions to render the caret before the BR between two lists
114                                                         if (!documentMode || documentMode < 9) {
115                                                                 tempElm = dom.create('br');
116                                                                 root.parentNode.insertBefore(tempElm, root);
117                                                         }
118
119                                                         rng.setStartBefore(root);
120                                                         rng.setEndBefore(root);
121                                                 } else {
122                                                         rng.setStartAfter(root);
123                                                         rng.setEndAfter(root);
124                                                 }
125                                         } else {
126                                                 rng.setStart(root, 0);
127                                                 rng.setEnd(root, 0);
128                                         }
129                                 }
130
131                                 selection.setRng(rng);
132
133                                 // Remove tempElm created for old IE:s
134                                 dom.remove(tempElm);
135                                 selection.scrollIntoView(root);
136                         }
137
138                         // Creates a new block element by cloning the current one or creating a new one if the name is specified
139                         // This function will also copy any text formatting from the parent block and add it to the new one
140                         function createNewBlock(name) {
141                                 var node = container, block, clonedNode, caretNode;
142
143                                 block = name || parentBlockName == "TABLE" ? dom.create(name || newBlockName) : parentBlock.cloneNode(false);
144                                 caretNode = block;
145
146                                 // Clone any parent styles
147                                 if (settings.keep_styles !== false) {
148                                         do {
149                                                 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {
150                                                         // Never clone a caret containers
151                                                         if (node.id == '_mce_caret') {
152                                                                 continue;
153                                                         }
154
155                                                         clonedNode = node.cloneNode(false);
156                                                         dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique
157
158                                                         if (block.hasChildNodes()) {
159                                                                 clonedNode.appendChild(block.firstChild);
160                                                                 block.appendChild(clonedNode);
161                                                         } else {
162                                                                 caretNode = clonedNode;
163                                                                 block.appendChild(clonedNode);
164                                                         }
165                                                 }
166                                         } while ((node = node.parentNode));
167                                 }
168
169                                 // BR is needed in empty blocks on non IE browsers
170                                 if (!isIE) {
171                                         caretNode.innerHTML = '<br data-mce-bogus="1">';
172                                 }
173
174                                 return block;
175                         }
176
177                         // Returns true/false if the caret is at the start/end of the parent block element
178                         function isCaretAtStartOrEndOfBlock(start) {
179                                 var walker, node, name;
180
181                                 // Caret is in the middle of a text node like "a|b"
182                                 if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
183                                         return false;
184                                 }
185
186                                 // If after the last element in block node edge case for #5091
187                                 if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) {
188                                         return true;
189                                 }
190
191                                 // If the caret if before the first element in parentBlock
192                                 if (start && container.nodeType == 1 && container == parentBlock.firstChild) {
193                                         return true;
194                                 }
195
196                                 // Caret can be before/after a table
197                                 if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
198                                         return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
199                                 }
200
201                                 // Walk the DOM and look for text nodes or non empty elements
202                                 walker = new TreeWalker(container, parentBlock);
203
204                                 // If caret is in beginning or end of a text block then jump to the next/previous node
205                                 if (container.nodeType == 3) {
206                                         if (start && offset === 0) {
207                                                 walker.prev();
208                                         } else if (!start && offset == container.nodeValue.length) {
209                                                 walker.next();
210                                         }
211                                 }
212
213                                 while ((node = walker.current())) {
214                                         if (node.nodeType === 1) {
215                                                 // Ignore bogus elements
216                                                 if (!node.getAttribute('data-mce-bogus')) {
217                                                         // Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>
218                                                         name = node.nodeName.toLowerCase();
219                                                         if (nonEmptyElementsMap[name] && name !== 'br') {
220                                                                 return false;
221                                                         }
222                                                 }
223                                         } else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
224                                                 return false;
225                                         }
226
227                                         if (start) {
228                                                 walker.prev();
229                                         } else {
230                                                 walker.next();
231                                         }
232                                 }
233
234                                 return true;
235                         }
236
237                         // Wraps any text nodes or inline elements in the specified forced root block name
238                         function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
239                                 var newBlock, parentBlock, startNode, node, next, rootBlockName, blockName = newBlockName || 'P';
240
241                                 // Not in a block element or in a table cell or caption
242                                 parentBlock = dom.getParent(container, dom.isBlock);
243                                 rootBlockName = editor.getBody().nodeName.toLowerCase();
244                                 if (!parentBlock || !canSplitBlock(parentBlock)) {
245                                         parentBlock = parentBlock || editableRoot;
246
247                                         if (!parentBlock.hasChildNodes()) {
248                                                 newBlock = dom.create(blockName);
249                                                 parentBlock.appendChild(newBlock);
250                                                 rng.setStart(newBlock, 0);
251                                                 rng.setEnd(newBlock, 0);
252                                                 return newBlock;
253                                         }
254
255                                         // Find parent that is the first child of parentBlock
256                                         node = container;
257                                         while (node.parentNode != parentBlock) {
258                                                 node = node.parentNode;
259                                         }
260
261                                         // Loop left to find start node start wrapping at
262                                         while (node && !dom.isBlock(node)) {
263                                                 startNode = node;
264                                                 node = node.previousSibling;
265                                         }
266
267                                         if (startNode && schema.isValidChild(rootBlockName, blockName.toLowerCase())) {
268                                                 newBlock = dom.create(blockName);
269                                                 startNode.parentNode.insertBefore(newBlock, startNode);
270
271                                                 // Start wrapping until we hit a block
272                                                 node = startNode;
273                                                 while (node && !dom.isBlock(node)) {
274                                                         next = node.nextSibling;
275                                                         newBlock.appendChild(node);
276                                                         node = next;
277                                                 }
278
279                                                 // Restore range to it's past location
280                                                 rng.setStart(container, offset);
281                                                 rng.setEnd(container, offset);
282                                         }
283                                 }
284
285                                 return container;
286                         }
287
288                         // Inserts a block or br before/after or in the middle of a split list of the LI is empty
289                         function handleEmptyListItem() {
290                                 function isFirstOrLastLi(first) {
291                                         var node = containerBlock[first ? 'firstChild' : 'lastChild'];
292
293                                         // Find first/last element since there might be whitespace there
294                                         while (node) {
295                                                 if (node.nodeType == 1) {
296                                                         break;
297                                                 }
298
299                                                 node = node[first ? 'nextSibling' : 'previousSibling'];
300                                         }
301
302                                         return node === parentBlock;
303                                 }
304
305                                 function getContainerBlock() {
306                                         var containerBlockParent = containerBlock.parentNode;
307
308                                         if (containerBlockParent.nodeName == 'LI') {
309                                                 return containerBlockParent;
310                                         }
311
312                                         return containerBlock;
313                                 }
314
315                                 // Check if we are in an nested list
316                                 var containerBlockParentName = containerBlock.parentNode.nodeName;
317                                 if (/^(OL|UL|LI)$/.test(containerBlockParentName)) {
318                                         newBlockName = 'LI';
319                                 }
320
321                                 newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');
322
323                                 if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
324                                         if (containerBlockParentName == 'LI') {
325                                                 // Nested list is inside a LI
326                                                 dom.insertAfter(newBlock, getContainerBlock());
327                                         } else {
328                                                 // Is first and last list item then replace the OL/UL with a text block
329                                                 dom.replace(newBlock, containerBlock);
330                                         }
331                                 } else if (isFirstOrLastLi(true)) {
332                                         if (containerBlockParentName == 'LI') {
333                                                 // List nested in an LI then move the list to a new sibling LI
334                                                 dom.insertAfter(newBlock, getContainerBlock());
335                                                 newBlock.appendChild(dom.doc.createTextNode(' ')); // Needed for IE so the caret can be placed
336                                                 newBlock.appendChild(containerBlock);
337                                         } else {
338                                                 // First LI in list then remove LI and add text block before list
339                                                 containerBlock.parentNode.insertBefore(newBlock, containerBlock);
340                                         }
341                                 } else if (isFirstOrLastLi()) {
342                                         // Last LI in list then remove LI and add text block after list
343                                         dom.insertAfter(newBlock, getContainerBlock());
344                                         renderBlockOnIE(newBlock);
345                                 } else {
346                                         // Middle LI in list the split the list and insert a text block in the middle
347                                         // Extract after fragment and insert it after the current block
348                                         containerBlock = getContainerBlock();
349                                         tmpRng = rng.cloneRange();
350                                         tmpRng.setStartAfter(parentBlock);
351                                         tmpRng.setEndAfter(containerBlock);
352                                         fragment = tmpRng.extractContents();
353                                         dom.insertAfter(fragment, containerBlock);
354                                         dom.insertAfter(newBlock, containerBlock);
355                                 }
356
357                                 dom.remove(parentBlock);
358                                 moveToCaretPosition(newBlock);
359                                 undoManager.add();
360                         }
361
362                         // Walks the parent block to the right and look for BR elements
363                         function hasRightSideContent() {
364                                 var walker = new TreeWalker(container, parentBlock), node;
365
366                                 while ((node = walker.next())) {
367                                         if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {
368                                                 return true;
369                                         }
370                                 }
371                         }
372
373                         // Inserts a BR element if the forced_root_block option is set to false or empty string
374                         function insertBr() {
375                                 var brElm, extraBr, marker;
376
377                                 if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
378                                         // Insert extra BR element at the end block elements
379                                         if (!isIE && !hasRightSideContent()) {
380                                                 brElm = dom.create('br');
381                                                 rng.insertNode(brElm);
382                                                 rng.setStartAfter(brElm);
383                                                 rng.setEndAfter(brElm);
384                                                 extraBr = true;
385                                         }
386                                 }
387
388                                 brElm = dom.create('br');
389                                 rng.insertNode(brElm);
390
391                                 // Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it
392                                 if (isIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
393                                         brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
394                                 }
395
396                                 // Insert temp marker and scroll to that
397                                 marker = dom.create('span', {}, '&nbsp;');
398                                 brElm.parentNode.insertBefore(marker, brElm);
399                                 selection.scrollIntoView(marker);
400                                 dom.remove(marker);
401
402                                 if (!extraBr) {
403                                         rng.setStartAfter(brElm);
404                                         rng.setEndAfter(brElm);
405                                 } else {
406                                         rng.setStartBefore(brElm);
407                                         rng.setEndBefore(brElm);
408                                 }
409
410                                 selection.setRng(rng);
411                                 undoManager.add();
412                         }
413
414                         // Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element
415                         function trimLeadingLineBreaks(node) {
416                                 do {
417                                         if (node.nodeType === 3) {
418                                                 node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, '');
419                                         }
420
421                                         node = node.firstChild;
422                                 } while (node);
423                         }
424
425                         function getEditableRoot(node) {
426                                 var root = dom.getRoot(), parent, editableRoot;
427
428                                 // Get all parents until we hit a non editable parent or the root
429                                 parent = node;
430                                 while (parent !== root && dom.getContentEditable(parent) !== "false") {
431                                         if (dom.getContentEditable(parent) === "true") {
432                                                 editableRoot = parent;
433                                         }
434
435                                         parent = parent.parentNode;
436                                 }
437
438                                 return parent !== root ? editableRoot : root;
439                         }
440
441                         // Adds a BR at the end of blocks that only contains an IMG or INPUT since
442                         // these might be floated and then they won't expand the block
443                         function addBrToBlockIfNeeded(block) {
444                                 var lastChild;
445
446                                 // IE will render the blocks correctly other browsers needs a BR
447                                 if (!isIE) {
448                                         block.normalize(); // Remove empty text nodes that got left behind by the extract
449
450                                         // Check if the block is empty or contains a floated last child
451                                         lastChild = block.lastChild;
452                                         if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {
453                                                 dom.add(block, 'br');
454                                         }
455                                 }
456                         }
457
458                         // Delete any selected contents
459                         if (!rng.collapsed) {
460                                 editor.execCommand('Delete');
461                                 return;
462                         }
463
464                         // Event is blocked by some other handler for example the lists plugin
465                         if (evt.isDefaultPrevented()) {
466                                 return;
467                         }
468
469                         // Setup range items and newBlockName
470                         container = rng.startContainer;
471                         offset = rng.startOffset;
472                         newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block;
473                         newBlockName = newBlockName ? newBlockName.toUpperCase() : '';
474                         documentMode = dom.doc.documentMode;
475                         shiftKey = evt.shiftKey;
476
477                         // Resolve node index
478                         if (container.nodeType == 1 && container.hasChildNodes()) {
479                                 isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
480                                 container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
481                                 if (isAfterLastNodeInContainer && container.nodeType == 3) {
482                                         offset = container.nodeValue.length;
483                                 } else {
484                                         offset = 0;
485                                 }
486                         }
487
488                         // Get editable root node normaly the body element but sometimes a div or span
489                         editableRoot = getEditableRoot(container);
490
491                         // If there is no editable root then enter is done inside a contentEditable false element
492                         if (!editableRoot) {
493                                 return;
494                         }
495
496                         undoManager.beforeChange();
497
498                         // If editable root isn't block nor the root of the editor
499                         if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) {
500                                 if (!newBlockName || shiftKey) {
501                                         insertBr();
502                                 }
503
504                                 return;
505                         }
506
507                         // Wrap the current node and it's sibling in a default block if it's needed.
508                         // for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>
509                         // This won't happen if root blocks are disabled or the shiftKey is pressed
510                         if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) {
511                                 container = wrapSelfAndSiblingsInDefaultBlock(container, offset);
512                         }
513
514                         // Find parent block and setup empty block paddings
515                         parentBlock = dom.getParent(container, dom.isBlock);
516                         containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
517
518                         // Setup block names
519                         parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
520                         containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
521
522                         // Enter inside block contained within a LI then split or insert before/after LI
523                         if (containerBlockName == 'LI' && !evt.ctrlKey) {
524                                 parentBlock = containerBlock;
525                                 parentBlockName = containerBlockName;
526                         }
527
528                         // Handle enter in LI
529                         if (parentBlockName == 'LI') {
530                                 if (!newBlockName && shiftKey) {
531                                         insertBr();
532                                         return;
533                                 }
534
535                                 // Handle enter inside an empty list item
536                                 if (dom.isEmpty(parentBlock)) {
537                                         handleEmptyListItem();
538                                         return;
539                                 }
540                         }
541
542                         // Don't split PRE tags but insert a BR instead easier when writing code samples etc
543                         if (parentBlockName == 'PRE' && settings.br_in_pre !== false) {
544                                 if (!shiftKey) {
545                                         insertBr();
546                                         return;
547                                 }
548                         } else {
549                                 // If no root block is configured then insert a BR by default or if the shiftKey is pressed
550                                 if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) {
551                                         insertBr();
552                                         return;
553                                 }
554                         }
555
556                         // If parent block is root then never insert new blocks
557                         if (newBlockName && parentBlock === editor.getBody()) {
558                                 return;
559                         }
560
561                         // Default block name if it's not configured
562                         newBlockName = newBlockName || 'P';
563
564                         // Insert new block before/after the parent block depending on caret location
565                         if (isCaretAtStartOrEndOfBlock()) {
566                                 // If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup
567                                 if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') {
568                                         newBlock = createNewBlock(newBlockName);
569                                 } else {
570                                         newBlock = createNewBlock();
571                                 }
572
573                                 // Split the current container block element if enter is pressed inside an empty inner block element
574                                 if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {
575                                         // Split container block for example a BLOCKQUOTE at the current blockParent location for example a P
576                                         newBlock = dom.split(containerBlock, parentBlock);
577                                 } else {
578                                         dom.insertAfter(newBlock, parentBlock);
579                                 }
580
581                                 moveToCaretPosition(newBlock);
582                         } else if (isCaretAtStartOrEndOfBlock(true)) {
583                                 // Insert new block before
584                                 newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
585                                 renderBlockOnIE(newBlock);
586                         } else {
587                                 // Extract after fragment and insert it after the current block
588                                 tmpRng = rng.cloneRange();
589                                 tmpRng.setEndAfter(parentBlock);
590                                 fragment = tmpRng.extractContents();
591                                 trimLeadingLineBreaks(fragment);
592                                 newBlock = fragment.firstChild;
593                                 dom.insertAfter(fragment, parentBlock);
594                                 trimInlineElementsOnLeftSideOfBlock(newBlock);
595                                 addBrToBlockIfNeeded(parentBlock);
596                                 moveToCaretPosition(newBlock);
597                         }
598
599                         dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
600                         undoManager.add();
601                 }
602
603                 editor.on('keydown', function(evt) {
604                         if (evt.keyCode == 13) {
605                                 if (handleEnterKey(evt) !== false) {
606                                         evt.preventDefault();
607                                 }
608                         }
609                 });
610         };
611 });