]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/debian/missing-sources/framework/Web/Javascripts/source/tinymce-405/classes/EditorCommands.js
baculum: Add missing-sources directory in debian metadata structure
[bacula/bacula] / gui / baculum / debian / missing-sources / framework / Web / Javascripts / source / tinymce-405 / classes / EditorCommands.js
1 /**
2  * EditorCommands.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  * This class enables you to add custom editor commands and it contains
13  * overrides for native browser commands to address various bugs and issues.
14  *
15  * @class tinymce.EditorCommands
16  */
17 define("tinymce/EditorCommands", [
18         "tinymce/html/Serializer",
19         "tinymce/Env",
20         "tinymce/util/Tools"
21 ], function(Serializer, Env, Tools) {
22         // Added for compression purposes
23         var each = Tools.each, extend = Tools.extend;
24         var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode;
25         var isGecko = Env.gecko, isIE = Env.ie;
26         var TRUE = true, FALSE = false;
27
28         return function(editor) {
29                 var dom = editor.dom,
30                         selection = editor.selection,
31                         commands = {state: {}, exec: {}, value: {}},
32                         settings = editor.settings,
33                         formatter = editor.formatter,
34                         bookmark;
35
36                 /**
37                  * Executes the specified command.
38                  *
39                  * @method execCommand
40                  * @param {String} command Command to execute.
41                  * @param {Boolean} ui Optional user interface state.
42                  * @param {Object} value Optional value for command.
43                  * @return {Boolean} true/false if the command was found or not.
44                  */
45                 function execCommand(command, ui, value) {
46                         var func;
47
48                         command = command.toLowerCase();
49                         if ((func = commands.exec[command])) {
50                                 func(command, ui, value);
51                                 return TRUE;
52                         }
53
54                         return FALSE;
55                 }
56
57                 /**
58                  * Queries the current state for a command for example if the current selection is "bold".
59                  *
60                  * @method queryCommandState
61                  * @param {String} command Command to check the state of.
62                  * @return {Boolean/Number} true/false if the selected contents is bold or not, -1 if it's not found.
63                  */
64                 function queryCommandState(command) {
65                         var func;
66
67                         command = command.toLowerCase();
68                         if ((func = commands.state[command])) {
69                                 return func(command);
70                         }
71
72                         return -1;
73                 }
74
75                 /**
76                  * Queries the command value for example the current fontsize.
77                  *
78                  * @method queryCommandValue
79                  * @param {String} command Command to check the value of.
80                  * @return {Object} Command value of false if it's not found.
81                  */
82                 function queryCommandValue(command) {
83                         var func;
84
85                         command = command.toLowerCase();
86                         if ((func = commands.value[command])) {
87                                 return func(command);
88                         }
89
90                         return FALSE;
91                 }
92
93                 /**
94                  * Adds commands to the command collection.
95                  *
96                  * @method addCommands
97                  * @param {Object} command_list Name/value collection with commands to add, the names can also be comma separated.
98                  * @param {String} type Optional type to add, defaults to exec. Can be value or state as well.
99                  */
100                 function addCommands(command_list, type) {
101                         type = type || 'exec';
102
103                         each(command_list, function(callback, command) {
104                                 each(command.toLowerCase().split(','), function(command) {
105                                         commands[type][command] = callback;
106                                 });
107                         });
108                 }
109
110                 // Expose public methods
111                 extend(this, {
112                         execCommand: execCommand,
113                         queryCommandState: queryCommandState,
114                         queryCommandValue: queryCommandValue,
115                         addCommands: addCommands
116                 });
117
118                 // Private methods
119
120                 function execNativeCommand(command, ui, value) {
121                         if (ui === undefined) {
122                                 ui = FALSE;
123                         }
124
125                         if (value === undefined) {
126                                 value = null;
127                         }
128
129                         return editor.getDoc().execCommand(command, ui, value);
130                 }
131
132                 function isFormatMatch(name) {
133                         return formatter.match(name);
134                 }
135
136                 function toggleFormat(name, value) {
137                         formatter.toggle(name, value ? {value: value} : undefined);
138                         editor.nodeChanged();
139                 }
140
141                 function storeSelection(type) {
142                         bookmark = selection.getBookmark(type);
143                 }
144
145                 function restoreSelection() {
146                         selection.moveToBookmark(bookmark);
147                 }
148
149                 // Add execCommand overrides
150                 addCommands({
151                         // Ignore these, added for compatibility
152                         'mceResetDesignMode,mceBeginUndoLevel': function() {},
153
154                         // Add undo manager logic
155                         'mceEndUndoLevel,mceAddUndoLevel': function() {
156                                 editor.undoManager.add();
157                         },
158
159                         'Cut,Copy,Paste': function(command) {
160                                 var doc = editor.getDoc(), failed;
161
162                                 // Try executing the native command
163                                 try {
164                                         execNativeCommand(command);
165                                 } catch (ex) {
166                                         // Command failed
167                                         failed = TRUE;
168                                 }
169
170                                 // Present alert message about clipboard access not being available
171                                 if (failed || !doc.queryCommandSupported(command)) {
172                                         editor.windowManager.alert(
173                                                 "Your browser doesn't support direct access to the clipboard. " +
174                                                 "Please use the Ctrl+X/C/V keyboard shortcuts instead."
175                                         );
176                                 }
177                         },
178
179                         // Override unlink command
180                         unlink: function(command) {
181                                 if (selection.isCollapsed()) {
182                                         selection.select(selection.getNode());
183                                 }
184
185                                 execNativeCommand(command);
186                                 selection.collapse(FALSE);
187                         },
188
189                         // Override justify commands to use the text formatter engine
190                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
191                                 var align = command.substring(7);
192
193                                 if (align == 'full') {
194                                         align = 'justify';
195                                 }
196
197                                 // Remove all other alignments first
198                                 each('left,center,right,justify'.split(','), function(name) {
199                                         if (align != name) {
200                                                 formatter.remove('align' + name);
201                                         }
202                                 });
203
204                                 toggleFormat('align' + align);
205                                 execCommand('mceRepaint');
206                         },
207
208                         // Override list commands to fix WebKit bug
209                         'InsertUnorderedList,InsertOrderedList': function(command) {
210                                 var listElm, listParent;
211
212                                 execNativeCommand(command);
213
214                                 // WebKit produces lists within block elements so we need to split them
215                                 // we will replace the native list creation logic to custom logic later on
216                                 // TODO: Remove this when the list creation logic is removed
217                                 listElm = dom.getParent(selection.getNode(), 'ol,ul');
218                                 if (listElm) {
219                                         listParent = listElm.parentNode;
220
221                                         // If list is within a text block then split that block
222                                         if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {
223                                                 storeSelection();
224                                                 dom.split(listParent, listElm);
225                                                 restoreSelection();
226                                         }
227                                 }
228                         },
229
230                         // Override commands to use the text formatter engine
231                         'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
232                                 toggleFormat(command);
233                         },
234
235                         // Override commands to use the text formatter engine
236                         'ForeColor,HiliteColor,FontName': function(command, ui, value) {
237                                 toggleFormat(command, value);
238                         },
239
240                         FontSize: function(command, ui, value) {
241                                 var fontClasses, fontSizes;
242
243                                 // Convert font size 1-7 to styles
244                                 if (value >= 1 && value <= 7) {
245                                         fontSizes = explode(settings.font_size_style_values);
246                                         fontClasses = explode(settings.font_size_classes);
247
248                                         if (fontClasses) {
249                                                 value = fontClasses[value - 1] || value;
250                                         } else {
251                                                 value = fontSizes[value - 1] || value;
252                                         }
253                                 }
254
255                                 toggleFormat(command, value);
256                         },
257
258                         RemoveFormat: function(command) {
259                                 formatter.remove(command);
260                         },
261
262                         mceBlockQuote: function() {
263                                 toggleFormat('blockquote');
264                         },
265
266                         FormatBlock: function(command, ui, value) {
267                                 return toggleFormat(value || 'p');
268                         },
269
270                         mceCleanup: function() {
271                                 var bookmark = selection.getBookmark();
272
273                                 editor.setContent(editor.getContent({cleanup: TRUE}), {cleanup: TRUE});
274
275                                 selection.moveToBookmark(bookmark);
276                         },
277
278                         mceRemoveNode: function(command, ui, value) {
279                                 var node = value || selection.getNode();
280
281                                 // Make sure that the body node isn't removed
282                                 if (node != editor.getBody()) {
283                                         storeSelection();
284                                         editor.dom.remove(node, TRUE);
285                                         restoreSelection();
286                                 }
287                         },
288
289                         mceSelectNodeDepth: function(command, ui, value) {
290                                 var counter = 0;
291
292                                 dom.getParent(selection.getNode(), function(node) {
293                                         if (node.nodeType == 1 && counter++ == value) {
294                                                 selection.select(node);
295                                                 return FALSE;
296                                         }
297                                 }, editor.getBody());
298                         },
299
300                         mceSelectNode: function(command, ui, value) {
301                                 selection.select(value);
302                         },
303
304                         mceInsertContent: function(command, ui, value) {
305                                 var parser, serializer, parentNode, rootNode, fragment, args;
306                                 var marker, nodeRect, viewPortRect, rng, node, node2, bookmarkHtml, viewportBodyElement;
307
308                                 function trimOrPaddLeftRight(html) {
309                                         var rng, container, offset;
310
311                                         rng = selection.getRng(true);
312                                         container = rng.startContainer;
313                                         offset = rng.startOffset;
314
315                                         function hasSiblingText(siblingName) {
316                                                 return container[siblingName] && container[siblingName].nodeType == 3;
317                                         }
318
319                                         if (container.nodeType == 3) {
320                                                 if (offset > 0) {
321                                                         html = html.replace(/^&nbsp;/, ' ');
322                                                 } else if (!hasSiblingText('previousSibling')) {
323                                                         html = html.replace(/^ /, '&nbsp;');
324                                                 }
325
326                                                 if (offset < container.length) {
327                                                         html = html.replace(/&nbsp;(<br>|)$/, ' ');
328                                                 } else if (!hasSiblingText('nextSibling')) {
329                                                         html = html.replace(/(&nbsp;| )(<br>|)$/, '&nbsp;');
330                                                 }
331                                         }
332
333                                         return html;
334                                 }
335
336                                 // Check for whitespace before/after value
337                                 if (/^ | $/.test(value)) {
338                                         value = trimOrPaddLeftRight(value);
339                                 }
340
341                                 // Setup parser and serializer
342                                 parser = editor.parser;
343                                 serializer = new Serializer({}, editor.schema);
344                                 bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;</span>';
345
346                                 // Run beforeSetContent handlers on the HTML to be inserted
347                                 args = {content: value, format: 'html', selection: true};
348                                 editor.fire('BeforeSetContent', args);
349                                 value = args.content;
350
351                                 // Add caret at end of contents if it's missing
352                                 if (value.indexOf('{$caret}') == -1) {
353                                         value += '{$caret}';
354                                 }
355
356                                 // Replace the caret marker with a span bookmark element
357                                 value = value.replace(/\{\$caret\}/, bookmarkHtml);
358
359                                 // Insert node maker where we will insert the new HTML and get it's parent
360                                 if (!selection.isCollapsed()) {
361                                         editor.getDoc().execCommand('Delete', false, null);
362                                 }
363
364                                 parentNode = selection.getNode();
365
366                                 // Parse the fragment within the context of the parent node
367                                 args = {context: parentNode.nodeName.toLowerCase()};
368                                 fragment = parser.parse(value, args);
369
370                                 // Move the caret to a more suitable location
371                                 node = fragment.lastChild;
372                                 if (node.attr('id') == 'mce_marker') {
373                                         marker = node;
374
375                                         for (node = node.prev; node; node = node.walk(true)) {
376                                                 if (node.type == 3 || !dom.isBlock(node.name)) {
377                                                         node.parent.insert(marker, node, node.name === 'br');
378                                                         break;
379                                                 }
380                                         }
381                                 }
382
383                                 // If parser says valid we can insert the contents into that parent
384                                 if (!args.invalid) {
385                                         value = serializer.serialize(fragment);
386
387                                         // Check if parent is empty or only has one BR element then set the innerHTML of that parent
388                                         node = parentNode.firstChild;
389                                         node2 = parentNode.lastChild;
390                                         if (!node || (node === node2 && node.nodeName === 'BR')) {
391                                                 dom.setHTML(parentNode, value);
392                                         } else {
393                                                 selection.setContent(value);
394                                         }
395                                 } else {
396                                         // If the fragment was invalid within that context then we need
397                                         // to parse and process the parent it's inserted into
398
399                                         // Insert bookmark node and get the parent
400                                         selection.setContent(bookmarkHtml);
401                                         parentNode = selection.getNode();
402                                         rootNode = editor.getBody();
403
404                                         // Opera will return the document node when selection is in root
405                                         if (parentNode.nodeType == 9) {
406                                                 parentNode = node = rootNode;
407                                         } else {
408                                                 node = parentNode;
409                                         }
410
411                                         // Find the ancestor just before the root element
412                                         while (node !== rootNode) {
413                                                 parentNode = node;
414                                                 node = node.parentNode;
415                                         }
416
417                                         // Get the outer/inner HTML depending on if we are in the root and parser and serialize that
418                                         value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);
419                                         value = serializer.serialize(
420                                                 parser.parse(
421                                                         // Need to replace by using a function since $ in the contents would otherwise be a problem
422                                                         value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {
423                                                                 return serializer.serialize(fragment);
424                                                         })
425                                                 )
426                                         );
427
428                                         // Set the inner/outer HTML depending on if we are in the root or not
429                                         if (parentNode == rootNode) {
430                                                 dom.setHTML(rootNode, value);
431                                         } else {
432                                                 dom.setOuterHTML(parentNode, value);
433                                         }
434                                 }
435
436                                 marker = dom.get('mce_marker');
437
438                                 // Scroll range into view scrollIntoView on element can't be used since it will scroll the main view port as well
439                                 nodeRect = dom.getRect(marker);
440                                 viewPortRect = dom.getViewPort(editor.getWin());
441
442                                 // Check if node is out side the viewport if it is then scroll to it
443                                 if ((nodeRect.y + nodeRect.h > viewPortRect.y + viewPortRect.h || nodeRect.y < viewPortRect.y) ||
444                                         (nodeRect.x > viewPortRect.x + viewPortRect.w || nodeRect.x < viewPortRect.x)) {
445                                         viewportBodyElement = isIE ? editor.getDoc().documentElement : editor.getBody();
446                                         viewportBodyElement.scrollLeft = nodeRect.x;
447                                         viewportBodyElement.scrollTop = nodeRect.y - viewPortRect.h + 25;
448                                 }
449
450                                 // Move selection before marker and remove it
451                                 rng = dom.createRng();
452
453                                 // If previous sibling is a text node set the selection to the end of that node
454                                 node = marker.previousSibling;
455                                 if (node && node.nodeType == 3) {
456                                         rng.setStart(node, node.nodeValue.length);
457
458                                         // TODO: Why can't we normalize on IE
459                                         if (!isIE) {
460                                                 node2 = marker.nextSibling;
461                                                 if (node2 && node2.nodeType == 3) {
462                                                         node.appendData(node2.data);
463                                                         node2.parentNode.removeChild(node2);
464                                                 }
465                                         }
466                                 } else {
467                                         // If the previous sibling isn't a text node or doesn't exist set the selection before the marker node
468                                         rng.setStartBefore(marker);
469                                         rng.setEndBefore(marker);
470                                 }
471
472                                 // Remove the marker node and set the new range
473                                 dom.remove(marker);
474                                 selection.setRng(rng);
475
476                                 // Dispatch after event and add any visual elements needed
477                                 editor.fire('SetContent', args);
478                                 editor.addVisual();
479                         },
480
481                         mceInsertRawHTML: function(command, ui, value) {
482                                 selection.setContent('tiny_mce_marker');
483                                 editor.setContent(
484                                         editor.getContent().replace(/tiny_mce_marker/g, function() {
485                                                 return value;
486                                         })
487                                 );
488                         },
489
490                         mceToggleFormat: function(command, ui, value) {
491                                 toggleFormat(value);
492                         },
493
494                         mceSetContent: function(command, ui, value) {
495                                 editor.setContent(value);
496                         },
497
498                         'Indent,Outdent': function(command) {
499                                 var intentValue, indentUnit, value;
500
501                                 // Setup indent level
502                                 intentValue = settings.indentation;
503                                 indentUnit = /[a-z%]+$/i.exec(intentValue);
504                                 intentValue = parseInt(intentValue, 10);
505
506                                 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {
507                                         // If forced_root_blocks is set to false we don't have a block to indent so lets create a div
508                                         if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) {
509                                                 formatter.apply('div');
510                                         }
511
512                                         each(selection.getSelectedBlocks(), function(element) {
513                                                 var indentStyleName;
514
515                                                 if (element.nodeName != "LI") {
516                                                         indentStyleName = dom.getStyle(element, 'direction', true) == 'rtl' ? 'paddingRight' : 'paddingLeft';
517
518                                                         if (command == 'outdent') {
519                                                                 value = Math.max(0, parseInt(element.style[indentStyleName] || 0, 10) - intentValue);
520                                                                 dom.setStyle(element, indentStyleName, value ? value + indentUnit : '');
521                                                         } else {
522                                                                 value = (parseInt(element.style[indentStyleName] || 0, 10) + intentValue) + indentUnit;
523                                                                 dom.setStyle(element, indentStyleName, value);
524                                                         }
525                                                 }
526                                         });
527                                 } else {
528                                         execNativeCommand(command);
529                                 }
530                         },
531
532                         mceRepaint: function() {
533                                 if (isGecko) {
534                                         try {
535                                                 storeSelection(TRUE);
536
537                                                 if (selection.getSel()) {
538                                                         selection.getSel().selectAllChildren(editor.getBody());
539                                                 }
540
541                                                 selection.collapse(TRUE);
542                                                 restoreSelection();
543                                         } catch (ex) {
544                                                 // Ignore
545                                         }
546                                 }
547                         },
548
549                         InsertHorizontalRule: function() {
550                                 editor.execCommand('mceInsertContent', false, '<hr />');
551                         },
552
553                         mceToggleVisualAid: function() {
554                                 editor.hasVisual = !editor.hasVisual;
555                                 editor.addVisual();
556                         },
557
558                         mceReplaceContent: function(command, ui, value) {
559                                 editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format: 'text'})));
560                         },
561
562                         mceInsertLink: function(command, ui, value) {
563                                 var anchor;
564
565                                 if (typeof(value) == 'string') {
566                                         value = {href: value};
567                                 }
568
569                                 anchor = dom.getParent(selection.getNode(), 'a');
570
571                                 // Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.
572                                 value.href = value.href.replace(' ', '%20');
573
574                                 // Remove existing links if there could be child links or that the href isn't specified
575                                 if (!anchor || !value.href) {
576                                         formatter.remove('link');
577                                 }
578
579                                 // Apply new link to selection
580                                 if (value.href) {
581                                         formatter.apply('link', value, anchor);
582                                 }
583                         },
584
585                         selectAll: function() {
586                                 var root = dom.getRoot(), rng = dom.createRng();
587
588                                 // Old IE does a better job with selectall than new versions
589                                 if (selection.getRng().setStart) {
590                                         rng.setStart(root, 0);
591                                         rng.setEnd(root, root.childNodes.length);
592
593                                         selection.setRng(rng);
594                                 } else {
595                                         execNativeCommand('SelectAll');
596                                 }
597                         },
598
599                         mceNewDocument: function() {
600                                 editor.setContent('');
601                         }
602                 });
603
604                 // Add queryCommandState overrides
605                 addCommands({
606                         // Override justify commands
607                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
608                                 var name = 'align' + command.substring(7);
609                                 var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks();
610                                 var matches = map(nodes, function(node) {
611                                         return !!formatter.matchNode(node, name);
612                                 });
613                                 return inArray(matches, TRUE) !== -1;
614                         },
615
616                         'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
617                                 return isFormatMatch(command);
618                         },
619
620                         mceBlockQuote: function() {
621                                 return isFormatMatch('blockquote');
622                         },
623
624                         Outdent: function() {
625                                 var node;
626
627                                 if (settings.inline_styles) {
628                                         if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
629                                                 return TRUE;
630                                         }
631
632                                         if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
633                                                 return TRUE;
634                                         }
635                                 }
636
637                                 return (
638                                         queryCommandState('InsertUnorderedList') ||
639                                         queryCommandState('InsertOrderedList') ||
640                                         (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'))
641                                 );
642                         },
643
644                         'InsertUnorderedList,InsertOrderedList': function(command) {
645                                 var list = dom.getParent(selection.getNode(), 'ul,ol');
646
647                                 return list &&
648                                         (
649                                                 command === 'insertunorderedlist' && list.tagName === 'UL' ||
650                                                 command === 'insertorderedlist' && list.tagName === 'OL'
651                                         );
652                         }
653                 }, 'state');
654
655                 // Add queryCommandValue overrides
656                 addCommands({
657                         'FontSize,FontName': function(command) {
658                                 var value = 0, parent;
659
660                                 if ((parent = dom.getParent(selection.getNode(), 'span'))) {
661                                         if (command == 'fontsize') {
662                                                 value = parent.style.fontSize;
663                                         } else {
664                                                 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
665                                         }
666                                 }
667
668                                 return value;
669                         }
670                 }, 'value');
671
672                 // Add undo manager logic
673                 addCommands({
674                         Undo: function() {
675                                 editor.undoManager.undo();
676                         },
677
678                         Redo: function() {
679                                 editor.undoManager.redo();
680                         }
681                 });
682         };
683 });