/*!
- * OOjs UI v0.12.12
+ * OOjs UI v0.13.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2015 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2015-10-13T20:38:18Z
+ * Date: 2015-10-27T17:52:51Z
*/
( function ( OO ) {
}
}
} );
+ // pick up dynamic state, like focus, value of form inputs, scroll position, etc.
+ state = cls.static.gatherPreInfuseState( $elem, data );
// jscs:disable requireCapitalizedConstructors
obj = new cls( data ); // rebuild widget
- // pick up dynamic state, like focus, value of form inputs, scroll position, etc.
- state = obj.gatherPreInfuseState( $elem );
// now replace old DOM with this new DOM.
if ( top ) {
$elem.replaceWith( obj.$element );
return obj;
};
+/**
+ * Gather the dynamic state (focus, value of form inputs, scroll position, etc.) of a HTML DOM node
+ * (and its children) that represent an Element of the same class and the given configuration,
+ * generated by the PHP implementation.
+ *
+ * This method is called just before `node` is detached from the DOM. The return value of this
+ * function will be passed to #restorePreInfuseState after the newly created widget's #$element
+ * is inserted into DOM to replace `node`.
+ *
+ * @protected
+ * @param {HTMLElement} node
+ * @param {Object} config
+ * @return {Object}
+ */
+OO.ui.Element.static.gatherPreInfuseState = function () {
+ return {};
+};
+
/**
* Get a jQuery function within a specific document.
*
return OO.ui.Element.static.scrollIntoView( this.$element[ 0 ], config );
};
-/**
- * Gather the dynamic state (focus, value of form inputs, scroll position, etc.) of a HTML DOM node
- * (and its children) that represent an Element of the same type and configuration as the current
- * one, generated by the PHP implementation.
- *
- * This method is called just before `node` is detached from the DOM. The return value of this
- * function will be passed to #restorePreInfuseState after this widget's #$element is inserted into
- * DOM to replace `node`.
- *
- * @protected
- * @param {HTMLElement} node
- * @return {Object}
- */
-OO.ui.Element.prototype.gatherPreInfuseState = function () {
- return {};
-};
-
/**
* Restore the pre-infusion dynamic state for this widget.
*
this.stackLayout.connect( this, { set: 'onStackLayoutSet' } );
if ( this.outlined ) {
this.outlineSelectWidget.connect( this, { select: 'onOutlineSelectWidgetSelect' } );
+ this.scrolling = false;
+ this.stackLayout.connect( this, { visibleItemChange: 'onStackLayoutVisibleItemChange' } );
}
if ( this.autoFocus ) {
// Event 'focus' does not bubble, but 'focusin' does
}
};
+/**
+ * Handle visibleItemChange events from the stackLayout
+ *
+ * The next visible page is set as the current page by selecting it
+ * in the outline
+ *
+ * @param {OO.ui.PageLayout} page The next visible page in the layout
+ */
+OO.ui.BookletLayout.prototype.onStackLayoutVisibleItemChange = function ( page ) {
+ // Set a flag to so that the resulting call to #onStackLayoutSet doesn't
+ // try and scroll the item into view again.
+ this.scrolling = true;
+ this.outlineSelectWidget.selectItemByData( page.getName() );
+ this.scrolling = false;
+};
+
/**
* Handle stack layout set events.
*
*/
OO.ui.BookletLayout.prototype.onStackLayoutSet = function ( page ) {
var layout = this;
- if ( page ) {
+ if ( !this.scrolling && page ) {
page.scrollElementIntoView( { complete: function () {
if ( layout.autoFocus ) {
layout.focus();
this.$element.addClass( 'oo-ui-stackLayout' );
if ( this.continuous ) {
this.$element.addClass( 'oo-ui-stackLayout-continuous' );
+ this.$element.on( 'scroll', OO.ui.debounce( this.onScroll.bind( this ), 250 ) );
}
if ( Array.isArray( config.items ) ) {
this.addItems( config.items );
* @param {OO.ui.Layout|null} item Current panel or `null` if no panel is shown
*/
+/**
+ * When used in continuous mode, this event is emitted when the user scrolls down
+ * far enough such that currentItem is no longer visible.
+ *
+ * @event visibleItemChange
+ * @param {OO.ui.PanelLayout} panel The next visible item in the layout
+ */
+
/* Methods */
+/**
+ * Handle scroll events from the layout element
+ *
+ * @param {jQuery.Event} e
+ * @fires visibleItemChange
+ */
+OO.ui.StackLayout.prototype.onScroll = function () {
+ var currentRect,
+ len = this.items.length,
+ currentIndex = this.items.indexOf( this.currentItem ),
+ newIndex = currentIndex,
+ containerRect = this.$element[ 0 ].getBoundingClientRect();
+
+ if ( !containerRect || ( !containerRect.top && !containerRect.bottom ) ) {
+ // Can't get bounding rect, possibly not attached.
+ return;
+ }
+
+ function getRect( item ) {
+ return item.$element[ 0 ].getBoundingClientRect();
+ }
+
+ function isVisible( item ) {
+ var rect = getRect( item );
+ return rect.bottom > containerRect.top && rect.top < containerRect.bottom;
+ }
+
+ currentRect = getRect( this.currentItem );
+
+ if ( currentRect.bottom < containerRect.top ) {
+ // Scrolled down past current item
+ while ( ++newIndex < len ) {
+ if ( isVisible( this.items[ newIndex ] ) ) {
+ break;
+ }
+ }
+ } else if ( currentRect.top > containerRect.bottom ) {
+ // Scrolled up past current item
+ while ( --newIndex >= 0 ) {
+ if ( isVisible( this.items[ newIndex ] ) ) {
+ break;
+ }
+ }
+ }
+
+ if ( newIndex !== currentIndex ) {
+ this.emit( 'visibleItemChange', this.items[ newIndex ] );
+ }
+};
+
/**
* Get the current panel.
*
OO.ui.InputWidget.static.supportsSimpleLabel = true;
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.InputWidget.static.gatherPreInfuseState = function ( node, config ) {
+ var
+ state = OO.ui.InputWidget.parent.static.gatherPreInfuseState( node, config ),
+ $input = state.$input || $( node ).find( '.oo-ui-inputWidget-input' );
+ state.value = $input.val();
+ // Might be better in TabIndexedElement, but it's awkward to do there because mixins are awkward
+ state.focus = $input.is( ':focus' );
+ return state;
+};
+
/* Events */
/**
return this;
};
-/**
- * @inheritdoc
- */
-OO.ui.InputWidget.prototype.gatherPreInfuseState = function ( node ) {
- var
- state = OO.ui.InputWidget.parent.prototype.gatherPreInfuseState.call( this, node ),
- $input = state.$input || $( node ).find( '.oo-ui-inputWidget-input' );
- state.value = $input.val();
- // Might be better in TabIndexedElement, but it's awkward to do there because mixins are awkward
- state.focus = $input.is( ':focus' );
- return state;
-};
-
/**
* @inheritdoc
*/
OO.inheritClass( OO.ui.CheckboxInputWidget, OO.ui.InputWidget );
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.CheckboxInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+ var
+ state = OO.ui.CheckboxInputWidget.parent.static.gatherPreInfuseState( node, config ),
+ $input = $( node ).find( '.oo-ui-inputWidget-input' );
+ state.$input = $input; // shortcut for performance, used in InputWidget
+ state.checked = $input.prop( 'checked' );
+ return state;
+};
+
/* Methods */
/**
return this.selected;
};
-/**
- * @inheritdoc
- */
-OO.ui.CheckboxInputWidget.prototype.gatherPreInfuseState = function ( node ) {
- var
- state = OO.ui.CheckboxInputWidget.parent.prototype.gatherPreInfuseState.call( this, node ),
- $input = $( node ).find( '.oo-ui-inputWidget-input' );
- state.$input = $input; // shortcut for performance, used in InputWidget
- state.checked = $input.prop( 'checked' );
- return state;
-};
-
/**
* @inheritdoc
*/
OO.inheritClass( OO.ui.RadioInputWidget, OO.ui.InputWidget );
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+ var
+ state = OO.ui.RadioInputWidget.parent.static.gatherPreInfuseState( node, config ),
+ $input = $( node ).find( '.oo-ui-inputWidget-input' );
+ state.$input = $input; // shortcut for performance, used in InputWidget
+ state.checked = $input.prop( 'checked' );
+ return state;
+};
+
/* Methods */
/**
return this.$input.prop( 'checked' );
};
-/**
- * @inheritdoc
- */
-OO.ui.RadioInputWidget.prototype.gatherPreInfuseState = function ( node ) {
- var
- state = OO.ui.RadioInputWidget.parent.prototype.gatherPreInfuseState.call( this, node ),
- $input = $( node ).find( '.oo-ui-inputWidget-input' );
- state.$input = $input; // shortcut for performance, used in InputWidget
- state.checked = $input.prop( 'checked' );
- return state;
-};
-
/**
* @inheritdoc
*/
OO.ui.RadioSelectInputWidget.static.supportsSimpleLabel = false;
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioSelectInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+ var state = OO.ui.RadioSelectInputWidget.parent.static.gatherPreInfuseState( node, config );
+ state.value = $( node ).find( '.oo-ui-radioInputWidget .oo-ui-inputWidget-input:checked' ).val();
+ return state;
+};
+
/* Methods */
/**
return this;
};
-/**
- * @inheritdoc
- */
-OO.ui.RadioSelectInputWidget.prototype.gatherPreInfuseState = function ( node ) {
- var state = OO.ui.RadioSelectInputWidget.parent.prototype.gatherPreInfuseState.call( this, node );
- state.value = $( node ).find( '.oo-ui-radioInputWidget .oo-ui-inputWidget-input:checked' ).val();
- return state;
-};
-
/**
* TextInputWidgets, like HTML text inputs, can be configured with options that customize the
* size of the field as well as its presentation. In addition, these widgets can be configured
this.minRows = config.rows !== undefined ? config.rows : '';
this.maxRows = config.maxRows || Math.max( 2 * ( this.minRows || 0 ), 10 );
this.validate = null;
+ this.styleHeight = null;
// Clone for resizing
if ( this.autosize ) {
integer: /^\d+$/
};
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.TextInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+ var
+ state = OO.ui.TextInputWidget.parent.static.gatherPreInfuseState( node, config ),
+ $input = $( node ).find( '.oo-ui-inputWidget-input' );
+ state.$input = $input; // shortcut for performance, used in InputWidget
+ if ( config.multiline ) {
+ state.scrollTop = $input.scrollTop();
+ }
+ return state;
+};
+
/* Events */
/**
* @event enter
*/
+/**
+ * A `resize` event is emitted when autosize is set and the widget resizes
+ *
+ * @event resize
+ */
+
/* Methods */
/**
* This only affects #multiline inputs that are {@link #autosize autosized}.
*
* @chainable
+ * @fires resize
*/
OO.ui.TextInputWidget.prototype.adjustSize = function () {
- var scrollHeight, innerHeight, outerHeight, maxInnerHeight, measurementError, idealHeight;
+ var scrollHeight, innerHeight, outerHeight, maxInnerHeight, measurementError, idealHeight, newHeight;
if ( this.multiline && this.autosize && this.$input.val() !== this.valCache ) {
this.$clone
this.$clone.addClass( 'oo-ui-element-hidden' );
// Only apply inline height when expansion beyond natural height is needed
- if ( idealHeight > innerHeight ) {
- // Use the difference between the inner and outer height as a buffer
- this.$input.css( 'height', idealHeight + ( outerHeight - innerHeight ) );
- } else {
- this.$input.css( 'height', '' );
+ // Use the difference between the inner and outer height as a buffer
+ newHeight = idealHeight > innerHeight ? idealHeight + ( outerHeight - innerHeight ) : '';
+ if ( newHeight !== this.styleHeight ) {
+ this.$input.css( 'height', newHeight );
+ this.styleHeight = newHeight;
+ this.emit( 'resize' );
}
}
return this;
};
/**
- * Select the entire text of the input.
+ * Focus the input and select a specified range within the text.
*
+ * @param {number} from Select from offset
+ * @param {number} [to] Select to offset, defaults to from
* @chainable
*/
-OO.ui.TextInputWidget.prototype.select = function () {
- this.$input.select();
- return this;
-};
-
-/**
- * Focus the input and move the cursor to the end.
- */
-OO.ui.TextInputWidget.prototype.moveCursorToEnd = function () {
- var textRange,
+OO.ui.TextInputWidget.prototype.selectRange = function ( from, to ) {
+ var textRange, isBackwards, start, end,
element = this.$input[ 0 ];
+
+ to = to || from;
+
+ isBackwards = to < from;
+ start = isBackwards ? to : from;
+ end = isBackwards ? from : to;
+
this.focus();
- if ( element.selectionStart !== undefined ) {
- element.selectionStart = element.selectionEnd = element.value.length;
+
+ if ( element.setSelectionRange ) {
+ element.setSelectionRange( start, end, isBackwards ? 'backward' : 'forward' );
} else if ( element.createTextRange ) {
// IE 8 and below
textRange = element.createTextRange();
- textRange.collapse( false );
+ textRange.collapse( true );
+ textRange.moveStart( 'character', start );
+ textRange.moveEnd( 'character', end - start );
textRange.select();
}
+ return this;
+};
+
+/**
+ * Get the length of the text input value.
+ *
+ * This could differ from the length of #getValue if the
+ * value gets filtered
+ *
+ * @return {number} Input length
+ */
+OO.ui.TextInputWidget.prototype.getInputLength = function () {
+ return this.$input[ 0 ].value.length;
+};
+
+/**
+ * Focus the input and select the entire text.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.select = function () {
+ return this.selectRange( 0, this.getInputLength() );
+};
+
+/**
+ * Focus the input and move the cursor to the start.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.moveCursorToStart = function () {
+ return this.selectRange( 0 );
+};
+
+/**
+ * Focus the input and move the cursor to the end.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.moveCursorToEnd = function () {
+ return this.selectRange( this.getInputLength() );
};
/**
return this;
};
-/**
- * @inheritdoc
- */
-OO.ui.TextInputWidget.prototype.gatherPreInfuseState = function ( node ) {
- var
- state = OO.ui.TextInputWidget.parent.prototype.gatherPreInfuseState.call( this, node ),
- $input = $( node ).find( '.oo-ui-inputWidget-input' );
- state.$input = $input; // shortcut for performance, used in InputWidget
- if ( this.multiline ) {
- state.scrollTop = $input.scrollTop();
- }
- return state;
-};
-
/**
* @inheritdoc
*/
}
};
-/*!
- * Deprecated aliases for classes in the `OO.ui.mixin` namespace.
- */
-
-/**
- * @inheritdoc OO.ui.mixin.ButtonElement
- * @deprecated Use {@link OO.ui.mixin.ButtonElement} instead.
- */
-OO.ui.ButtonElement = OO.ui.mixin.ButtonElement;
-
-/**
- * @inheritdoc OO.ui.mixin.ClippableElement
- * @deprecated Use {@link OO.ui.mixin.ClippableElement} instead.
- */
-OO.ui.ClippableElement = OO.ui.mixin.ClippableElement;
-
-/**
- * @inheritdoc OO.ui.mixin.DraggableElement
- * @deprecated Use {@link OO.ui.mixin.DraggableElement} instead.
- */
-OO.ui.DraggableElement = OO.ui.mixin.DraggableElement;
-
-/**
- * @inheritdoc OO.ui.mixin.DraggableGroupElement
- * @deprecated Use {@link OO.ui.mixin.DraggableGroupElement} instead.
- */
-OO.ui.DraggableGroupElement = OO.ui.mixin.DraggableGroupElement;
-
-/**
- * @inheritdoc OO.ui.mixin.FlaggedElement
- * @deprecated Use {@link OO.ui.mixin.FlaggedElement} instead.
- */
-OO.ui.FlaggedElement = OO.ui.mixin.FlaggedElement;
-
-/**
- * @inheritdoc OO.ui.mixin.GroupElement
- * @deprecated Use {@link OO.ui.mixin.GroupElement} instead.
- */
-OO.ui.GroupElement = OO.ui.mixin.GroupElement;
-
-/**
- * @inheritdoc OO.ui.mixin.GroupWidget
- * @deprecated Use {@link OO.ui.mixin.GroupWidget} instead.
- */
-OO.ui.GroupWidget = OO.ui.mixin.GroupWidget;
-
-/**
- * @inheritdoc OO.ui.mixin.IconElement
- * @deprecated Use {@link OO.ui.mixin.IconElement} instead.
- */
-OO.ui.IconElement = OO.ui.mixin.IconElement;
-
-/**
- * @inheritdoc OO.ui.mixin.IndicatorElement
- * @deprecated Use {@link OO.ui.mixin.IndicatorElement} instead.
- */
-OO.ui.IndicatorElement = OO.ui.mixin.IndicatorElement;
-
-/**
- * @inheritdoc OO.ui.mixin.ItemWidget
- * @deprecated Use {@link OO.ui.mixin.ItemWidget} instead.
- */
-OO.ui.ItemWidget = OO.ui.mixin.ItemWidget;
-
-/**
- * @inheritdoc OO.ui.mixin.LabelElement
- * @deprecated Use {@link OO.ui.mixin.LabelElement} instead.
- */
-OO.ui.LabelElement = OO.ui.mixin.LabelElement;
-
-/**
- * @inheritdoc OO.ui.mixin.LookupElement
- * @deprecated Use {@link OO.ui.mixin.LookupElement} instead.
- */
-OO.ui.LookupElement = OO.ui.mixin.LookupElement;
-
-/**
- * @inheritdoc OO.ui.mixin.PendingElement
- * @deprecated Use {@link OO.ui.mixin.PendingElement} instead.
- */
-OO.ui.PendingElement = OO.ui.mixin.PendingElement;
-
-/**
- * @inheritdoc OO.ui.mixin.PopupElement
- * @deprecated Use {@link OO.ui.mixin.PopupElement} instead.
- */
-OO.ui.PopupElement = OO.ui.mixin.PopupElement;
-
-/**
- * @inheritdoc OO.ui.mixin.TabIndexedElement
- * @deprecated Use {@link OO.ui.mixin.TabIndexedElement} instead.
- */
-OO.ui.TabIndexedElement = OO.ui.mixin.TabIndexedElement;
-
-/**
- * @inheritdoc OO.ui.mixin.TitledElement
- * @deprecated Use {@link OO.ui.mixin.TitledElement} instead.
- */
-OO.ui.TitledElement = OO.ui.mixin.TitledElement;
-
}( OO ) );