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 | } |