Trevor Parscal has uploaded a new change for review.

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


Change subject: Basic classes for grid and panel layouts and outline widget
......................................................................

Basic classes for grid and panel layouts and outline widget

* Also corrects issues with several of the stand-alone files by fixing and 
using makeStaticLoader.php

Change-Id: I6b92c0204e176c914c26eff8c03ea417578e080c
---
M .docs/categories.json
M VisualEditor.php
M demos/ve/index.php
M maintenance/makeStaticLoader.php
M modules/ve/test/index.php
A modules/ve/ui/layouts/ve.ui.GridLayout.js
A modules/ve/ui/layouts/ve.ui.PanelLayout.js
A modules/ve/ui/ve.ui.Element.js
A modules/ve/ui/ve.ui.Layout.js
M modules/ve/ui/ve.ui.Widget.js
A modules/ve/ui/widgets/ve.ui.OutlineItemWidget.js
A modules/ve/ui/widgets/ve.ui.OutlineWidget.js
12 files changed, 619 insertions(+), 37 deletions(-)


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

diff --git a/.docs/categories.json b/.docs/categories.json
index 6900e4f..b03d8f0 100644
--- a/.docs/categories.json
+++ b/.docs/categories.json
@@ -13,7 +13,13 @@
                "groups": [
                        {
                                "name": "General",
-                               "classes": ["ve.ce", "ve.ce.NodeFactory", 
"ve.ce.Surface", "ve.ce.SurfaceObserver", "ve.ce.DomRange"]
+                               "classes": [
+                                       "ve.ce",
+                                       "ve.ce.NodeFactory",
+                                       "ve.ce.Surface",
+                                       "ve.ce.SurfaceObserver",
+                                       "ve.ce.DomRange"
+                               ]
                        },
                        {
                                "name": "Nodes",
@@ -73,7 +79,19 @@
                "groups": [
                        {
                                "name": "General",
-                               "classes": ["ve.ui", "ve.ui.Context", 
"ve.ui.Frame", "ve.ui.InspectorFactory", "ve.ui.Toolbar", "ve.ui.ToolFactory", 
"ve.ui.Dialog"]
+                               "classes": [
+                                       "ve.ui",
+                                       "ve.ui.Context",
+                                       "ve.ui.Frame",
+                                       "ve.ui.Toolbar",
+                                       "ve.ui.Element",
+                                       "ve.ui.Window",
+                                       "ve.ui.WindowSet"
+                               ]
+                       },
+                       {
+                               "name": "Factories",
+                               "classes": ["ve.ui.*Factory"]
                        },
                        {
                                "name": "Tools",
@@ -86,6 +104,10 @@
                        {
                                "name": "Widgets",
                                "classes": ["ve.ui.*Widget"]
+                       },
+                       {
+                               "name": "Layouts",
+                               "classes": ["ve.ui.*Layout"]
                        },
                        {
                                "name": "Dialogs",
@@ -131,7 +153,9 @@
                        },
                        {
                                "name": "JavaScript",
-                               "classes": ["Array", "Boolean", "Date", 
"Function", "Number", "Object", "RegExp", "String"]
+                               "classes": [
+                                       "Array", "Boolean", "Date", "Function", 
"Number", "Object", "RegExp", "String"
+                               ]
                        }
                ]
        }
diff --git a/VisualEditor.php b/VisualEditor.php
index cdca7b6..ba867ff 100644
--- a/VisualEditor.php
+++ b/VisualEditor.php
@@ -305,11 +305,16 @@
                        've/ui/ve.ui.InspectorFactory.js',
                        've/ui/ve.ui.Dialog.js',
                        've/ui/ve.ui.DialogFactory.js',
+                       've/ui/ve.ui.Element.js',
+                       've/ui/ve.ui.Layout.js',
                        've/ui/ve.ui.Widget.js',
                        've/ui/ve.ui.Tool.js',
                        've/ui/ve.ui.Toolbar.js',
                        've/ui/ve.ui.ToolFactory.js',
 
+                       've/ui/layouts/ve.ui.GridLayout.js',
+                       've/ui/layouts/ve.ui.PanelLayout.js',
+
                        've/ui/widgets/ve.ui.LabeledWidget.js',
                        've/ui/widgets/ve.ui.FlaggableWidget.js',
                        've/ui/widgets/ve.ui.ButtonWidget.js',
diff --git a/demos/ve/index.php b/demos/ve/index.php
index 9deb8b8..213e62d 100644
--- a/demos/ve/index.php
+++ b/demos/ve/index.php
@@ -23,9 +23,16 @@
                <meta charset="UTF-8">
                <title>VisualEditor Standalone Demo</title>
 
-               <!-- Generated by maintenance/makeStaticLoader.php -->
+               <!-- Generated by makeStaticLoader.php -->
                <!-- Standalone Init -->
                <link rel=stylesheet 
href="../../modules/ve/init/sa/styles/ve.init.sa.css">
+               <script>
+                       if ( window.devicePixelRatio > 1 ) {
+                               document.write( '<link rel="stylesheet" 
href="../../modules/ve/ui/styles/ve.ui.Icons-vector.css">' );
+                       } else {
+                               document.write( '<link rel="stylesheet" 
href="../../modules/ve/ui/styles/ve.ui.Icons-raster.css">' );
+                       }
+               </script>
                <!-- ext.visualEditor.core -->
                <link rel=stylesheet 
href="../../modules/ve/styles/ve.Surface.css">
                <link rel=stylesheet 
href="../../modules/ve/ce/styles/ve.ce.DocumentNode.css">
@@ -36,17 +43,9 @@
                <link rel=stylesheet 
href="../../modules/ve/ui/styles/ve.ui.Window.css">
                <link rel=stylesheet 
href="../../modules/ve/ui/styles/ve.ui.Dialog.css">
                <link rel=stylesheet 
href="../../modules/ve/ui/styles/ve.ui.Inspector.css">
-               <link rel=stylesheet 
href="../../modules/ve/ui/styles/ve.ui.Tool.css">
                <link rel=stylesheet 
href="../../modules/ve/ui/styles/ve.ui.Toolbar.css">
+               <link rel=stylesheet 
href="../../modules/ve/ui/styles/ve.ui.Tool.css">
                <link rel=stylesheet 
href="../../modules/ve/ui/styles/ve.ui.Widget.css">
-
-               <script>
-                       if ( window.devicePixelRatio > 1 ) {
-                               document.write( '<link rel="stylesheet" 
href="../../modules/ve/ui/styles/ve.ui.Icons-vector.css">' );
-                       } else {
-                               document.write( '<link rel="stylesheet" 
href="../../modules/ve/ui/styles/ve.ui.Icons-raster.css">' );
-                       }
-               </script>
 
                <!-- demo -->
                <link rel="stylesheet" href="demo.css">
@@ -63,7 +62,7 @@
                </ul>
                <div class="ve-demo-editor"></div>
 
-               <!-- Generated by maintenance/makeStaticLoader.php -->
+               <!-- Generated by makeStaticLoader.php -->
                <!-- Dependencies -->
                <script src="../../modules/jquery/jquery.js"></script>
                <script src="../../modules/rangy/rangy-core.js"></script>
@@ -82,7 +81,7 @@
                <script>
                        <?php
                                require( 
'../../modules/../VisualEditor.i18n.php' );
-                               echo 've.init.platform.addMessages( ' . 
json_encode( $messages['en'] ) . ');';
+                               echo 've.init.platform.addMessages( ' . 
json_encode( $messages['en'] ) . ');' . "\n";
                        ?>
                        ve.init.platform.setModulesUrl( '../../modules/' );
                </script>
@@ -137,8 +136,6 @@
                <script 
src="../../modules/ve/dm/nodes/ve.dm.ListItemNode.js"></script>
                <script 
src="../../modules/ve/dm/nodes/ve.dm.ListNode.js"></script>
                <script 
src="../../modules/ve/dm/nodes/ve.dm.MetaNode.js"></script>
-               <script 
src="../../modules/ve/dm/nodes/ve.dm.MWEntityNode.js"></script>
-               <script 
src="../../modules/ve/dm/nodes/ve.dm.MWMetaNode.js"></script>
                <script 
src="../../modules/ve/dm/nodes/ve.dm.ParagraphNode.js"></script>
                <script 
src="../../modules/ve/dm/nodes/ve.dm.PreformattedNode.js"></script>
                <script 
src="../../modules/ve/dm/nodes/ve.dm.TableCellNode.js"></script>
@@ -146,6 +143,10 @@
                <script 
src="../../modules/ve/dm/nodes/ve.dm.TableRowNode.js"></script>
                <script 
src="../../modules/ve/dm/nodes/ve.dm.TableSectionNode.js"></script>
                <script 
src="../../modules/ve/dm/nodes/ve.dm.TextNode.js"></script>
+               <script 
src="../../modules/ve/dm/nodes/ve.dm.MWEntityNode.js"></script>
+               <script 
src="../../modules/ve/dm/nodes/ve.dm.MWHeadingNode.js"></script>
+               <script 
src="../../modules/ve/dm/nodes/ve.dm.MWMetaNode.js"></script>
+               <script 
src="../../modules/ve/dm/nodes/ve.dm.MWPreformattedNode.js"></script>
                <script 
src="../../modules/ve/dm/annotations/ve.dm.LinkAnnotation.js"></script>
                <script 
src="../../modules/ve/dm/annotations/ve.dm.MWExternalLinkAnnotation.js"></script>
                <script 
src="../../modules/ve/dm/annotations/ve.dm.MWInternalLinkAnnotation.js"></script>
@@ -172,7 +173,6 @@
                <script 
src="../../modules/ve/ce/nodes/ve.ce.ImageNode.js"></script>
                <script 
src="../../modules/ve/ce/nodes/ve.ce.ListItemNode.js"></script>
                <script 
src="../../modules/ve/ce/nodes/ve.ce.ListNode.js"></script>
-               <script 
src="../../modules/ve/ce/nodes/ve.ce.MWEntityNode.js"></script>
                <script 
src="../../modules/ve/ce/nodes/ve.ce.ParagraphNode.js"></script>
                <script 
src="../../modules/ve/ce/nodes/ve.ce.PreformattedNode.js"></script>
                <script 
src="../../modules/ve/ce/nodes/ve.ce.TableCellNode.js"></script>
@@ -180,6 +180,9 @@
                <script 
src="../../modules/ve/ce/nodes/ve.ce.TableRowNode.js"></script>
                <script 
src="../../modules/ve/ce/nodes/ve.ce.TableSectionNode.js"></script>
                <script 
src="../../modules/ve/ce/nodes/ve.ce.TextNode.js"></script>
+               <script 
src="../../modules/ve/ce/nodes/ve.ce.MWEntityNode.js"></script>
+               <script 
src="../../modules/ve/ce/nodes/ve.ce.MWHeadingNode.js"></script>
+               <script 
src="../../modules/ve/ce/nodes/ve.ce.MWPreformattedNode.js"></script>
                <script src="../../modules/ve/ui/ve.ui.js"></script>
                <script src="../../modules/ve/ui/ve.ui.Context.js"></script>
                <script src="../../modules/ve/ui/ve.ui.Frame.js"></script>
@@ -189,10 +192,14 @@
                <script 
src="../../modules/ve/ui/ve.ui.InspectorFactory.js"></script>
                <script src="../../modules/ve/ui/ve.ui.Dialog.js"></script>
                <script 
src="../../modules/ve/ui/ve.ui.DialogFactory.js"></script>
+               <script src="../../modules/ve/ui/ve.ui.Element.js"></script>
+               <script src="../../modules/ve/ui/ve.ui.Layout.js"></script>
                <script src="../../modules/ve/ui/ve.ui.Widget.js"></script>
                <script src="../../modules/ve/ui/ve.ui.Tool.js"></script>
                <script src="../../modules/ve/ui/ve.ui.Toolbar.js"></script>
                <script src="../../modules/ve/ui/ve.ui.ToolFactory.js"></script>
+               <script 
src="../../modules/ve/ui/layouts/ve.ui.GridLayout.js"></script>
+               <script 
src="../../modules/ve/ui/layouts/ve.ui.PanelLayout.js"></script>
                <script 
src="../../modules/ve/ui/widgets/ve.ui.LabeledWidget.js"></script>
                <script 
src="../../modules/ve/ui/widgets/ve.ui.FlaggableWidget.js"></script>
                <script 
src="../../modules/ve/ui/widgets/ve.ui.ButtonWidget.js"></script>
@@ -225,8 +232,10 @@
                <script 
src="../../modules/ve/ui/tools/buttons/ve.ui.RedoButtonTool.js"></script>
                <script 
src="../../modules/ve/ui/tools/buttons/ve.ui.UndoButtonTool.js"></script>
                <script 
src="../../modules/ve/ui/tools/dropdowns/ve.ui.FormatDropdownTool.js"></script>
+               <script 
src="../../modules/ve/ui/tools/dropdowns/ve.ui.MWFormatDropdownTool.js"></script>
                <script 
src="../../modules/ve/ui/inspectors/ve.ui.LinkInspector.js"></script>
                <script 
src="../../modules/ve/ui/inspectors/ve.ui.MWLinkInspector.js"></script>
+
                <!-- demo -->
                <script>
                        $( document ).ready( function () {
diff --git a/maintenance/makeStaticLoader.php b/maintenance/makeStaticLoader.php
index 978f181..071e32c 100644
--- a/maintenance/makeStaticLoader.php
+++ b/maintenance/makeStaticLoader.php
@@ -52,6 +52,7 @@
                        'scripts' => array(
                                've/init/sa/ve.init.sa.js',
                                've/init/sa/ve.init.sa.Platform.js',
+                               've/init/sa/ve.init.sa.Target.js',
                        ),
                        'headAdd' => '
                <script>
diff --git a/modules/ve/test/index.php b/modules/ve/test/index.php
index 73c0f2a..62a7b1a 100644
--- a/modules/ve/test/index.php
+++ b/modules/ve/test/index.php
@@ -8,7 +8,7 @@
                <link rel="stylesheet" href="../../qunit/qunit.css">
                <script src="../../qunit/qunit.js"></script>
 
-               <!-- Generated by maintenance/makeStaticLoader.php -->
+               <!-- Generated by makeStaticLoader.php -->
                <!-- Dependencies -->
                <script src="../../jquery/jquery.js"></script>
                <script src="../../rangy/rangy-core.js"></script>
@@ -27,7 +27,7 @@
                <script>
                        <?php
                                require( '../../../VisualEditor.i18n.php' );
-                               echo 've.init.platform.addMessages( ' . 
json_encode( $messages['en'] ) . ');';
+                               echo 've.init.platform.addMessages( ' . 
json_encode( $messages['en'] ) . ');' . "\n";
                        ?>
                        ve.init.platform.setModulesUrl( '../../' );
                </script>
@@ -138,10 +138,14 @@
                <script src="../../ve/ui/ve.ui.InspectorFactory.js"></script>
                <script src="../../ve/ui/ve.ui.Dialog.js"></script>
                <script src="../../ve/ui/ve.ui.DialogFactory.js"></script>
+               <script src="../../ve/ui/ve.ui.Element.js"></script>
+               <script src="../../ve/ui/ve.ui.Layout.js"></script>
                <script src="../../ve/ui/ve.ui.Widget.js"></script>
                <script src="../../ve/ui/ve.ui.Tool.js"></script>
                <script src="../../ve/ui/ve.ui.Toolbar.js"></script>
                <script src="../../ve/ui/ve.ui.ToolFactory.js"></script>
+               <script src="../../ve/ui/layouts/ve.ui.GridLayout.js"></script>
+               <script src="../../ve/ui/layouts/ve.ui.PanelLayout.js"></script>
                <script 
src="../../ve/ui/widgets/ve.ui.LabeledWidget.js"></script>
                <script 
src="../../ve/ui/widgets/ve.ui.FlaggableWidget.js"></script>
                <script 
src="../../ve/ui/widgets/ve.ui.ButtonWidget.js"></script>
diff --git a/modules/ve/ui/layouts/ve.ui.GridLayout.js 
b/modules/ve/ui/layouts/ve.ui.GridLayout.js
new file mode 100644
index 0000000..cec0041
--- /dev/null
+++ b/modules/ve/ui/layouts/ve.ui.GridLayout.js
@@ -0,0 +1,141 @@
+/*!
+ * VisualEditor UserInterface GridLayout class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * Grid layout.
+ *
+ * @class
+ * @extends ve.ui.Layout
+ *
+ * @constructor
+ * @param {ve.ui.PanelLayout[]} panels Panels in the grid
+ * @param {Object} [config] Config options
+ * @cfg {number[]} [widths] Widths of columns as ratios
+ * @cfg {number[]} [heights] Heights of columns as ratios
+ */
+ve.ui.GridLayout = function VeUiGridLayout( panels, config ) {
+       var i, len;
+
+       // Parent constructor
+       ve.ui.Layout.call( this, config );
+
+       // Properties
+       this.panels = [];
+       this.widths = [];
+       this.heights = [];
+
+       // Initialization
+       this.$.addClass( 've-ui-gridLayout' );
+       for ( i = 0, len = panels.length; i < len; i++ ) {
+               this.panels.push( panels[i] );
+               this.$.append( panels[i].$ );
+       }
+       if ( config.widths || config.heights ) {
+               this.layout( config.widths || [], config.heights || [] );
+       } else {
+               // Arrange in columns by default
+               this.layout( new Array( this.panels.length ), [] );
+       }
+};
+
+/* Inheritance */
+
+ve.inheritClass( ve.ui.GridLayout, ve.ui.Layout );
+
+/* Events */
+
+/**
+ * @event layout
+ */
+
+/**
+ * @event update
+ */
+
+/* Static Properties */
+
+ve.ui.GridLayout.static.tagName = 'div';
+
+/* Methods */
+
+/**
+ * Set grid dimensions.
+ *
+ * @method
+ * @param {number[]} widths Widths of columns as ratios
+ * @param {number[]} heights Heights of rows as ratios
+ * @throws {Error} If grid is not large enough to fit all panels
+ */
+ve.ui.GridLayout.prototype.layout = function ( widths, heights ) {
+       var x, y, xd, yd,
+               cols = widths.length,
+               rows = heights.length;
+
+       // Verify grid is big enough to fit panels
+       if ( cols * rows < this.panels.length ) {
+               throw new Error( 'Grid is not large enough to fit ' + 
this.panels.length + 'panels' );
+       }
+
+       // Sum up denominators
+       for ( x = 0; x < cols; x++ ) {
+               xd += widths[x];
+       }
+       for ( y = 0; y < rows; y++ ) {
+               yd += heights[yd];
+       }
+       // Store factors
+       this.widths = [];
+       this.heights = [];
+       for ( x = 0; x < cols; x++ ) {
+               this.widths[x] = widths[x] / xd;
+       }
+       for ( y = 0; y < rows; y++ ) {
+               this.heights[x] = heights[y] / yd;
+       }
+       // Synchronize view
+       this.update();
+       this.emit( 'layout' );
+};
+
+/**
+ * Update panel positions and sizes.
+ *
+ * @method
+ */
+ve.ui.GridLayout.prototype.update = function () {
+       var x, y, panel,
+               cols = this.widths.length,
+               rows = this.heights.length,
+               width = this.$.width(),
+               height = this.$.height(),
+               dim = { 'width': 0, 'height': 0, 'left': 0, 'top': 0 };
+
+       for ( x = 0; x < cols; x++ ) {
+               for ( y = 0; y < rows; y++ ) {
+                       panel = this.panels[( x * cols ) + y];
+                       dim.left += dim.width;
+                       dim.top += dim.height;
+                       dim.width = width * this.widths[x];
+                       dim.height = height * this.heights[y];
+                       panel.$.css( dim );
+               }
+       }
+       this.emit( 'update' );
+};
+
+/**
+ * Get a panel at a given position.
+ *
+ * The x and y position is affected by the current grid layout.
+ *
+ * @method
+ * @param {number} x Horizontal position
+ * @param {number} y Vertical position
+ */
+ve.ui.GridLayout.prototype.getPanel = function ( x, y ) {
+       return this.panels[( x * this.widths.length ) + y];
+};
diff --git a/modules/ve/ui/layouts/ve.ui.PanelLayout.js 
b/modules/ve/ui/layouts/ve.ui.PanelLayout.js
new file mode 100644
index 0000000..bf41a57
--- /dev/null
+++ b/modules/ve/ui/layouts/ve.ui.PanelLayout.js
@@ -0,0 +1,31 @@
+/*!
+ * VisualEditor UserInterface PanelLayout class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * Panel layout.
+ *
+ * @class
+ * @extends ve.ui.Layout
+ *
+ * @constructor
+ * @param {Object} [config] Config options
+ * @cfg {boolean} [scroll] Allow vertical scrolling
+ */
+ve.ui.PanelLayout = function VeUiPanelLayout( config ) {
+       // Parent constructor
+       ve.ui.Layout.call( this, config );
+
+       // Initialization
+       this.$.addClass( 've-ui-panelLayout' );
+       if ( config.scroll ) {
+               this.$.css( 'overflow-x', 'auto' );
+       }
+};
+
+/* Inheritance */
+
+ve.inheritClass( ve.ui.PanelLayout, ve.ui.Layout );
diff --git a/modules/ve/ui/ve.ui.Element.js b/modules/ve/ui/ve.ui.Element.js
new file mode 100644
index 0000000..3d01262
--- /dev/null
+++ b/modules/ve/ui/ve.ui.Element.js
@@ -0,0 +1,44 @@
+/*!
+ * VisualEditor UserInterface Element class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * Creates an ve.ui.Element object.
+ *
+ * @class
+ * @abstract
+ *
+ * @constructor
+ * @param {Object} [config] Config options
+ * @cfg {Function} [$$=$] jQuery for the frame the widget is in
+ */
+ve.ui.Element = function VeUiElement( config ) {
+       // Initialize config
+       config = ve.extendObject( { '$$': $ }, config );
+
+       // Properties
+       this.$$ = config.$$;
+       this.$ = this.$$( '<' + this.constructor.static.tagName + '>' );
+};
+
+/* Static Properties */
+
+/**
+ * @static
+ * @property
+ * @inheritable
+ */
+ve.ui.Element.static = {};
+
+/**
+ * HTML element name.
+ *
+ * @static
+ * @property
+ * @type {string}
+ * @inheritable
+ */
+ve.ui.Element.static.tagName = 'div';
diff --git a/modules/ve/ui/ve.ui.Layout.js b/modules/ve/ui/ve.ui.Layout.js
new file mode 100644
index 0000000..0d41037
--- /dev/null
+++ b/modules/ve/ui/ve.ui.Layout.js
@@ -0,0 +1,37 @@
+/*!
+ * VisualEditor UserInterface Layout class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * Creates an ve.ui.Layout object.
+ *
+ * @class
+ * @abstract
+ * @extends ve.ui.Element
+ * @mixin ve.EventEmitter
+ *
+ * @constructor
+ * @param {Object} [config] Config options
+ */
+ve.ui.Layout = function VeUiLayout( config ) {
+       // Initialize config
+       config = config || {};
+
+       // Parent constructor
+       ve.EventEmitter.call( this );
+
+       // Mixin constructors
+       ve.ui.Element.call( this, config );
+
+       // Initialization
+       this.$.addClass( 've-ui-layout' );
+};
+
+/* Inheritance */
+
+ve.inheritClass( ve.ui.Layout, ve.ui.Element );
+
+ve.mixinClass( ve.ui.Layout, ve.EventEmitter );
diff --git a/modules/ve/ui/ve.ui.Widget.js b/modules/ve/ui/ve.ui.Widget.js
index cfdfdc9..8bf0211 100644
--- a/modules/ve/ui/ve.ui.Widget.js
+++ b/modules/ve/ui/ve.ui.Widget.js
@@ -10,23 +10,24 @@
  *
  * @class
  * @abstract
- * @extends ve.EventEmitter
+ * @extends ve.ui.Element
+ * @mixin ve.EventEmitter
  *
  * @constructor
  * @param {Object} [config] Config options
- * @cfg {Function} [$$=$] jQuery for the frame the widget is in
  * @cfg {boolean} [disabled=false] Disable
  */
 ve.ui.Widget = function VeUiWidget( config ) {
        // Initialize config
-       config = ve.extendObject( { '$$': $, 'disabled': false }, config );
+       config = ve.extendObject( { 'disabled': false }, config );
 
        // Parent constructor
        ve.EventEmitter.call( this );
 
+       // Mixin constructors
+       ve.ui.Element.call( this, config );
+
        // Properties
-       this.$$ = config.$$;
-       this.$ = this.$$( '<' + this.constructor.static.tagName + '>' );
        this.disabled = config.disabled;
 
        // Initialization
@@ -36,19 +37,9 @@
 
 /* Inheritance */
 
-ve.inheritClass( ve.ui.Widget, ve.EventEmitter );
+ve.inheritClass( ve.ui.Widget, ve.ui.Element );
 
-/* Static Properties */
-
-/**
- * HTML element name.
- *
- * @static
- * @property
- * @type {string}
- * @inheritable
- */
-ve.ui.Widget.static.tagName = 'div';
+ve.mixinClass( ve.ui.Widget, ve.EventEmitter );
 
 /* Methods */
 
diff --git a/modules/ve/ui/widgets/ve.ui.OutlineItemWidget.js 
b/modules/ve/ui/widgets/ve.ui.OutlineItemWidget.js
new file mode 100644
index 0000000..29e5bc8
--- /dev/null
+++ b/modules/ve/ui/widgets/ve.ui.OutlineItemWidget.js
@@ -0,0 +1,113 @@
+/*!
+ * VisualEditor UserInterface OutlineItemWidget class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * Creates an ve.ui.OutlineItemWidget object.
+ *
+ * @class
+ * @extends ve.ui.Widget
+ * @mixins ve.ui.LabeledWidget
+ *
+ * @constructor
+ * @param {jQuery|string} label Item label
+ * @param {Mixed} data Item data
+ * @param {Object} [config] Config options
+ * @cfg {string} [rel] Value for `rel` attribute in DOM, allowing per-item 
styling
+ * @cfg {boolean} [selected] Select item
+ */
+ve.ui.OutlineItemWidget = function VeUiOutlineItemWidget( label, data, config 
) {
+       // Config intialization
+       config = ve.extendObject( { 'selected': false }, config );
+
+       // Parent constructor
+       ve.ui.Widget.call( this, config );
+
+       // Mixin constructors
+       ve.ui.LabeledWidget.call( this, this.$$( '<span>' ), config );
+
+       // Properties
+       this.label = label;
+       this.data = data;
+       this.selected = config.selected;
+
+       // Events
+       this.$.on( 'click', ve.bind( this.onClick, this ) );
+
+       // Initialization
+       this.setLabel( this.label );
+       this.$label.addClass( 've-ui-outlineItemWidget-label' );
+       this.$.addClass( 've-ui-outlineItemWidget' ).append( this.$label );
+       this.$.attr( 'rel', config.rel );
+};
+
+/* Inheritance */
+
+ve.inheritClass( ve.ui.OutlineItemWidget, ve.ui.Widget );
+
+ve.mixinClass( ve.ui.OutlineItemWidget, ve.ui.LabeledWidget );
+
+/* Events */
+
+/**
+ * @event select
+ */
+
+/* Static Properties */
+
+ve.ui.OutlineItemWidget.static.tagName = 'li';
+
+/* Methods */
+
+/**
+ * Handle mouse click events.
+ *
+ * @method
+ * @private
+ * @param {jQuery.Event} e Mouse click event
+ */
+ve.ui.OutlineItemWidget.prototype.onClick = function () {
+       this.setSelected( true );
+       return false;
+};
+
+/**
+ * Get item data.
+ *
+ * @method
+ * @returns {Mixed} Item data
+ */
+ve.ui.OutlineItemWidget.prototype.getData = function () {
+       return this.data;
+};
+
+/**
+ * Check if item is selected.
+ *
+ * @method
+ * @returns {boolean} Item is selected
+ */
+ve.ui.OutlineItemWidget.prototype.isSelected = function () {
+       return this.selected;
+};
+
+/**
+ * Set selected state.
+ *
+ * @method
+ * @param {boolean} [state=false] Select item
+ * @chainable
+ */
+ve.ui.OutlineItemWidget.prototype.setSelected = function ( state ) {
+       this.selected = !!state;
+       if ( this.selected ) {
+               this.emit( 'select' );
+               this.$.addClass( 've-ui-outlineItemWidget-selected' );
+       } else {
+               this.$.removeClass( 've-ui-outlineItemWidget-selected' );
+       }
+       return this;
+};
diff --git a/modules/ve/ui/widgets/ve.ui.OutlineWidget.js 
b/modules/ve/ui/widgets/ve.ui.OutlineWidget.js
new file mode 100644
index 0000000..7e6cf20
--- /dev/null
+++ b/modules/ve/ui/widgets/ve.ui.OutlineWidget.js
@@ -0,0 +1,182 @@
+/*!
+ * VisualEditor UserInterface OutlineWidget class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * Create an ve.ui.OutlineWidget object.
+ *
+ * @class
+ * @extends ve.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Config options
+ */
+ve.ui.OutlineWidget = function VeUiOutlineWidget( config ) {
+       // Config intialization
+       config = config || {};
+
+       // Parent constructor
+       ve.ui.Widget.call( this, config );
+
+       // Properties
+       this.sequence = [];
+       this.$items = this.$$( '<ul>' ).addClass( 've-ui-outlineWidget-items' );
+
+       // Initialization
+       this.$.hide().addClass( 've-ui-outlineWidget' ).append( this.$items );
+};
+
+/* Inheritance */
+
+ve.inheritClass( ve.ui.OutlineWidget, ve.ui.Widget );
+
+/* Events */
+
+/**
+ * @event select
+ * @param {ve.ui.OutlineItemWidget} item Selected item
+ */
+
+/* Methods */
+
+/**
+ * Handle item select events.
+ *
+ * @method
+ * @private
+ * @param {ve.ui.OutlineItemWidget} item Selected item
+ */
+ve.ui.OutlineWidget.prototype.onItemSelect = function ( item ) {
+       var i, len;
+
+       if ( !this.disabled ) {
+               // Make selection mutually exclusive
+               for ( i = 0, len = this.items.length; i < len; i++ ) {
+                       if ( this.items[i] !== item ) {
+                               this.items[i].setSelected( false );
+                       }
+               }
+               this.emit( 'select', item );
+       }
+};
+
+/**
+ * Get items.
+ *
+ * @method
+ * @returns {ve.ui.OutlineItemWidget[]} Items
+ */
+ve.ui.OutlineWidget.prototype.getItems = function () {
+       return this.items.slice( 0 );
+};
+
+/**
+ * Get selected item.
+ *
+ * @method
+ * @returns {ve.ui.OutlineItemWidget|null} Selected item
+ */
+ve.ui.OutlineWidget.prototype.getSelectedItem = function () {
+       var i, len;
+
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               if ( this.items[i].isSelected() ) {
+                       return this.items[i];
+               }
+       }
+       return null;
+};
+
+/**
+ * Select an item.
+ *
+ * @method
+ * @param {ve.ui.OutlineItemWidget} [item] Item to select, omit to deselect all
+ * @chainable
+ */
+ve.ui.OutlineWidget.prototype.selectItem = function ( item ) {
+       var i, len;
+
+       if ( item ) {
+               item.setSelected( true );
+       } else {
+               // Deselect all
+               for ( i = 0, len = this.items.length; i < len; i++ ) {
+                       this.items[i].setSelected( false );
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Add items.
+ *
+ * Adding an existing item (by value) will move it.
+ *
+ * @method
+ * @param {ve.ui.OutlineItemWidget[]} items Item
+ * @chainable
+ */
+ve.ui.OutlineWidget.prototype.addItems = function ( items ) {
+       var i, len, item;
+
+       for ( i = 0, len = items.length; i < len; i++ ) {
+               item = items[i];
+
+               // Check if item exists then remove it first, effectively 
"moving" it
+               if ( this.items.indexOf( item ) !== -1 ) {
+                       this.removeItems( [item] );
+               }
+               // Add the item
+               this.items.push( item );
+               item.on( 'select', ve.bind( this.onItemSelect, this, item ) );
+       }
+
+       return this;
+};
+
+/**
+ * Remove items.
+ *
+ * Items will be detached, not removed, so they can be used later.
+ *
+ * @method
+ * @param {ve.ui.OutlineItemWidget[]} items Items to remove
+ * @chainable
+ */
+ve.ui.OutlineWidget.prototype.removeItems = function ( items ) {
+       var i, len, item, index;
+
+       // Remove specific items
+       for ( i = 0, len = items.length; i < len; i++ ) {
+               item = items[i];
+               index = this.items.indexOf( item );
+               if ( index !== -1 ) {
+                       this.items.splice( index, 1 );
+                       item.$.detach();
+                       item.removeAllListeners( 'select' );
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Clear all items.
+ *
+ * Items will be detached, not removed, so they can be used later.
+ *
+ * @method
+ * @chainable
+ */
+ve.ui.OutlineWidget.prototype.clearItems = function () {
+       // Remove all items, leaving empty groups
+       this.items = [];
+       this.$items.children().detach();
+
+       return this;
+};

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6b92c0204e176c914c26eff8c03ea417578e080c
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Trevor Parscal <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to