Robmoen has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/74082


Change subject: WIP: Add size widget to media edit dialog
......................................................................

WIP: Add size widget to media edit dialog

TODO: Documentation!

Change-Id: Id4e0953ce7de991f94ffefa39f311fbcdd283d29
---
M VisualEditor.php
M modules/ve-mw/dm/nodes/ve.dm.MWBlockImageNode.js
M modules/ve-mw/dm/nodes/ve.dm.MWInlineImageNode.js
M modules/ve-mw/ui/dialogs/ve.ui.MWMediaEditDialog.js
M modules/ve/ui/styles/ve.ui.Widget.css
M modules/ve/ui/widgets/ve.ui.InputWidget.js
A modules/ve/ui/widgets/ve.ui.MediaSizeWidget.js
A modules/ve/ui/widgets/ve.ui.NumberInputWidget.js
8 files changed, 283 insertions(+), 35 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor 
refs/changes/82/74082/1

diff --git a/VisualEditor.php b/VisualEditor.php
index 4edcb4c..3a85d8c 100644
--- a/VisualEditor.php
+++ b/VisualEditor.php
@@ -477,6 +477,8 @@
                        've/ui/widgets/ve.ui.MenuWidget.js',
                        've/ui/widgets/ve.ui.LookupInputWidget.js',
                        've/ui/widgets/ve.ui.TextInputMenuWidget.js',
+                       've/ui/widgets/ve.ui.NumberInputWidget.js',
+                       've/ui/widgets/ve.ui.MediaSizeWidget.js',
                        've/ui/widgets/ve.ui.LinkTargetInputWidget.js',
                        've-mw/ui/widgets/ve.ui.MWLinkTargetInputWidget.js',
                        've-mw/ui/widgets/ve.ui.MWCategoryInputWidget.js',
diff --git a/modules/ve-mw/dm/nodes/ve.dm.MWBlockImageNode.js 
b/modules/ve-mw/dm/nodes/ve.dm.MWBlockImageNode.js
index 3bd67f5..8aff454 100644
--- a/modules/ve-mw/dm/nodes/ve.dm.MWBlockImageNode.js
+++ b/modules/ve-mw/dm/nodes/ve.dm.MWBlockImageNode.js
@@ -57,6 +57,8 @@
                        width: $img.attr( 'width' ),
                        height: $img.attr( 'height' ),
                        resource: $img.attr( 'resource' ),
+                       originalWidth: $img.attr( 'width' ),
+                       originalHeight: $img.attr( 'height' ),
                        originalClasses: classes
                };
 
diff --git a/modules/ve-mw/dm/nodes/ve.dm.MWInlineImageNode.js 
b/modules/ve-mw/dm/nodes/ve.dm.MWInlineImageNode.js
index 923f52e..1d179d7 100644
--- a/modules/ve-mw/dm/nodes/ve.dm.MWInlineImageNode.js
+++ b/modules/ve-mw/dm/nodes/ve.dm.MWInlineImageNode.js
@@ -56,6 +56,8 @@
 
        attributes.width = width !== undefined && width !== '' ? Number( width 
) : null;
        attributes.height = height !== undefined && height !== '' ? Number( 
height ) : null;
+       attributes.originalWidth = attributes.width;
+       attributes.originalHeight = attributes.height;
 
        attributes.isLinked = $firstChild.is( 'a' );
        if ( attributes.isLinked ) {
diff --git a/modules/ve-mw/ui/dialogs/ve.ui.MWMediaEditDialog.js 
b/modules/ve-mw/ui/dialogs/ve.ui.MWMediaEditDialog.js
index 3cb3d63..fe38fff 100644
--- a/modules/ve-mw/ui/dialogs/ve.ui.MWMediaEditDialog.js
+++ b/modules/ve-mw/ui/dialogs/ve.ui.MWMediaEditDialog.js
@@ -20,6 +20,7 @@
        ve.ui.MWDialog.call( this, surface, config );
 
        // Properties
+       this.node = null;
        this.captionNode = null;
 };
 
