mediawiki-extensions-Visual.../modules/ve/ce/ve.ce.FocusableNode.js
Rob Moen c472b2fe4a Make local overlays local to surface and remove insane z-indexes
ve.ui.Surface.js
* Make local overlay a child of ve-ui-surface and a sibling to
  ve-ce-surface elements.
** This keeps local overlays relative to their surface and eliminates the
   need for insane z-indexes.

ve.ui.PopupWidget.js
* PopupWidget boundaries are now relative to ve-ce-surface and no longer
  protrude out

ve.ce.Node.css, ve.ui.Window.css
* Removal or replacement of insane z-indexes.

ve.ce.FocusableNode.js, ve.ce.ProtectedNode.js, ve.ce.ResizableNode.js,
ve.ui.Context.js
* Translate offsets from local overlay

ve.init.mw.ViewPageTarget-monobook.css,
ve.init.mw.ViewPageTarget-vector.css
* Skin specific z-indexes for global overlay

ve.init.mw.ViewPageTarget.js
* Applied direction specific mw class to ce.Surface vs ui.Surface to
  prevent mw content styles from being applied to ui elements.

ve.ui.Dialog.css
* Adjustments to surface inside of dialog so that relative offsets for
  local overlays can be properly calculated.

ve.ui.Surface.css
* Explicitly force .ve-ui-surface to be relative so that it's children can
  be relatively positioned.

ve.ui.Widget.css
* Removal of unnecessary font-size properties now that local overlay is
  sibling of surface.

ve.js
* Added get relative position helper method to translate position offsets
  from target parent

Bug: 50241
Change-Id: Ibadce404a2286bc5dcec48f0d9da89004dbbd867
2013-07-02 19:35:43 +00:00

196 lines
4.1 KiB
JavaScript

/*!
* VisualEditor ContentEditable FocusableNode class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* ContentEditable resizable node.
*
* Focusable elements have a special treatment by ve.ce.Surface. When the user selects only a single
* node, if it is focusable, the surface will set the focusable node's focused state. Other systems,
* such as the context, may also use a focusable node's $focusable property as a hint of where the
* primary element in the node is. Typically, and by default, the primary element is the root
* element, but in some cases it may need to be configured to be a specific child element within the
* node's DOM rendering.
*
* If your focusable node changes size and the highlight must be redrawn, call redrawHighlight().
* 'resize' and 'rerender' are already bound to call this.
*
* @class
* @abstract
*
* @constructor
* @param {jQuery} [$focusable] Primary element user is focusing on
*/
ve.ce.FocusableNode = function VeCeFocusableNode( $focusable ) {
// Properties
this.focused = false;
this.$focusable = $focusable || this.$;
this.$highlights = $( [] );
this.surface = null;
// Events
this.connect( this, {
'setup': 'onFocusableSetup',
'resize': 'onFocusableResize',
'rerender': 'onFocusableRerender',
'live': 'onFocusableLive'
} );
};
/* Events */
/**
* @event focus
*/
/**
* @event blur
*/
/* Methods */
/**
* Handle setup event.
*
* @method
*/
ve.ce.FocusableNode.prototype.onFocusableSetup = function () {
this.surface = this.root.getSurface();
};
/**
* Handle node live.
*
* @method
*/
ve.ce.FocusableNode.prototype.onFocusableLive = function () {
var surfaceModel = this.root.getSurface().getModel();
if ( this.live ) {
surfaceModel.connect( this, { 'history': 'onFocusableHistory' } );
} else {
surfaceModel.disconnect( this, { 'history': 'onFocusableHistory' } );
}
};
/**
* Handle history event.
*
* @method
*/
ve.ce.FocusableNode.prototype.onFocusableHistory = function () {
if ( this.focused ) {
this.redrawHighlight();
}
};
/**
* Handle resize event.
*
* @method
*/
ve.ce.FocusableNode.prototype.onFocusableResize = function () {
if ( this.focused ) {
this.redrawHighlight();
}
};
/**
* Handle rerender event.
*
* @method
*/
ve.ce.FocusableNode.prototype.onFocusableRerender = function () {
if ( this.focused ) {
this.redrawHighlight();
}
};
/**
* Check if node is focused.
*
* @method
* @returns {boolean} Node is focused
*/
ve.ce.FocusableNode.prototype.isFocused = function () {
return this.focused;
};
/**
* Set the selected state of the node.
*
* @method
* @param {boolean} value Node is focused
* @emits focus
* @emits blur
*/
ve.ce.FocusableNode.prototype.setFocused = function ( value ) {
value = !!value;
if ( this.focused !== value ) {
this.focused = value;
if ( this.focused ) {
this.emit( 'focus' );
this.$.addClass( 've-ce-node-focused' );
this.createHighlight();
} else {
this.emit( 'blur' );
this.$.removeClass( 've-ce-node-focused' );
this.clearHighlight();
}
}
};
/**
* Creates highlight.
*
* @method
*/
ve.ce.FocusableNode.prototype.createHighlight = function () {
this.$.find( '*' ).add( this.$ ).each(
ve.bind( function( i, element ) {
var offset, $element = $( element );
if ( $element.not( ':visible' ) ) {
return true;
}
offset = ve.Element.getRelativePosition(
$element, this.getRoot().getSurface().getSurface().$
);
this.$highlights = this.$highlights.add(
$( '<div>' )
.css( {
height: $element.height(),
width: $element.width(),
top: offset.top,
left: offset.left
} )
.addClass( 've-ce-focusableNode-highlight' )
);
}, this )
);
this.surface.replaceHighlight( this.$highlights );
};
/**
* Clears highlight.
*
* @method
*/
ve.ce.FocusableNode.prototype.clearHighlight = function () {
this.$highlights = $( [] );
this.surface.replaceHighlight( null );
};
/**
* Redraws highlight.
*
* @method
*/
ve.ce.FocusableNode.prototype.redrawHighlight = function () {
this.clearHighlight();
this.createHighlight();
};