r105634 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r105633‎ | r105634 | r105635 >
Date:01:28, 9 December 2011
Author:tparscal
Status:deferred (Comments)
Tags:
Comment:
Added es.Inspector and es.LinkInspector - and a whole bunch of other changes (some are hacks) to get a link inspector working nicely
Modified paths:
  • /trunk/extensions/VisualEditor/VisualEditor.php (modified) (history)
  • /trunk/extensions/VisualEditor/demo/index.html (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/bases/es.Inspector.js (added) (history)
  • /trunk/extensions/VisualEditor/modules/es/images/close.png (added) (history)
  • /trunk/extensions/VisualEditor/modules/es/images/icons.psd (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/inspectors (added) (history)
  • /trunk/extensions/VisualEditor/modules/es/inspectors/es.LinkInspector.js (added) (history)
  • /trunk/extensions/VisualEditor/modules/es/styles/es.Inspector.css (added) (history)
  • /trunk/extensions/VisualEditor/modules/es/tools/es.AnnotationButtonTool.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/tools/es.ClearButtonTool.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/tools/es.DropdownTool.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/views/es.ContextView.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/views/es.MenuView.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/views/es.SurfaceView.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/views/es.ToolbarView.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/sandbox/sandbox.js (modified) (history)

Diff [purge]

Index: trunk/extensions/VisualEditor/VisualEditor.php
@@ -73,6 +73,7 @@
7474 'es/bases/es.DocumentViewNode.js',
7575 'es/bases/es.DocumentViewBranchNode.js',
7676 'es/bases/es.DocumentViewLeafNode.js',
 77+ 'es/bases/es.Inspector.js',
7778 'es/bases/es.Tool.js',
7879 'es/models/es.DocumentModel.js',
7980 'es/models/es.HeadingModel.js',
@@ -89,6 +90,15 @@
9091 'es/serializers/es.HtmlSerializer.js',
9192 'es/serializers/es.JsonSerializer.js',
9293 'es/serializers/es.WikitextSerializer.js',
 94+ 'es/inspectors/es.LinkInspector.js',
 95+ 'es/tools/es.ButtonTool.js',
 96+ 'es/tools/es.AnnotationButtonTool.js',
 97+ 'es/tools/es.ClearButtonTool.js',
 98+ 'es/tools/es.HistoryButtonTool.js',
 99+ 'es/tools/es.ListButtonTool.js',
 100+ 'es/tools/es.IndentationButtonTool.js',
 101+ 'es/tools/es.DropdownTool.js',
 102+ 'es/tools/es.FormatDropdownTool.js',
93103 'es/views/es.ContextView.js',
94104 'es/views/es.ContentView.js',
95105 'es/views/es.DocumentView.js',
@@ -103,14 +113,6 @@
104114 'es/views/es.TableRowView.js',
105115 'es/views/es.TableView.js',
106116 'es/views/es.ToolbarView.js',
107 - 'es/tools/es.ButtonTool.js',
108 - 'es/tools/es.AnnotationButtonTool.js',
109 - 'es/tools/es.ClearButtonTool.js',
110 - 'es/tools/es.HistoryButtonTool.js',
111 - 'es/tools/es.ListButtonTool.js',
112 - 'es/tools/es.IndentationButtonTool.js',
113 - 'es/tools/es.DropdownTool.js',
114 - 'es/tools/es.FormatDropdownTool.js'
115117
116118 ),
117119 'styles' => array(
@@ -118,6 +120,7 @@
119121 'es/styles/es.ContextView.css',
120122 'es/styles/es.ContentView.css',
121123 'es/styles/es.DocumentView.css',
 124+ 'es/styles/es.Inspector.css',
122125 'es/styles/es.ToolbarView.css',
123126 'es/styles/es.MenuView.css',
124127 ),
Index: trunk/extensions/VisualEditor/demo/index.html
@@ -7,6 +7,7 @@
88 <link rel="stylesheet" href="../modules/es/styles/es.ContextView.css">
99 <link rel="stylesheet" href="../modules/es/styles/es.ContentView.css">
1010 <link rel="stylesheet" href="../modules/es/styles/es.DocumentView.css">
 11+ <link rel="stylesheet" href="../modules/es/styles/es.Inspector.css">
1112 <link rel="stylesheet" href="../modules/es/styles/es.ToolbarView.css">
1213 <link rel="stylesheet" href="../modules/es/styles/es.MenuView.css">
1314 <link rel="stylesheet" href="../modules/sandbox/sandbox.css">
@@ -112,6 +113,7 @@
113114 <script src="../modules/es/bases/es.DocumentViewNode.js"></script>
114115 <script src="../modules/es/bases/es.DocumentViewBranchNode.js"></script>
115116 <script src="../modules/es/bases/es.DocumentViewLeafNode.js"></script>
 117+ <script src="../modules/es/bases/es.Inspector.js"></script>
116118 <script src="../modules/es/bases/es.Tool.js"></script>
117119
118120 <!-- Models -->
@@ -127,6 +129,19 @@
128130 <script src="../modules/es/models/es.HeadingModel.js"></script>
129131 <script src="../modules/es/models/es.TransactionModel.js"></script>
130132
 133+ <!-- Inspectors -->
 134+ <script src="../modules/es/inspectors/es.LinkInspector.js"></script>
 135+
 136+ <!-- Tools -->
 137+ <script src="../modules/es/tools/es.ButtonTool.js"></script>
 138+ <script src="../modules/es/tools/es.AnnotationButtonTool.js"></script>
 139+ <script src="../modules/es/tools/es.ClearButtonTool.js"></script>
 140+ <script src="../modules/es/tools/es.HistoryButtonTool.js"></script>
 141+ <script src="../modules/es/tools/es.ListButtonTool.js"></script>
 142+ <script src="../modules/es/tools/es.IndentationButtonTool.js"></script>
 143+ <script src="../modules/es/tools/es.DropdownTool.js"></script>
 144+ <script src="../modules/es/tools/es.FormatDropdownTool.js"></script>
 145+
131146 <!-- Views -->
132147 <script src="../modules/es/views/es.SurfaceView.js"></script>
133148 <script src="../modules/es/views/es.ToolbarView.js"></script>
@@ -143,15 +158,6 @@
144159 <script src="../modules/es/views/es.TableCellView.js"></script>
145160 <script src="../modules/es/views/es.HeadingView.js"></script>
146161
147 - <script src="../modules/es/tools/es.ButtonTool.js"></script>
148 - <script src="../modules/es/tools/es.AnnotationButtonTool.js"></script>
149 - <script src="../modules/es/tools/es.ClearButtonTool.js"></script>
150 - <script src="../modules/es/tools/es.HistoryButtonTool.js"></script>
151 - <script src="../modules/es/tools/es.ListButtonTool.js"></script>
152 - <script src="../modules/es/tools/es.IndentationButtonTool.js"></script>
153 - <script src="../modules/es/tools/es.DropdownTool.js"></script>
154 - <script src="../modules/es/tools/es.FormatDropdownTool.js"></script>
155 -
156162 <!-- Demo -->
157163 <script src="../modules/sandbox/sandbox.js"></script>
158164 </body>
Index: trunk/extensions/VisualEditor/modules/es/tools/es.ClearButtonTool.js
@@ -19,13 +19,16 @@
2020 /* Methods */
2121
2222 es.ClearButtonTool.prototype.onClick = function() {
23 - var tx = this.toolbar.surfaceView.model.getDocument().prepareContentAnnotation(
24 - this.toolbar.surfaceView.currentSelection,
25 - 'clear',
26 - this.pattern
27 - );
28 - this.toolbar.surfaceView.model.transact( tx );
29 - this.toolbar.surfaceView.clearInsertionAnnotations();
 23+ var surfaceView = this.toolbar.getSurfaceView(),
 24+ surfaceModel = surfaceView.getModel(),
 25+ tx =surfaceModel.getDocument().prepareContentAnnotation(
 26+ surfaceView.currentSelection,
 27+ 'clear',
 28+ this.pattern
 29+ );
 30+ surfaceModel.transact( tx );
 31+ surfaceView.clearInsertionAnnotations();
 32+ surfaceView.getContextView().closeInspector();
3033 };
3134
3235 es.ClearButtonTool.prototype.updateState = function( annotations ) {
Index: trunk/extensions/VisualEditor/modules/es/tools/es.AnnotationButtonTool.js
@@ -13,14 +13,23 @@
1414 es.ButtonTool.call( this, toolbar, name );
1515
1616 // Properties
17 - this.annotation = data;
 17+ this.annotation = data.annotation;
 18+ this.inspector = data.inspector;
1819 this.active = false;
1920 };
2021
2122 /* Methods */
2223
2324 es.AnnotationButtonTool.prototype.onClick = function() {
24 - this.toolbar.getSurfaceView().annotate( this.active ? 'clear' : 'set', this.annotation );
 25+ var surfaceView = this.toolbar.getSurfaceView();
 26+ if ( this.inspector ) {
 27+ if ( !this.active && surfaceView.getModel().getSelection().getLength() ) {
 28+ surfaceView.annotate( 'set', this.annotation );
 29+ }
 30+ this.toolbar.getSurfaceView().getContextView().openInspector( this.inspector );
 31+ } else {
 32+ surfaceView.annotate( this.active ? 'clear' : 'set', this.annotation );
 33+ }
2534 };
2635
2736 es.AnnotationButtonTool.prototype.updateState = function( annotations, nodes ) {
@@ -36,21 +45,28 @@
3746 /* Registration */
3847
3948 es.Tool.tools.bold = {
40 - constructor: es.AnnotationButtonTool,
41 - name: 'bold',
42 - data: { 'type': 'textStyle/bold' }
 49+ 'constructor': es.AnnotationButtonTool,
 50+ 'name': 'bold',
 51+ 'data': {
 52+ 'annotation': { 'type': 'textStyle/bold' }
 53+ }
4354 };
4455
4556 es.Tool.tools.italic = {
46 - constructor: es.AnnotationButtonTool,
47 - name: 'italic',
48 - data: { 'type': 'textStyle/italic' }
 57+ 'constructor': es.AnnotationButtonTool,
 58+ 'name': 'italic',
 59+ 'data': {
 60+ 'annotation': { 'type': 'textStyle/italic' }
 61+ }
4962 };
5063
5164 es.Tool.tools.link = {
52 - constructor: es.AnnotationButtonTool,
53 - name: 'link',
54 - data: { 'type': 'link/internal', 'data': { 'title': '' } }
 65+ 'constructor': es.AnnotationButtonTool,
 66+ 'name': 'link',
 67+ 'data': {
 68+ 'annotation': { 'type': 'link/internal', 'data': { 'title': '' } },
 69+ 'inspector': 'link'
 70+ }
5571 };
5672
5773 /* Inheritance */
Index: trunk/extensions/VisualEditor/modules/es/tools/es.DropdownTool.js
@@ -27,7 +27,7 @@
2828 .add( this.toolbar.surfaceView.$ )
2929 .mousedown( function( e ) {
3030 if ( e.which === 1 ) {
31 - _this.menuView.hide();
 31+ _this.menuView.close();
3232 }
3333 } );
3434 this.$.bind( {
@@ -41,7 +41,7 @@
4242 // Don't respond to menu clicks
4343 var $item = $( e.target ).closest( '.es-menuView' );
4444 if ( e.which === 1 && $item.length === 0 ) {
45 - _this.menuView.toggle();
 45+ _this.menuView.open();
4646 }
4747 }
4848 } );
Index: trunk/extensions/VisualEditor/modules/es/styles/es.Inspector.css
@@ -0,0 +1,104 @@
 2+.es-inspector {
 3+ display: none;
 4+ position: absolute;
 5+ border: solid 1px #cccccc;
 6+ -webkit-border-radius: 0.25em;
 7+ -moz-border-radius: 0.25em;
 8+ -o-border-radius: 0.25em;
 9+ border-radius: 0.25em;
 10+ background-color: rgba(255,255,255,0.95);
 11+ -webkit-box-shadow: 0 0.25em 1em 0 rgba(0,0,0,0.25);
 12+ -moz-box-shadow: 0 0.25em 1em 0 rgba(0,0,0,0.25);
 13+ box-shadow: 0 0.25em 1em 0 rgba(0,0,0,0.25);
 14+ padding: 0.75em;
 15+ padding-top: 2.5em;
 16+ min-width: 15em;
 17+ z-index: 3;
 18+}
 19+
 20+.es-inspector-closeButton,
 21+.es-inspector-clearButton {
 22+ position: absolute;
 23+ top: 0.25em;
 24+ width: 2em;
 25+ height: 2em;
 26+ background-position: center center;
 27+ background-repeat: no-repeat;
 28+ cursor: pointer;
 29+ -moz-opacity: 0.25;
 30+ filter:alpha(opacity=25);
 31+ opacity: 0.25;
 32+}
 33+
 34+.es-inspector-closeButton {
 35+ right: 0.25em;
 36+ background-image: url(../images/close.png);
 37+}
 38+
 39+.es-inspector-clearButton {
 40+ right: 2.25em;
 41+ background-image: url(../images/clear.png);
 42+}
 43+
 44+.es-inspector-closeButton:hover,
 45+.es-inspector-clearButton:hover {
 46+ -moz-opacity: 1;
 47+ filter:alpha(opacity=100);
 48+ opacity: 1;
 49+}
 50+
 51+.es-inspector-doneButton {
 52+ float: right;
 53+ margin: 0.5em 0 0 0;
 54+ padding: 0.5em 0.75em;
 55+ font-size: 1em;
 56+ border: solid 1px #cccccc;
 57+ -webkit-border-radius: 0.5em;
 58+ -moz-border-radius: 0.5em;
 59+ -o-border-radius: 0.5em;
 60+ border-radius: 0.5em;
 61+ cursor: pointer;
 62+ background: #f7fbff; /* Old browsers */
 63+ /* IE9 SVG, needs conditional override of 'filter' to 'none' */
 64+ background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2Y3ZmJmZiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNkZGYyZmYiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
 65+ background: -moz-linear-gradient(top, #f7fbff 0%, #ddf2ff 100%); /* FF3.6+ */
 66+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f7fbff), color-stop(100%,#ddf2ff)); /* Chrome,Safari4+ */
 67+ background: -webkit-linear-gradient(top, #f7fbff 0%,#ddf2ff 100%); /* Chrome10+,Safari5.1+ */
 68+ background: -o-linear-gradient(top, #f7fbff 0%,#ddf2ff 100%); /* Opera 11.10+ */
 69+ background: -ms-linear-gradient(top, #f7fbff 0%,#ddf2ff 100%); /* IE10+ */
 70+ background: linear-gradient(top, #f7fbff 0%,#ddf2ff 100%); /* W3C */
 71+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f7fbff', endColorstr='#ddf2ff',GradientType=0 ); /* IE6-8 */
 72+}
 73+
 74+.es-inspector-doneButton:hover {
 75+ border-color: #dddddd;
 76+}
 77+
 78+.es-inspector form {
 79+ margin: 0;
 80+ padding: 0.75em 0 0 0;
 81+ border-top: solid 1px #dddddd;
 82+ white-space: nowrap;
 83+}
 84+
 85+.es-inspector form input {
 86+ display: inline-block;
 87+ width: 14em;
 88+ font-size: 1em;
 89+ padding: 0.25em;
 90+}
 91+
 92+.es-inspector form label {
 93+ display: inline-block;
 94+ width: 6em;
 95+}
 96+
 97+.es-inspector-title {
 98+ position: absolute;
 99+ top: 0;
 100+ left: 0.75em;
 101+ height: 2.5em;
 102+ line-height: 2.5em;
 103+ font-weight: bold;
 104+ color: #666666;
 105+}
Index: trunk/extensions/VisualEditor/modules/es/images/close.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/extensions/VisualEditor/modules/es/images/close.png
___________________________________________________________________
Added: svn:mime-type
1106 + application/octet-stream
Index: trunk/extensions/VisualEditor/modules/es/images/icons.psd
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: trunk/extensions/VisualEditor/modules/es/inspectors/es.LinkInspector.js
@@ -0,0 +1,88 @@
 2+/**
 3+ * Creates an es.LinkInspector object.
 4+ *
 5+ * @class
 6+ * @constructor
 7+ * @param {es.ToolbarView} toolbar
 8+ */
 9+es.LinkInspector = function( toolbar, context ) {
 10+ // Inheritance
 11+ es.Inspector.call( this, toolbar, context );
 12+
 13+ // Properties
 14+ this.$clearButton = $( '<div class="es-inspector-clearButton"></div>' ).prependTo( this.$ );
 15+ this.$.prepend( '<div class="es-inspector-title">Link inspector</div>' );
 16+ this.$form = $( '<form></form>' ).appendTo( this.$ );
 17+ this.$locationLabel = $( '<label>Page title</label>' ).appendTo( this.$form );
 18+ this.$locationInput = $( '<input type="text">' ).appendTo( this.$form );
 19+ this.$doneButton = $( '<div class="es-inspector-doneButton">Done</div>' )
 20+ .appendTo( this.$ );
 21+
 22+ // Events
 23+ var _this = this;
 24+ this.$clearButton.click( function() {
 25+ var surfaceView = _this.context.getSurfaceView(),
 26+ surfaceModel = surfaceView.getModel(),
 27+ tx = surfaceModel.getDocument().prepareContentAnnotation(
 28+ surfaceView.currentSelection,
 29+ 'clear',
 30+ /link\/.*/
 31+ );
 32+ surfaceModel.transact( tx );
 33+ _this.context.closeInspector();
 34+ } );
 35+ this.$doneButton.click( function() {
 36+ _this.close();
 37+ } );
 38+};
 39+
 40+/* Methods */
 41+
 42+es.LinkInspector.prototype.getTitleFromSelection = function() {
 43+ var surfaceView = this.context.getSurfaceView(),
 44+ surfaceModel = surfaceView.getModel(),
 45+ documentModel = surfaceModel.getDocument(),
 46+ data = documentModel.getData( surfaceView.currentSelection );
 47+ if ( data.length ) {
 48+ var annotation = es.DocumentModel.getMatchingAnnotations( data[0], /link\/.*/ );
 49+ if ( annotation.length ) {
 50+ annotation = annotation[0];
 51+ }
 52+ if ( annotation ) {
 53+ return annotation.data.title;
 54+ }
 55+ }
 56+ return null;
 57+};
 58+
 59+es.LinkInspector.prototype.onOpen = function() {
 60+ var title = this.getTitleFromSelection();
 61+ if ( title !== null ) {
 62+ this.$locationInput.val( title );
 63+ }
 64+};
 65+
 66+es.LinkInspector.prototype.onClose = function() {
 67+ var title = this.$locationInput.val();
 68+ if ( title === this.getTitleFromSelection() ) {
 69+ return;
 70+ }
 71+ var surfaceView = this.context.getSurfaceView(),
 72+ surfaceModel = surfaceView.getModel();
 73+ var clear = surfaceModel.getDocument().prepareContentAnnotation(
 74+ surfaceView.currentSelection,
 75+ 'clear',
 76+ /link\/.*/
 77+ );
 78+ surfaceModel.transact( clear );
 79+ var set = surfaceModel.getDocument().prepareContentAnnotation(
 80+ surfaceView.currentSelection,
 81+ 'set',
 82+ { 'type': 'link/internal', 'data': { 'title': title } }
 83+ );
 84+ surfaceModel.transact( set );
 85+};
 86+
 87+/* Inheritance */
 88+
 89+es.extendClass( es.LinkInspector, es.Inspector );
Index: trunk/extensions/VisualEditor/modules/es/bases/es.Inspector.js
@@ -0,0 +1,50 @@
 2+/**
 3+ * Creates an es.Inspector object.
 4+ *
 5+ * @class
 6+ * @constructor
 7+ * @param {es.ToolbarView} toolbar
 8+ * @param {String} name
 9+ */
 10+es.Inspector = function( toolbar, context ) {
 11+ // Inheritance
 12+ es.EventEmitter.call( this );
 13+ if ( !toolbar || !context ) {
 14+ return;
 15+ }
 16+
 17+ // Properties
 18+ this.toolbar = toolbar;
 19+ this.context = context;
 20+ this.$ = $( '<div class="es-inspector"></div>' );
 21+ this.$closeButton = $( '<div class="es-inspector-closeButton"></div>' ).appendTo( this.$ );
 22+
 23+ // Events
 24+ var _this = this;
 25+ this.$closeButton.click( function() {
 26+ _this.context.closeInspector();
 27+ } );
 28+};
 29+
 30+/* Methods */
 31+
 32+es.Inspector.prototype.open = function() {
 33+ this.$.show();
 34+ this.context.closeMenu();
 35+ if ( this.onOpen ) {
 36+ this.onOpen();
 37+ }
 38+ this.emit( 'open' );
 39+};
 40+
 41+es.Inspector.prototype.close = function() {
 42+ this.$.hide();
 43+ if ( this.onClose ) {
 44+ this.onClose();
 45+ }
 46+ this.emit( 'close' );
 47+};
 48+
 49+/* Inheritance */
 50+
 51+es.extendClass( es.Inspector, es.EventEmitter );
Index: trunk/extensions/VisualEditor/modules/es/views/es.ContextView.js
@@ -6,13 +6,21 @@
77 * @param {jQuery} $overlay DOM selection to add nodes to
88 */
99 es.ContextView = function( surfaceView, $overlay ) {
10 - var _this = this;
 10+ // Inheritance
 11+ if ( !surfaceView ) {
 12+ return;
 13+ }
1114
1215 // Properties
1316 this.surfaceView = surfaceView;
 17+ this.surfaceView.attachContextView( this );
 18+ this.inspectors = {};
 19+ this.inspector = null;
 20+ this.position = null;
1421 this.$ = $( '<div class="es-contextView"></div>' ).appendTo( $overlay || $( 'body' ) );
15 - this.$panels = $( '<div class="es-contextView-panels"></div>' ).appendTo( this.$ );
1622 this.$toolbar = $( '<div class="es-contextView-toolbar"></div>' );
 23+ this.$inspectors = $( '<div class="es-contextView-inspectors"></div>' ).appendTo( this.$ );
 24+ this.$icon = $( '<div class="es-contextView-icon"></div>' ).appendTo( this.$ );
1725 this.toolbarView = new es.ToolbarView(
1826 this.$toolbar,
1927 this.surfaceView,
@@ -25,22 +33,9 @@
2634 null,
2735 this.$
2836 );
29 - this.$icon = $( '<div class="es-contextView-icon"></div>' ).appendTo( this.$ );
3037
31 - // Example panel
32 - this.$panels.append(
33 - '<div class="es-contextView-panel" rel="link">' +
34 - '<div><label>Page title or URL <input type="text"></label></div>' +
35 - '<div><a href="#cancel">Cancel</a> <button>Change</button></div>' +
36 - '</div>'
37 - );
38 - this.$panels.find( '[href="#cancel"]' ).click( function( e ) {
39 - _this.$panels.children().hide();
40 - e.preventDefault();
41 - return false;
42 - } );
43 -
4438 // Events
 39+ var _this = this;
4540 this.$icon.bind( {
4641 'mousedown': function( e ) {
4742 if ( e.which === 1 ) {
@@ -50,28 +45,65 @@
5146 },
5247 'mouseup': function( e ) {
5348 if ( e.which === 1 ) {
54 - _this.menuView.toggle();
 49+ if ( _this.inspector ) {
 50+ _this.closeInspector();
 51+ } else {
 52+ if ( _this.isMenuOpen() ) {
 53+ _this.closeMenu();
 54+ } else {
 55+ _this.openMenu();
 56+ }
 57+ }
5558 }
5659 }
5760 } );
 61+
 62+ // Intitialization
 63+ this.addInspector( 'link', new es.LinkInspector( this.toolbarView, this ) );
5864 };
5965
6066 /* Methods */
6167
 68+es.ContextView.prototype.getSurfaceView = function() {
 69+ return this.surfaceView;
 70+};
 71+
 72+es.ContextView.prototype.openMenu = function() {
 73+ this.menuView.open();
 74+};
 75+
 76+es.ContextView.prototype.closeMenu = function() {
 77+ this.menuView.close();
 78+};
 79+
 80+es.ContextView.prototype.isMenuOpen = function() {
 81+ return this.menuView.isOpen();
 82+};
 83+
6284 es.ContextView.prototype.set = function() {
6385 this.$.removeClass(
6486 'es-contextView-position-below es-contextView-position-above ' +
6587 'es-contextView-position-left es-contextView-position-right ' +
6688 'es-contextView-position-start es-contextView-position-end'
6789 );
 90+ this.positionIcon();
 91+ if ( this.position ) {
 92+ this.positionOverlay( this.menuView.$ );
 93+ if ( this.inspector ) {
 94+ this.positionOverlay( this.inspectors[this.inspector].$ );
 95+ }
 96+ }
 97+};
 98+
 99+es.ContextView.prototype.positionIcon = function() {
68100 var selection = this.surfaceView.getModel().getSelection(),
69 - position,
70101 offset;
 102+ this.position = null;
71103 if ( selection.from < selection.to ) {
72104 var $lastRange = this.surfaceView.$.find( '.es-contentView-range:visible:last' );
73105 if ( $lastRange.length ) {
74106 offset = $lastRange.offset();
75 - position = new es.Position(
 107+ this.position = new es.Position(
76108 offset.left + $lastRange.width(), offset.top + $lastRange.height()
77109 );
78110 this.$.addClass( 'es-contextView-position-end' );
@@ -80,43 +112,91 @@
81113 var $firstRange = this.surfaceView.$.find( '.es-contentView-range:visible:first' );
82114 if ( $firstRange.length ) {
83115 offset = $firstRange.offset();
84 - position = new es.Position( offset.left, offset.top );
 116+ this.position = new es.Position( offset.left, offset.top );
85117 this.$.addClass( 'es-contextView-position-start' );
86118 }
87119 }
88 - if ( position ) {
89 - var $menu = this.menuView.$,
90 - menuMargin = 5,
91 - menuWidth = $menu.width(),
92 - menuHeight = $menu.height(),
93 - $window = $( window ),
94 - windowWidth = $window.width(),
95 - windowHeight = $window.height(),
96 - windowScrollTop = $window.scrollTop();
97 - // Center align menu
98 - var menuLeft = -Math.round( menuWidth / 2 );
99 - // Adjust menu left or right depending on viewport
100 - if ( ( position.left - menuMargin ) + menuLeft < 0 ) {
101 - // Move right a bit past center
102 - menuLeft -= position.left + menuLeft - menuMargin;
103 - } else if ( ( menuMargin + position.left ) - menuLeft > windowWidth ) {
104 - // Move left a bit past center
105 - menuLeft += windowWidth - menuMargin - ( position.left - menuLeft );
106 - }
107 - $menu.css( 'left', menuLeft );
108 - // Position menu on top or bottom depending on viewport
109 - if ( position.top + menuHeight + ( menuMargin * 2 ) < windowHeight + windowScrollTop ) {
110 - this.$.addClass( 'es-contextView-position-below' );
111 - } else {
112 - this.$.addClass( 'es-contextView-position-above' );
113 - }
114 - this.$.css( { 'left': position.left, 'top': position.top } );
 120+ if ( this.position ) {
 121+ this.$.css( { 'left': this.position.left, 'top': this.position.top } );
115122 this.$icon.fadeIn( 'fast' );
 123+ } else {
 124+ this.$icon.hide();
116125 }
117126 };
118127
 128+es.ContextView.prototype.positionOverlay = function( $overlay ) {
 129+ var overlayMargin = 5,
 130+ overlayWidth = $overlay.outerWidth(),
 131+ overlayHeight = $overlay.outerHeight(),
 132+ $window = $( window ),
 133+ windowWidth = $window.width(),
 134+ windowHeight = $window.height(),
 135+ windowScrollTop = $window.scrollTop();
 136+ // Center align overlay
 137+ var overlayLeft = -Math.round( overlayWidth / 2 );
 138+ // Adjust overlay left or right depending on viewport
 139+ if ( ( this.position.left - overlayMargin ) + overlayLeft < 0 ) {
 140+ // Move right a bit past center
 141+ overlayLeft -= this.position.left + overlayLeft - overlayMargin;
 142+ } else if ( ( overlayMargin + this.position.left ) - overlayLeft > windowWidth ) {
 143+ // Move left a bit past center
 144+ overlayLeft += windowWidth - overlayMargin - ( this.position.left - overlayLeft );
 145+ }
 146+ $overlay.css( 'left', overlayLeft );
 147+ // Position overlay on top or bottom depending on viewport
 148+ if ( this.position.top + overlayHeight + ( overlayMargin * 2 ) < windowHeight + windowScrollTop ) {
 149+ this.$.addClass( 'es-contextView-position-below' );
 150+ } else {
 151+ this.$.addClass( 'es-contextView-position-above' );
 152+ }
 153+};
 154+
119155 es.ContextView.prototype.clear = function() {
120 - this.$panels.hide().children().hide();
 156+ if ( this.inspector ) {
 157+ this.closeInspector();
 158+ }
121159 this.$icon.hide();
122 - this.menuView.hide();
 160+ this.menuView.close();
123161 };
 162+
 163+es.ContextView.prototype.openInspector = function( name ) {
 164+ if ( !( name in this.inspectors ) ) {
 165+ throw 'Missing inspector error. Can not open nonexistent inspector: ' + name;
 166+ }
 167+ this.inspectors[name].open();
 168+ this.$inspectors.show();
 169+ this.positionOverlay( this.inspectors[name].$ );
 170+ this.inspector = name;
 171+};
 172+
 173+es.ContextView.prototype.closeInspector = function() {
 174+ if ( this.inspector ) {
 175+ this.inspectors[this.inspector].close();
 176+ this.$inspectors.hide();
 177+ this.inspector = null;
 178+ }
 179+};
 180+
 181+es.ContextView.prototype.getInspector = function( name ) {
 182+ if ( name in this.inspectors ) {
 183+ return this.inspectors[name];
 184+ }
 185+ return null;
 186+};
 187+
 188+es.ContextView.prototype.addInspector = function( name, inspector ) {
 189+ if ( name in this.inspectors ) {
 190+ throw 'Duplicate inspector error. Previous registration with the same name: ' + name;
 191+ }
 192+ this.inspectors[name] = inspector;
 193+ this.$inspectors.append( inspector.$ );
 194+};
 195+
 196+es.ContextView.prototype.removeInspector = function( name ) {
 197+ if ( name in this.inspectors ) {
 198+ throw 'Missing inspector error. Can not remove nonexistent inspector: ' + name;
 199+ }
 200+ this.inspectors[name].detach();
 201+ delete this.inspectors[name];
 202+ this.inspector = null;
 203+};
Index: trunk/extensions/VisualEditor/modules/es/views/es.MenuView.js
@@ -102,16 +102,16 @@
103103 return this.$.css( { 'top': position.top, 'left': position.left } );
104104 };
105105
106 -es.MenuView.prototype.show = function() {
 106+es.MenuView.prototype.open = function() {
107107 this.$.show();
108108 };
109109
110 -es.MenuView.prototype.toggle = function() {
111 - this.$.toggle();
 110+es.MenuView.prototype.close = function() {
 111+ this.$.hide();
112112 };
113113
114 -es.MenuView.prototype.hide = function() {
115 - this.$.hide();
 114+es.MenuView.prototype.isOpen = function() {
 115+ return this.$.is( ':visible' );
116116 };
117117
118118 es.MenuView.prototype.onSelect = function( item, event ) {
Index: trunk/extensions/VisualEditor/modules/es/views/es.SurfaceView.js
@@ -19,7 +19,7 @@
2020 this.model = model;
2121 this.currentSelection = new es.Range();
2222 this.documentView = new es.DocumentView( this.model.getDocument(), this );
23 - this.contextView = new es.ContextView( this );
 23+ this.contextView = null;
2424 this.$ = $container
2525 .addClass( 'es-surfaceView' )
2626 .append( this.documentView.$ );
@@ -146,10 +146,12 @@
147147 } );
148148 $window.scroll( function() {
149149 _this.dimensions.scrollTop = $window.scrollTop();
150 - if ( _this.currentSelection.getLength() && !_this.mouse.selectingMode ) {
151 - _this.contextView.set();
152 - } else {
153 - _this.contextView.clear();
 150+ if ( _this.contextView ) {
 151+ if ( _this.currentSelection.getLength() && !_this.mouse.selectingMode ) {
 152+ _this.contextView.set();
 153+ } else {
 154+ _this.contextView.clear();
 155+ }
154156 }
155157 } );
156158
@@ -163,6 +165,14 @@
164166
165167 /* Methods */
166168
 169+es.SurfaceView.prototype.attachContextView = function( contextView ) {
 170+ this.contextView = contextView;
 171+};
 172+
 173+es.SurfaceView.prototype.getContextView = function() {
 174+ return this.contextView ;
 175+};
 176+
167177 es.SurfaceView.prototype.annotate = function( method, annotation ) {
168178 if ( method === 'toggle' ) {
169179 var annotations = this.getAnnotations();
@@ -275,10 +285,12 @@
276286 _this.showCursor();
277287 _this.documentView.clearSelection( _this.currentSelection );
278288 }
279 - if ( _this.currentSelection.getLength() && !_this.mouse.selectingMode ) {
280 - _this.contextView.set();
281 - } else {
282 - _this.contextView.clear();
 289+ if ( _this.contextView ) {
 290+ if ( _this.currentSelection.getLength() && !_this.mouse.selectingMode ) {
 291+ _this.contextView.set();
 292+ } else {
 293+ _this.contextView.clear();
 294+ }
283295 }
284296 _this.updateSelectionTimeout = undefined;
285297 }
@@ -417,9 +429,11 @@
418430 if ( e.which === 1 ) { // left mouse button
419431 this.mouse.selectingMode = this.mouse.selectedRange = null;
420432 this.model.select( this.currentSelection, true );
421 - // We have to manually call this because the selection will not have changed between the
422 - // most recent mousemove and this mouseup
423 - this.contextView.set();
 433+ if ( this.contextView ) {
 434+ // We have to manually call this because the selection will not have changed between the
 435+ // most recent mousemove and this mouseup
 436+ this.contextView.set();
 437+ }
424438 }
425439 };
426440
Index: trunk/extensions/VisualEditor/modules/es/views/es.ToolbarView.js
@@ -2,6 +2,9 @@
33 es.ToolbarView = function( $container, surfaceView, config ) {
44 // Inheritance TODO: Do we still need it?
55 es.EventEmitter.call( this );
 6+ if ( !surfaceView ) {
 7+ return;
 8+ }
69
710 // References for use in closures
811 var _this = this,
@@ -15,7 +18,6 @@
1619 this.$.after( this.$spacer );
1720 this.tools = [];
1821
19 -
2022 // Events
2123 /*
2224 * This code is responsible for switching toolbar into floating mode when scrolling (with
@@ -88,4 +90,4 @@
8991 }
9092 };
9193
92 -es.extendClass( es.ToolbarView, es.EventEmitter );
\ No newline at end of file
 94+es.extendClass( es.ToolbarView, es.EventEmitter );
Index: trunk/extensions/VisualEditor/modules/sandbox/sandbox.js
@@ -441,6 +441,7 @@
442442 window.surfaceModel = new es.SurfaceModel( window.doc );
443443 window.surfaceView = new es.SurfaceView( $( '#es-editor' ), window.surfaceModel );
444444 window.toolbarView = new es.ToolbarView( $( '#es-toolbar' ), window.surfaceView );
 445+ window.contextView = new es.ContextView( window.surfaceView );
445446 window.surfaceModel.select( new es.Range( 1, 1 ) );
446447
447448 var $modeButtons = $( '.es-modes-button' ),

Follow-up revisions

RevisionCommit summaryAuthorDate
r105755Followup r105634; use the label "Edit link" for link inspectorerik08:09, 10 December 2011

Comments

#Comment by Eloquence (talk | contribs)   07:24, 10 December 2011

Any objections to changing the UI label to "Edit link"? Inspector is a perfectly cromulent technical term but I'd prefer something more obvious in the UI.

#Comment by Trevor Parscal (WMF) (talk | contribs)   08:01, 10 December 2011

Seems totally reasonable to me- we need to do some hallway testing with the ui we are building here as well, we have various concerns, theories and ideas that we want to validate.

#Comment by P858snake (talk | contribs)   07:55, 10 December 2011
Index: trunk/extensions/VisualEditor/modules/es/images/close.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: trunk/extensions/VisualEditor/modules/es/images/close.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Mime type please.

Status & tagging log