@@ -48,7 +49,17 @@
        // Parent method
        ve.ui.MWDialog.prototype.initialize.call( this );
 
-       // Properties
+       // Media size
+       this.sizeFieldset = new ve.ui.FieldsetLayout( {
+               '$$': this.frame.$$,
+               //'label': ve.msg( 
'visualeditor-dialog-media-size-content-section' ),
+               'label': 'Size',
+               'icon': 'parameter'
+       } );
+
+       this.mediaSizeWidget = new ve.ui.MediaSizeWidget( { '$$': this.frame.$$ 
} );
+       this.sizeFieldset.$.append( this.mediaSizeWidget.$ );
+
        this.contentFieldset = new ve.ui.FieldsetLayout( {
                '$$': this.frame.$$,
                'label': ve.msg( 'visualeditor-dialog-media-content-section' ),
@@ -57,17 +68,32 @@
 
        // Initialization
        this.$body.addClass( 've-ui-mwMediaEditDialog-body' );
-       this.$body.append( this.contentFieldset.$ );
+       this.$body.append( this.sizeFieldset.$, this.contentFieldset.$ );
 };
 
 ve.ui.MWMediaEditDialog.prototype.onOpen = function () {
-       var data, doc = this.surface.getModel().getDocument();
+       var data,
+               doc = this.surface.getModel().getDocument(),
+               node = this.surface.getView().getFocusedNode(),
+               model = node.getModel();
 
        // Parent method
        ve.ui.MWDialog.prototype.onOpen.call( this );
 
+       // Set node
+       this.node = node;
        // Get caption content
-       this.captionNode = 
this.surface.getView().getFocusedNode().getModel().getCaptionNode();
+       this.captionNode = model.getCaptionNode();
+
+       // Init size widget
+       this.mediaSizeWidget.initialize( {
+               'originalHeight': model.getAttribute( 'originalHeight' ),
+               'originalWidth': model.getAttribute( 'originalWidth' ),
+               'height': model.getAttribute( 'height' ),
+               'width': model.getAttribute( 'width' )
+       } );
+
+       // Init caption
        if ( this.captionNode && this.captionNode.getLength() > 0 ) {
                data = doc.getData( this.captionNode.getRange(), true );
        } else {
@@ -91,14 +117,28 @@
 };
 
 ve.ui.MWMediaEditDialog.prototype.onClose = function ( action ) {
-       var data, doc, surfaceModel = this.surface.getModel();
+       var data,
+               surfaceModel = this.surface.getModel(),
+               doc = surfaceModel.getDocument(),
+               dimensions = {};
 
        // Parent method
        ve.ui.MWDialog.prototype.onClose.call( this );
 
        if ( action === 'apply' ) {
+               // Did the image size change
+               if ( this.mediaSizeWidget.changed ) {
+                       dimensions = this.mediaSizeWidget.getDimensions();
+                       // Change size.
+                       surfaceModel.change(
+                               ve.dm.Transaction.newFromAttributeChanges(
+                                       doc, this.node.getModel().getOffset(), 
dimensions ), surfaceModel.getSelection()
+                       );
+                       // Is this needed?
+                       this.surface.getView().getFocusedNode().emit( 'resize' 
);
+               }
+               // Get caption data
                data = this.captionSurface.getModel().getDocument().getData();
-               doc = surfaceModel.getDocument();
                if ( this.captionNode ) {
                        // Replace the contents of the caption
                        surfaceModel.getFragment( this.captionNode.getRange(), 
true ).insertContent( data );
diff --git a/modules/ve/ui/styles/ve.ui.Widget.css 
b/modules/ve/ui/styles/ve.ui.Widget.css
index d41ddae..22d044c 100644
--- a/modules/ve/ui/styles/ve.ui.Widget.css
+++ b/modules/ve/ui/styles/ve.ui.Widget.css
@@ -284,22 +284,14 @@
        padding: 0.5em 0;
 }
 
-/* ve.ui.TextInputWidget */
+/* ve-ui-inputWidget */
 
-.ve-ui-textInputWidget {
-       -webkit-box-sizing: border-box;
-       -moz-box-sizing: border-box;
-       box-sizing: border-box;
-       width: 20em;
-       position: relative;
-}
-
-.ve-ui-textInputWidget input,
-.ve-ui-textInputWidget input:focus[readonly],
-.ve-ui-widget-disabled.ve-ui-textInputWidget input:focus,
-.ve-ui-textInputWidget textarea,
-.ve-ui-textInputWidget textarea:focus[readonly],
-.ve-ui-widget-disabled.ve-ui-textInputWidget textarea:focus {
+.ve-ui-inputWidget input,
+.ve-ui-inputWidget input:focus[readonly],
+.ve-ui-widget-disabled.ve-ui-inputWidget input:focus,
+.ve-ui-inputWidget textarea,
+.ve-ui-inputWidget textarea:focus[readonly],
+.ve-ui-widget-disabled.ve-ui-inputWidget textarea:focus {
        display: inline-block;
        font-size: 1em;
        font-family: sans-serif;
@@ -322,31 +314,38 @@
        transition: border-color 200ms, box-shadow 200ms, background-color 
200ms;
 }
 
-.ve-ui-textInputWidget-pending input,
-.ve-ui-textInputWidget-pending textarea {
-       background-color: transparent;
-}
-
-.ve-ui-textInputWidget input:focus,
-.ve-ui-textInputWidget textarea:focus {
+.ve-ui-inputWidget input:focus,
+.ve-ui-inputWidget textarea:focus {
        outline: none;
        border-color: #a7dcff;
        box-shadow: 0 0 0.3em #a7dcff, 0 0 0 white;
        background-color: #fff;
 }
 
-.ve-ui-textInputWidget input[readonly],
-.ve-ui-textInputWidget textarea[readonly] {
+.ve-ui-inputWidget input[readonly],
+.ve-ui-inputWidget textarea[readonly] {
        color: #777;
        text-shadow: 0 1px 1px #fff;
 }
 
-.ve-ui-widget-disabled.ve-ui-textInputWidget input,
-.ve-ui-widget-disabled.ve-ui-textInputWidget input:focus,
-.ve-ui-widget-disabled.ve-ui-textInputWidget textarea,
-.ve-ui-widget-disabled.ve-ui-textInputWidget textarea:focus {
+.ve-ui-widget-disabled.ve-ui-inputWidget input,
+.ve-ui-widget-disabled.ve-ui-inputWidget input:focus,
+.ve-ui-widget-disabled.ve-ui-inputWidget textarea,
+.ve-ui-widget-disabled.ve-ui-inputWidget textarea:focus {
        color: #ccc;
        text-shadow: 0 1px 1px #fff;
+}
+
+/* ve.ui.TextInputWidget */
+
+.ve-ui-textInputWidget {
+       position: relative;
+       width: 20em;
+}
+
+.ve-ui-textInputWidget-pending input,
+.ve-ui-textInputWidget-pending textarea {
+       background-color: transparent;
 }
 
 .ve-ui-textInputWidget-decorated input,
@@ -477,3 +476,20 @@
        overflow-y: auto;
        line-height: 0;
 }
+
+/* ve.ui.MediaSizeWidget */
+
+.ve-ui-mediaSizeWidget > div {
+       float: left;
+       margin: 0 0.25em;
+}
+
+.ve-ui-mediaSizeWidget label {
+       float: left;
+}
+
+.ve-ui-mediaSizeWidget .ve-ui-numberInputWidget {
+       float: left;
+       margin-left: 0.5em;
+       margin-right: 1em;
+}
diff --git a/modules/ve/ui/widgets/ve.ui.InputWidget.js 
b/modules/ve/ui/widgets/ve.ui.InputWidget.js
index a5e76df..0270ebb 100644
--- a/modules/ve/ui/widgets/ve.ui.InputWidget.js
+++ b/modules/ve/ui/widgets/ve.ui.InputWidget.js
@@ -116,7 +116,7 @@
 ve.ui.InputWidget.prototype.setValue = function ( value ) {
        var domValue = this.$input.val();
        value = this.sanitizeValue( value );
-       if ( this.value !== value ) {
+       if ( this.value !== value || this.value !== domValue ) {
                this.value = value;
                // Only update the DOM if we must
                if ( domValue !== this.value ) {
diff --git a/modules/ve/ui/widgets/ve.ui.MediaSizeWidget.js 
b/modules/ve/ui/widgets/ve.ui.MediaSizeWidget.js
new file mode 100644
index 0000000..93cec31
--- /dev/null
+++ b/modules/ve/ui/widgets/ve.ui.MediaSizeWidget.js
@@ -0,0 +1,89 @@
+ve.ui.MediaSizeWidget = function VeUiMediaSizeWidget( config ) {
+       config = ve.extendObject( {}, config );
+       // Parent constructor
+       ve.ui.Widget.call( this, config );
+
+       // Properties
+       this.initialDimensions = {};
+       this.changed = false;
+       this.ratio = null;
+
+       this.widthInput = new ve.ui.NumberInputWidget( { '$$': this.$$ } );
+       this.widthLabel = new ve.ui.InputLabelWidget(
+               { '$$': this.$$, 'input': this.widthInput, 'label': 'Width' }
+       );
+       this.$width = this.$$( '<div>' ).append( this.widthLabel.$, 
this.widthInput.$ );
+
+       this.heightInput = new ve.ui.NumberInputWidget( { '$$': this.$$ } );
+       this.heightLabel = new ve.ui.InputLabelWidget(
+               { '$$': this.$$, 'input': this.heightInput, 'label': 'Height' }
+       );
+
+       this.$height = this.$$( '<div>' ).append( this.heightLabel.$, 
this.heightInput.$ );
+       this.$layout = this.$$( '<div>' ).append( this.$width, this.$height )
+               .addClass( 've-ui-mediaSizeWidget' );
+
+       this.$.append( this.$layout );
+
+       // Events
+       this.widthInput.$input.on( 'input change cut paste keyup blur', 
ve.bind( this.onWidthInput, this ) );
+       this.heightInput.$input.on( 'input change cut paste keyup blur', 
ve.bind( this.onHeightInput, this ) );
+};
+
+/* Inheritance */
+
+ve.inheritClass( ve.ui.MediaSizeWidget, ve.ui.Widget );
+
+/* Methods */
+
+ve.ui.MediaSizeWidget.prototype.initialize = function ( dimensions ) {
+       this.widthInput.setValue( dimensions.width );
+       this.heightInput.setValue( dimensions.height );
+       // Store original dimensions
+       this.initialDimensions = {
+               'height': parseInt( dimensions.height, 10 ),
+               'width': parseInt( dimensions.width, 10 )
+       };
+       // Set ratio from original dimensions
+       this.ratio = dimensions.originalWidth / dimensions.originalHeight;
+};
+
+ve.ui.MediaSizeWidget.prototype.getDimensions = function () {
+       return {
+               'width': this.widthInput.getValue(),
+               'height': this.heightInput.getValue()
+       };
+};
+
+ve.ui.MediaSizeWidget.prototype.onWidthInput = function () {
+       var changeWidth = this.widthInput.getValue(),
+               changeHeight = Math.round( changeWidth / this.ratio );
+       if ( !this.widthInput.isValid() ) {
+               this.resetDimensions();
+               return;
+       }
+       this.heightInput.setValue( changeHeight );
+       this.setChangedFlag();
+};
+
+ve.ui.MediaSizeWidget.prototype.onHeightInput = function () {
+       var changeHeight = this.heightInput.getValue(),
+               changeWidth = Math.round( changeHeight * this.ratio );
+       if ( !this.heightInput.isValid() ) {
+               this.resetDimensions();
+               return;
+       }
+       this.widthInput.setValue( changeWidth );
+       this.setChangedFlag();
+};
+
+ve.ui.MediaSizeWidget.prototype.setChangedFlag = function () {
+       this.changed = this.initialDimensions.height !== 
this.heightInput.getValue() ||
+                       this.initialDimensions.width !== 
this.widthInput.getValue();
+};
+
+ve.ui.MediaSizeWidget.prototype.resetDimensions = function () {
+       this.widthInput.setValue( this.initialDimensions.width );
+       this.heightInput.setValue( this.initialDimensions.height );
+       this.changed = false;
+};
diff --git a/modules/ve/ui/widgets/ve.ui.NumberInputWidget.js 
b/modules/ve/ui/widgets/ve.ui.NumberInputWidget.js
new file mode 100644
index 0000000..7e85fc9
--- /dev/null
+++ b/modules/ve/ui/widgets/ve.ui.NumberInputWidget.js
@@ -0,0 +1,97 @@
+/*!
+ * VisualEditor UserInterface NumberInputWidget class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * Creates an ve.ui.NumberInputWidget object.
+ *
+ * @class
+ * @extends ve.ui.InputWidget
+ *
+ * @constructor
+ * @param {Object} [config] Config options
+ */
+ve.ui.NumberInputWidget = function VeUiTextInputWidget( config ) {
+       config = config || {};
+
+       // Parent constructor
+       ve.ui.InputWidget.call( this, config );
+
+       // Events
+       this.$input.on( 'keypress', ve.bind( this.onKeyPress, this ) );
+
+       // Initialization
+       this.$.addClass( 've-ui-numberInputWidget' );
+};
+
+/* Inheritance */
+
+ve.inheritClass( ve.ui.NumberInputWidget, ve.ui.InputWidget );
+
+/* Events */
+
+/**
+ * User presses enter inside the text box.
+ *
+ * Not called if input is multiline.
+ *
+ * @event enter
+ */
+
+/* Methods */
+
+/**
+ * Handles key press events.
+ *
+ * @param {jQuery.Event} e Key press event
+ * @emits enter If enter key is pressed and input is not multiline
+ */
+ve.ui.NumberInputWidget.prototype.onKeyPress = function ( e ) {
+       if ( e.which === ve.Keys.ENTER && !this.multiline ) {
+               this.emit( 'enter' );
+       }
+};
+
+/* Methods */
+
+/**
+ * Get input element.
+ *
+ * @method
+ * @param {Object} [config] Config options
+ * @returns {jQuery} Input element
+ */
+ve.ui.NumberInputWidget.prototype.getInputElement = function () {
+       // Using text as Chrome auto formats input=number
+       return this.$$( '<input>' ).attr( 'type', 'text' );
+};
+
+/**
+ * Get the value of the input.
+ *
+ * @method
+ * @returns {string} Input value
+ */
+ve.ui.NumberInputWidget.prototype.getValue = function () {
+       return parseInt( this.value, 10 );
+};
+
+ve.ui.NumberInputWidget.prototype.isValid = function () {
+       return !( isNaN( this.getValue() ) || this.getValue() <= 0 );
+};
+
+/**
+ * Sanitize incoming value.
+ *
+ * Ensures value is a number, and converts undefined and null to empty strings.
+ *
+ * @method
+ * @param {string} value Original value
+ * @returns {string} Sanitized value
+ */
+ve.ui.NumberInputWidget.prototype.sanitizeValue = function ( value ) {
+       return value === undefined || value === null ? '' : String( value 
).replace(/[^0-9.]/g, '');
+};

-- 
To view, visit https://gerrit.wikimedia.org/r/74082
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Id4e0953ce7de991f94ffefa39f311fbcdd283d29
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Robmoen <rm...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to