| 21 | | /** |
| 22 | | * Registers a function to be called whenever a style changes its state in the |
| 23 | | * editing area. The current state is passed to the function. The possible |
| 24 | | * states are {@link CKEDITOR.TRISTATE_ON} and {@link CKEDITOR.TRISTATE_OFF}. |
| | 21 | CKEDITOR.tools.extend( CKEDITOR.editor.prototype, |
| | 22 | { |
| | 23 | /** |
| | 24 | * Registers a function to be called whenever a style changes its state in the |
| | 25 | * editing area. The current state is passed to the function. The possible |
| | 26 | * states are {@link CKEDITOR.TRISTATE_ON} and {@link CKEDITOR.TRISTATE_OFF}. |
| 27 | | * @example |
| 28 | | * // Create a style object for the <b> element. |
| 29 | | * var style = new CKEDITOR.style( { element : 'b' } ); |
| 30 | | * var editor = CKEDITOR.instances.editor1; |
| 31 | | * editor.attachStyleStateChange( style, function( state ) |
| 32 | | * { |
| 33 | | * if ( state == CKEDITOR.TRISTATE_ON ) |
| 34 | | * alert( 'The current state for the B element is ON' ); |
| 35 | | * else |
| 36 | | * alert( 'The current state for the B element is OFF' ); |
| 37 | | * }); |
| 38 | | */ |
| 39 | | CKEDITOR.editor.prototype.attachStyleStateChange = function( style, callback ) |
| 40 | | { |
| 41 | | // Try to get the list of attached callbacks. |
| 42 | | var styleStateChangeCallbacks = this._.styleStateChangeCallbacks; |
| | 29 | * @example |
| | 30 | * // Create a style object for the <b> element. |
| | 31 | * var style = new CKEDITOR.style( { element : 'b' } ); |
| | 32 | * var editor = CKEDITOR.instances.editor1; |
| | 33 | * editor.attachStyleStateChange( style, function( state ) |
| | 34 | * { |
| | 35 | * if ( state == CKEDITOR.TRISTATE_ON ) |
| | 36 | * alert( 'The current state for the B element is ON' ); |
| | 37 | * else |
| | 38 | * alert( 'The current state for the B element is OFF' ); |
| | 39 | * }); |
| | 40 | */ |
| | 41 | attachStyleStateChange : function( style, callback ) |
| | 42 | { |
| | 43 | // Try to get the list of attached callbacks. |
| | 44 | var styleStateChangeCallbacks = this._.styleStateChangeCallbacks; |
| 44 | | // If it doesn't exist, it means this is the first call. So, let's create |
| 45 | | // all the structure to manage the style checks and the callback calls. |
| 46 | | if ( !styleStateChangeCallbacks ) |
| 47 | | { |
| 48 | | // Create the callbacks array. |
| 49 | | styleStateChangeCallbacks = this._.styleStateChangeCallbacks = []; |
| | 46 | // If it doesn't exist, it means this is the first call. So, let's create |
| | 47 | // all the structure to manage the style checks and the callback calls. |
| | 48 | if ( !styleStateChangeCallbacks ) |
| | 49 | { |
| | 50 | // Create the callbacks array. |
| | 51 | styleStateChangeCallbacks = this._.styleStateChangeCallbacks = []; |
| 51 | | // Attach to the selectionChange event, so we can check the styles at |
| 52 | | // that point. |
| 53 | | this.on( 'selectionChange', function( ev ) |
| 54 | | { |
| 55 | | // Loop throw all registered callbacks. |
| 56 | | for ( var i = 0 ; i < styleStateChangeCallbacks.length ; i++ ) |
| 57 | | { |
| 58 | | var callback = styleStateChangeCallbacks[ i ]; |
| | 53 | // Attach to the selectionChange event, so we can check the styles at |
| | 54 | // that point. |
| | 55 | this.on( 'selectionChange', function( ev ) |
| | 56 | { |
| | 57 | // Loop throw all registered callbacks. |
| | 58 | for ( var i = 0 ; i < styleStateChangeCallbacks.length ; i++ ) |
| | 59 | { |
| | 60 | var callback = styleStateChangeCallbacks[ i ]; |
| 64 | | // If the state changed since the last check. |
| 65 | | if ( callback.state !== currentState ) |
| 66 | | { |
| 67 | | // Call the callback function, passing the current |
| 68 | | // state to it. |
| 69 | | callback.fn.call( this, currentState ); |
| | 66 | // If the state changed since the last check. |
| | 67 | if ( callback.state !== currentState ) |
| | 68 | { |
| | 69 | // Call the callback function, passing the current |
| | 70 | // state to it. |
| | 71 | callback.fn.call( this, currentState ); |
| | 86 | fireStyleStateChange : function( style, state ) |
| | 87 | { |
| | 88 | var callbacks = this._.styleStateChangeCallbacks; |
| | 89 | |
| | 90 | for ( var i = 0 ; i < callbacks.length ; i++ ) |
| | 91 | { |
| | 92 | var callback = callbacks[ i ]; |
| | 93 | if ( callback.style == style && |
| | 94 | callback.state !== state ) |
| | 95 | { |
| | 96 | callback.fn.call( this, state ); |
| | 97 | callback.state = state; |
| | 98 | } |
| | 99 | } |
| | 100 | }, |
| | 101 | |
| | 102 | pendStyle : function ( style, type ) |
| | 103 | { |
| | 104 | var pendings = this._.pendingStyles || ( this._.pendingStyles = [] ), |
| | 105 | length = pendings.length, |
| | 106 | pendingStyle, |
| | 107 | remove = -1, |
| | 108 | duplicate = -1; |
| | 109 | |
| | 110 | for ( var i = 0; i < length; i++ ) |
| | 111 | { |
| | 112 | pendingStyle = pendings[ i ]; |
| | 113 | if( pendingStyle.style == style ) |
| | 114 | { |
| | 115 | if( pendingStyle.type != type ) |
| | 116 | remove = i; |
| | 117 | else |
| | 118 | duplicate = i; |
| | 119 | } |
| | 120 | } |
| | 121 | |
| | 122 | if( duplicate == -1 ) |
| | 123 | { |
| | 124 | if( remove != -1 ) |
| | 125 | pendings.splice( remove, 1 ); |
| | 126 | else |
| | 127 | pendings.push( { style : style, type : type } ); |
| | 128 | } |
| | 129 | |
| | 130 | this.fireStyleStateChange( style, type ); |
| | 131 | return pendings.length; |
| | 132 | }, |
| | 133 | |
| | 134 | applyPending : function ( range ) |
| | 135 | { |
| | 136 | var pendings = this._.pendingStyles, |
| | 137 | length = pendings && pendings.length, |
| | 138 | pendingStyle; |
| | 139 | |
| | 140 | for ( var i = 0; i < length; i++ ) |
| | 141 | { |
| | 142 | pendingStyle = pendings[ i ]; |
| | 143 | range ? pendingStyle.style[ ( pendingStyle.type == CKEDITOR.TRISTATE_ON ? 'applyTo' : 'removeFrom' ) + 'Range']( range) |
| | 144 | : pendingStyle.style[ pendingStyle.type == CKEDITOR.TRISTATE_ON ? 'apply' : 'remove']( this.document ); |
| | 145 | } |
| | 146 | |
| | 147 | this._.pendingStyles = []; |
| | 148 | }, |
| | 149 | |
| | 150 | removePending : function () |
| | 151 | { |
| | 152 | var pendings = this._.pendingStyles, |
| | 153 | length = pendings && pendings.length, |
| | 154 | pendingStyle; |
| | 155 | |
| | 156 | for ( var i = 0; i < length; i++ ) |
| | 157 | { |
| | 158 | pendingStyle = pendings[ i ]; |
| | 159 | this.fireStyleStateChange( pendingStyle.style, CKEDITOR.TRISTATE_ON ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_ON ); |
| | 160 | } |
| | 161 | |
| | 162 | this._.pendingStyles = []; |
| | 163 | } |
| | 164 | |
| | 165 | } ); |
| | 166 | |
| | 379 | pendApply : function( isRemove ) |
| | 380 | { |
| | 381 | var editor = CKEDITOR.currentInstance; |
| | 382 | if( editor && editor.pendStyle( |
| | 383 | this, CKEDITOR[ isRemove ? 'TRISTATE_OFF' : 'TRISTATE_ON' ] ) ) |
| | 384 | { |
| | 385 | var doc = editor.document; |
| | 386 | doc.$.execCommand( 'FontName', false, '_cke_style' ); |
| | 387 | doc.on( 'DOMNodeInserted', onDomChangeDetectPending, editor ); |
| | 388 | |
| | 389 | var sel = editor.getSelection(), |
| | 390 | nativeSel = sel.getNative(), |
| | 391 | image = { anchorNode : nativeSel.anchorNode , |
| | 392 | anchorOffset : nativeSel.anchorOffset }; |
| | 393 | |
| | 394 | function checkRemovePending( evt ) |
| | 395 | { |
| | 396 | if( evt.name == 'blur' |
| | 397 | || nativeSel.anchorNode != image.anchorNode |
| | 398 | || nativeSel.anchorOffset != image.anchorOffset ) |
| | 399 | { |
| | 400 | evt.removeListener(); |
| | 401 | editor.removePending(); |
| | 402 | doc.removeListener( 'DOMNodeInserted', onDomChangeDetectPending ); |
| | 403 | } |
| | 404 | } |
| | 405 | |
| | 406 | doc.on( 'mouseup', checkRemovePending ); |
| | 407 | doc.on( 'keyup', checkRemovePending ); |
| | 408 | editor.on( 'blur', checkRemovePending ); |
| | 409 | editor.on( 'contentDomUnload', checkRemovePending ); |
| | 410 | } |
| | 411 | }, |
| | 412 | |
| | 413 | pendRemove : function() |
| | 414 | { |
| | 415 | this.pendApply( true ); |
| | 416 | }, |
| | 417 | |
| | 1621 | |
| | 1622 | function onDomChangeDetectPending( evt ) |
| | 1623 | { |
| | 1624 | var target = evt.data.getTarget(), |
| | 1625 | parent = target.type == CKEDITOR.NODE_TEXT && target.getParent(), |
| | 1626 | styleMarker = parent && parent.$.face == '_cke_style' ? parent : null; |
| | 1627 | |
| | 1628 | if ( styleMarker ) |
| | 1629 | { |
| | 1630 | var range = new CKEDITOR.dom.range( this.document ); |
| | 1631 | range.selectNodeContents( styleMarker ); |
| | 1632 | |
| | 1633 | // Remove Apple-style-span marker; |
| | 1634 | var bookmark = range.createBookmark(); |
| | 1635 | styleMarker.remove( true ); |
| | 1636 | range.moveToBookmark( bookmark ); |
| | 1637 | |
| | 1638 | // Apply pending styles to the marked range. |
| | 1639 | this.applyPending( range ); |
| | 1640 | |
| | 1641 | range.collapse(); |
| | 1642 | range.select(); |
| | 1643 | } |
| | 1644 | } |