Nation Notes module contributed by Z&H Healthcare.
[openemr.git] / library / custom_template / ckeditor / _source / plugins / tableresize / plugin.js
blob22c8e6ae9309005a974e4a3e63fc97685d6c38dc
1 /*
2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
4 */
6 (function()
8         var pxUnit = CKEDITOR.tools.cssLength,
9                 needsIEHacks = CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.quirks || CKEDITOR.env.version < 7 );
11         function getWidth( el )
12         {
13                 return CKEDITOR.env.ie ? el.$.clientWidth : parseInt( el.getComputedStyle( 'width' ), 10 );
14         }
16         function getBorderWidth( element, side )
17         {
18                 var computed = element.getComputedStyle( 'border-' + side + '-width' ),
19                         borderMap =
20                         {
21                                 thin: '0px',
22                                 medium: '1px',
23                                 thick: '2px'
24                         };
26                 if ( computed.indexOf( 'px' ) < 0 )
27                 {
28                         // look up keywords
29                         if ( computed in borderMap && element.getComputedStyle( 'border-style' ) != 'none' )
30                                 computed = borderMap[ computed ];
31                         else
32                                 computed = 0;
33                 }
35                 return parseInt( computed, 10 );
36         }
38         // Gets the table row that contains the most columns.
39         function getMasterPillarRow( table )
40         {
41                 var $rows = table.$.rows,
42                         maxCells = 0, cellsCount,
43                         $elected, $tr;
45                 for ( var i = 0, len = $rows.length ; i < len; i++ )
46                 {
47                         $tr = $rows[ i ];
48                         cellsCount = $tr.cells.length;
50                         if ( cellsCount > maxCells )
51                         {
52                                 maxCells = cellsCount;
53                                 $elected = $tr;
54                         }
55                 }
57                 return $elected;
58         }
60         function buildTableColumnPillars( table )
61         {
62                 var pillars = [],
63                         pillarIndex = -1,
64                         rtl = ( table.getComputedStyle( 'direction' ) == 'rtl' );
66                 // Get the raw row element that cointains the most columns.
67                 var $tr = getMasterPillarRow( table );
69                 // Get the tbody element and position, which will be used to set the
70                 // top and bottom boundaries.
71                 var tbody = new CKEDITOR.dom.element( table.$.tBodies[ 0 ] ),
72                         tbodyPosition = tbody.getDocumentPosition();
74                 // Loop thorugh all cells, building pillars after each one of them.
75                 for ( var i = 0, len = $tr.cells.length ; i < len ; i++ )
76                 {
77                         // Both the current cell and the successive one will be used in the
78                         // pillar size calculation.
79                         var td = new CKEDITOR.dom.element( $tr.cells[ i ] ),
80                                 nextTd = $tr.cells[ i + 1 ] && new CKEDITOR.dom.element( $tr.cells[ i + 1 ] );
82                         pillarIndex += td.$.colSpan || 1;
84                         // Calculate the pillar boundary positions.
85                         var pillarLeft, pillarRight, pillarWidth, pillarPadding;
87                         var x = td.getDocumentPosition().x;
89                         // Calculate positions based on the current cell.
90                         rtl ?
91                                 pillarRight = x + getBorderWidth( td, 'left' ) :
92                                 pillarLeft  = x + td.$.offsetWidth - getBorderWidth( td, 'right' );
94                         // Calculate positions based on the next cell, if available.
95                         if ( nextTd )
96                         {
97                                 x =  nextTd.getDocumentPosition().x;
99                                 rtl ?
100                                         pillarLeft      = x + nextTd.$.offsetWidth - getBorderWidth( nextTd, 'right' ) :
101                                         pillarRight     = x + getBorderWidth( nextTd, 'left' );
102                         }
103                         // Otherwise calculate positions based on the table (for last cell).
104                         else
105                         {
106                                 x =  table.getDocumentPosition().x;
108                                 rtl ?
109                                         pillarLeft      = x :
110                                         pillarRight     = x + table.$.offsetWidth;
111                         }
113                         pillarWidth = Math.max( pillarRight - pillarLeft, 3 );
115                         // Make the pillar touch area at least 14 pixels wide, for easy to use.
116                         pillarPadding = Math.max( Math.round( 7 - ( pillarWidth / 2 ) ), 0 );
118                         // The pillar should reflects exactly the shape of the hovered
119                         // column border line.
120                         pillars.push( {
121                                 table : table,
122                                 index : pillarIndex,
123                                 x : pillarLeft,
124                                 y : tbodyPosition.y,
125                                 width : pillarWidth,
126                                 height: tbody.$.offsetHeight,
127                                 padding : pillarPadding,
128                                 rtl : rtl } );
129                 }
131                 return pillars;
132         }
134         function getPillarAtPosition( pillars, positionX )
135         {
136                 for ( var i = 0, len = pillars.length ; i < len ; i++ )
137                 {
138                         var pillar = pillars[ i ],
139                                 pad = pillar.padding;
141                         if ( positionX >= pillar.x - pad && positionX <= ( pillar.x + pillar.width + pad ) )
142                                 return pillar;
143                 }
145                 return null;
146         }
148         function cancel( evt )
149         {
150                 ( evt.data || evt ).preventDefault();
151         }
153         function columnResizer( editor )
154         {
155                 var pillar,
156                         document,
157                         resizer,
158                         isResizing,
159                         startOffset,
160                         currentShift;
162                 var leftSideCells, rightSideCells, leftShiftBoundary, rightShiftBoundary;
164                 function detach()
165                 {
166                         pillar = null;
167                         currentShift = 0;
168                         isResizing = 0;
170                         document.removeListener( 'mouseup', onMouseUp );
171                         resizer.removeListener( 'mousedown', onMouseDown );
172                         resizer.removeListener( 'mousemove', onMouseMove );
174                         document.getBody().setStyle( 'cursor', 'auto' );
176                         // Hide the resizer (remove it on IE7 - #5890).
177                         needsIEHacks ? resizer.remove() : resizer.hide();
178                 }
180                 function resizeStart()
181                 {
182                         // Before starting to resize, figure out which cells to change
183                         // and the boundaries of this resizing shift.
185                         var columnIndex = pillar.index,
186                                 map = CKEDITOR.tools.buildTableMap( pillar.table ),
187                                 leftColumnCells = [],
188                                 rightColumnCells = [],
189                                 leftMinSize = Number.MAX_VALUE,
190                                 rightMinSize = leftMinSize,
191                                 rtl = pillar.rtl;
193                         for ( var i = 0, len = map.length ; i < len ; i++ )
194                         {
195                                 var row                 = map[ i ],
196                                         leftCell        = row[ columnIndex + ( rtl ? 1 : 0 ) ],
197                                         rightCell       = row[ columnIndex + ( rtl ? 0 : 1 ) ];
199                                 leftCell        = leftCell && new CKEDITOR.dom.element( leftCell );
200                                 rightCell       = rightCell && new CKEDITOR.dom.element( rightCell );
202                                 if ( !leftCell || !rightCell || !leftCell.equals( rightCell ) )
203                                 {
204                                         leftCell && ( leftMinSize = Math.min( leftMinSize, getWidth( leftCell ) ) );
205                                         rightCell && ( rightMinSize = Math.min( rightMinSize, getWidth( rightCell ) ) );
207                                         leftColumnCells.push( leftCell );
208                                         rightColumnCells.push( rightCell );
209                                 }
210                         }
212                         // Cache the list of cells to be resized.
213                         leftSideCells = leftColumnCells;
214                         rightSideCells = rightColumnCells;
216                         // Cache the resize limit boundaries.
217                         leftShiftBoundary =  pillar.x - leftMinSize;
218                         rightShiftBoundary = pillar.x + rightMinSize;
220                         resizer.setOpacity( 0.5 );
221                         startOffset = parseInt( resizer.getStyle( 'left' ), 10 );
222                         currentShift = 0;
223                         isResizing = 1;
225                         resizer.on( 'mousemove', onMouseMove );
227                         // Prevent the native drag behavior otherwise 'mousemove' won't fire.
228                         document.on( 'dragstart', cancel );
229                 }
231                 function resizeEnd()
232                 {
233                         isResizing = 0;
235                         resizer.setOpacity( 0 );
237                         currentShift && resizeColumn();
239                         var table = pillar.table;
240                         setTimeout( function () { table.removeCustomData( '_cke_table_pillars' ); }, 0 );
242                         document.removeListener( 'dragstart', cancel );
243                 }
245                 function resizeColumn()
246                 {
247                         var rtl = pillar.rtl,
248                                 cellsCount = rtl ? rightSideCells.length : leftSideCells.length;
250                         // Perform the actual resize to table cells, only for those by side of the pillar.
251                         for ( var i = 0 ; i < cellsCount ; i++ )
252                         {
253                                 var leftCell = leftSideCells[ i ],
254                                         rightCell = rightSideCells[ i ],
255                                         table = pillar.table;
257                                 // Defer the resizing to avoid any interference among cells.
258                                 CKEDITOR.tools.setTimeout(
259                                         function( leftCell, leftOldWidth, rightCell, rightOldWidth, tableWidth, sizeShift )
260                                         {
261                                                 leftCell && leftCell.setStyle( 'width', pxUnit( Math.max( leftOldWidth + sizeShift, 0 ) ) );
262                                                 rightCell && rightCell.setStyle( 'width', pxUnit( Math.max( rightOldWidth - sizeShift, 0 ) ) );
264                                                 // If we're in the last cell, we need to resize the table as well
265                                                 if ( tableWidth )
266                                                         table.setStyle( 'width', pxUnit( tableWidth + sizeShift * ( rtl ? -1 : 1 ) ) );
267                                         }
268                                         , 0,
269                                         this, [
270                                                 leftCell, leftCell && getWidth( leftCell ),
271                                                 rightCell, rightCell && getWidth( rightCell ),
272                                                 ( !leftCell || !rightCell ) && ( getWidth( table ) + getBorderWidth( table, 'left' ) + getBorderWidth( table, 'right' ) ),
273                                                 currentShift ] );
274                         }
275                 }
277                 function onMouseDown( evt )
278                 {
279                         cancel( evt );
281                         resizeStart();
283                         document.on( 'mouseup', onMouseUp, this );
284                 }
286                 function onMouseUp( evt )
287                 {
288                         evt.removeListener();
290                         resizeEnd();
291                 }
293                 function onMouseMove( evt )
294                 {
295                         move( evt.data.$.clientX );
296                 }
298                 document = editor.document;
300                 resizer = CKEDITOR.dom.element.createFromHtml(
301                         '<div data-cke-temp=1 contenteditable=false unselectable=on '+
302                         'style="position:absolute;cursor:col-resize;filter:alpha(opacity=0);opacity:0;' +
303                                 'padding:0;background-color:#004;background-image:none;border:0px none;z-index:10"></div>', document );
305                 // Except on IE6/7 (#5890), place the resizer after body to prevent it
306                 // from being editable.
307                 if ( !needsIEHacks )
308                         document.getDocumentElement().append( resizer );
310                 this.attachTo = function( targetPillar )
311                 {
312                         // Accept only one pillar at a time.
313                         if ( isResizing )
314                                 return;
316                         // On IE6/7, we append the resizer everytime we need it. (#5890)
317                         if ( needsIEHacks )
318                         {
319                                 document.getBody().append( resizer );
320                                 currentShift = 0;
321                         }
323                         pillar = targetPillar;
325                         resizer.setStyles(
326                                 {
327                                         width: pxUnit( targetPillar.width ),
328                                         height : pxUnit( targetPillar.height ),
329                                         left : pxUnit( targetPillar.x ),
330                                         top : pxUnit( targetPillar.y )
331                                 });
333                         // In IE6/7, it's not possible to have custom cursors for floating
334                         // elements in an editable document. Show the resizer in that case,
335                         // to give the user a visual clue.
336                         needsIEHacks && resizer.setOpacity( 0.25 );
338                         resizer.on( 'mousedown', onMouseDown, this );
340                         document.getBody().setStyle( 'cursor', 'col-resize' );
342                         // Display the resizer to receive events but don't show it,
343                         // only change the cursor to resizable shape.
344                         resizer.show();
345                 };
347                 var move = this.move = function( posX )
348                 {
349                         if ( !pillar )
350                                 return 0;
352                         var pad = pillar.padding;
354                         if ( !isResizing && ( posX < pillar.x - pad || posX > ( pillar.x + pillar.width + pad ) ) )
355                         {
356                                 detach();
357                                 return 0;
358                         }
360                         var resizerNewPosition = posX - Math.round( resizer.$.offsetWidth / 2 );
362                         if ( isResizing )
363                         {
364                                 if ( resizerNewPosition == leftShiftBoundary || resizerNewPosition == rightShiftBoundary )
365                                         return 1;
367                                 resizerNewPosition = Math.max( resizerNewPosition, leftShiftBoundary );
368                                 resizerNewPosition = Math.min( resizerNewPosition, rightShiftBoundary );
370                                 currentShift = resizerNewPosition - startOffset;
371                         }
373                         resizer.setStyle( 'left', pxUnit( resizerNewPosition ) );
375                         return 1;
376                 };
377         }
379         function clearPillarsCache( evt )
380         {
381                 var target = evt.data.getTarget();
383                 if ( evt.name == 'mouseout' )
384                 {
385                         // Bypass interal mouse move.
386                         if ( !target.is ( 'table' ) )
387                                 return;
389                         var dest = new CKEDITOR.dom.element( evt.data.$.relatedTarget || evt.data.$.toElement );
390                         while( dest && dest.$ && !dest.equals( target ) && !dest.is( 'body' ) )
391                                 dest = dest.getParent();
392                         if ( !dest || dest.equals( target ) )
393                                 return;
394                 }
396                 target.getAscendant( 'table', 1 ).removeCustomData( '_cke_table_pillars' );
397                 evt.removeListener();
398         }
400         CKEDITOR.plugins.add( 'tableresize',
401         {
402                 requires : [ 'tabletools' ],
403                 init : function( editor )
404                 {
405                         editor.on( 'contentDom', function()
406                         {
407                                 var resizer;
409                                 editor.document.getBody().on( 'mousemove', function( evt )
410                                         {
411                                                 evt = evt.data;
413                                                 // If we're already attached to a pillar, simply move the
414                                                 // resizer.
415                                                 if ( resizer && resizer.move( evt.$.clientX ) )
416                                                 {
417                                                         cancel( evt );
418                                                         return;
419                                                 }
421                                                 // Considering table, tr, td, tbody but nothing else.
422                                                 var target = evt.getTarget(),
423                                                         table,
424                                                         pillars;
426                                                 if ( !target.is( 'table' ) && !target.getAscendant( 'tbody', 1 ) )
427                                                         return;
429                                                 table = target.getAscendant( 'table', 1 );
431                                                 if ( !( pillars = table.getCustomData( '_cke_table_pillars' ) ) )
432                                                 {
433                                                         // Cache table pillars calculation result.
434                                                         table.setCustomData( '_cke_table_pillars', ( pillars = buildTableColumnPillars( table ) ) );
435                                                         table.on( 'mouseout', clearPillarsCache );
436                                                         table.on( 'mousedown', clearPillarsCache );
437                                                 }
439                                                 var pillar = getPillarAtPosition( pillars, evt.$.clientX );
440                                                 if ( pillar )
441                                                 {
442                                                         !resizer && ( resizer = new columnResizer( editor ) );
443                                                         resizer.attachTo( pillar );
444                                                 }
445                                         });
446                         });
447                 }
448         });
450 })();