Index: trunk/extensions/VisualEditor/VisualEditor.php |
— | — | @@ -73,6 +73,7 @@ |
74 | 74 | 'es/bases/es.DocumentViewNode.js', |
75 | 75 | 'es/bases/es.DocumentViewBranchNode.js', |
76 | 76 | 'es/bases/es.DocumentViewLeafNode.js', |
| 77 | + 'es/bases/es.Inspector.js', |
77 | 78 | 'es/bases/es.Tool.js', |
78 | 79 | 'es/models/es.DocumentModel.js', |
79 | 80 | 'es/models/es.HeadingModel.js', |
— | — | @@ -89,6 +90,15 @@ |
90 | 91 | 'es/serializers/es.HtmlSerializer.js', |
91 | 92 | 'es/serializers/es.JsonSerializer.js', |
92 | 93 | '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', |
93 | 103 | 'es/views/es.ContextView.js', |
94 | 104 | 'es/views/es.ContentView.js', |
95 | 105 | 'es/views/es.DocumentView.js', |
— | — | @@ -103,14 +113,6 @@ |
104 | 114 | 'es/views/es.TableRowView.js', |
105 | 115 | 'es/views/es.TableView.js', |
106 | 116 | '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' |
115 | 117 | |
116 | 118 | ), |
117 | 119 | 'styles' => array( |
— | — | @@ -118,6 +120,7 @@ |
119 | 121 | 'es/styles/es.ContextView.css', |
120 | 122 | 'es/styles/es.ContentView.css', |
121 | 123 | 'es/styles/es.DocumentView.css', |
| 124 | + 'es/styles/es.Inspector.css', |
122 | 125 | 'es/styles/es.ToolbarView.css', |
123 | 126 | 'es/styles/es.MenuView.css', |
124 | 127 | ), |
Index: trunk/extensions/VisualEditor/demo/index.html |
— | — | @@ -7,6 +7,7 @@ |
8 | 8 | <link rel="stylesheet" href="../modules/es/styles/es.ContextView.css"> |
9 | 9 | <link rel="stylesheet" href="../modules/es/styles/es.ContentView.css"> |
10 | 10 | <link rel="stylesheet" href="../modules/es/styles/es.DocumentView.css"> |
| 11 | + <link rel="stylesheet" href="../modules/es/styles/es.Inspector.css"> |
11 | 12 | <link rel="stylesheet" href="../modules/es/styles/es.ToolbarView.css"> |
12 | 13 | <link rel="stylesheet" href="../modules/es/styles/es.MenuView.css"> |
13 | 14 | <link rel="stylesheet" href="../modules/sandbox/sandbox.css"> |
— | — | @@ -112,6 +113,7 @@ |
113 | 114 | <script src="../modules/es/bases/es.DocumentViewNode.js"></script> |
114 | 115 | <script src="../modules/es/bases/es.DocumentViewBranchNode.js"></script> |
115 | 116 | <script src="../modules/es/bases/es.DocumentViewLeafNode.js"></script> |
| 117 | + <script src="../modules/es/bases/es.Inspector.js"></script> |
116 | 118 | <script src="../modules/es/bases/es.Tool.js"></script> |
117 | 119 | |
118 | 120 | <!-- Models --> |
— | — | @@ -127,6 +129,19 @@ |
128 | 130 | <script src="../modules/es/models/es.HeadingModel.js"></script> |
129 | 131 | <script src="../modules/es/models/es.TransactionModel.js"></script> |
130 | 132 | |
| 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 | + |
131 | 146 | <!-- Views --> |
132 | 147 | <script src="../modules/es/views/es.SurfaceView.js"></script> |
133 | 148 | <script src="../modules/es/views/es.ToolbarView.js"></script> |
— | — | @@ -143,15 +158,6 @@ |
144 | 159 | <script src="../modules/es/views/es.TableCellView.js"></script> |
145 | 160 | <script src="../modules/es/views/es.HeadingView.js"></script> |
146 | 161 | |
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 | | - |
156 | 162 | <!-- Demo --> |
157 | 163 | <script src="../modules/sandbox/sandbox.js"></script> |
158 | 164 | </body> |
Index: trunk/extensions/VisualEditor/modules/es/tools/es.ClearButtonTool.js |
— | — | @@ -19,13 +19,16 @@ |
20 | 20 | /* Methods */ |
21 | 21 | |
22 | 22 | 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(); |
30 | 33 | }; |
31 | 34 | |
32 | 35 | es.ClearButtonTool.prototype.updateState = function( annotations ) { |
Index: trunk/extensions/VisualEditor/modules/es/tools/es.AnnotationButtonTool.js |
— | — | @@ -13,14 +13,23 @@ |
14 | 14 | es.ButtonTool.call( this, toolbar, name ); |
15 | 15 | |
16 | 16 | // Properties |
17 | | - this.annotation = data; |
| 17 | + this.annotation = data.annotation; |
| 18 | + this.inspector = data.inspector; |
18 | 19 | this.active = false; |
19 | 20 | }; |
20 | 21 | |
21 | 22 | /* Methods */ |
22 | 23 | |
23 | 24 | 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 | + } |
25 | 34 | }; |
26 | 35 | |
27 | 36 | es.AnnotationButtonTool.prototype.updateState = function( annotations, nodes ) { |
— | — | @@ -36,21 +45,28 @@ |
37 | 46 | /* Registration */ |
38 | 47 | |
39 | 48 | 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 | + } |
43 | 54 | }; |
44 | 55 | |
45 | 56 | 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 | + } |
49 | 62 | }; |
50 | 63 | |
51 | 64 | 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 | + } |
55 | 71 | }; |
56 | 72 | |
57 | 73 | /* Inheritance */ |
Index: trunk/extensions/VisualEditor/modules/es/tools/es.DropdownTool.js |
— | — | @@ -27,7 +27,7 @@ |
28 | 28 | .add( this.toolbar.surfaceView.$ ) |
29 | 29 | .mousedown( function( e ) { |
30 | 30 | if ( e.which === 1 ) { |
31 | | - _this.menuView.hide(); |
| 31 | + _this.menuView.close(); |
32 | 32 | } |
33 | 33 | } ); |
34 | 34 | this.$.bind( { |
— | — | @@ -41,7 +41,7 @@ |
42 | 42 | // Don't respond to menu clicks |
43 | 43 | var $item = $( e.target ).closest( '.es-menuView' ); |
44 | 44 | if ( e.which === 1 && $item.length === 0 ) { |
45 | | - _this.menuView.toggle(); |
| 45 | + _this.menuView.open(); |
46 | 46 | } |
47 | 47 | } |
48 | 48 | } ); |
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 |
1 | 106 | + 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 @@ |
7 | 7 | * @param {jQuery} $overlay DOM selection to add nodes to |
8 | 8 | */ |
9 | 9 | es.ContextView = function( surfaceView, $overlay ) { |
10 | | - var _this = this; |
| 10 | + // Inheritance |
| 11 | + if ( !surfaceView ) { |
| 12 | + return; |
| 13 | + } |
11 | 14 | |
12 | 15 | // Properties |
13 | 16 | this.surfaceView = surfaceView; |
| 17 | + this.surfaceView.attachContextView( this ); |
| 18 | + this.inspectors = {}; |
| 19 | + this.inspector = null; |
| 20 | + this.position = null; |
14 | 21 | this.$ = $( '<div class="es-contextView"></div>' ).appendTo( $overlay || $( 'body' ) ); |
15 | | - this.$panels = $( '<div class="es-contextView-panels"></div>' ).appendTo( this.$ ); |
16 | 22 | 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.$ ); |
17 | 25 | this.toolbarView = new es.ToolbarView( |
18 | 26 | this.$toolbar, |
19 | 27 | this.surfaceView, |
— | — | @@ -25,22 +33,9 @@ |
26 | 34 | null, |
27 | 35 | this.$ |
28 | 36 | ); |
29 | | - this.$icon = $( '<div class="es-contextView-icon"></div>' ).appendTo( this.$ ); |
30 | 37 | |
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 | | - |
44 | 38 | // Events |
| 39 | + var _this = this; |
45 | 40 | this.$icon.bind( { |
46 | 41 | 'mousedown': function( e ) { |
47 | 42 | if ( e.which === 1 ) { |
— | — | @@ -50,28 +45,65 @@ |
51 | 46 | }, |
52 | 47 | 'mouseup': function( e ) { |
53 | 48 | 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 | + } |
55 | 58 | } |
56 | 59 | } |
57 | 60 | } ); |
| 61 | + |
| 62 | + // Intitialization |
| 63 | + this.addInspector( 'link', new es.LinkInspector( this.toolbarView, this ) ); |
58 | 64 | }; |
59 | 65 | |
60 | 66 | /* Methods */ |
61 | 67 | |
| 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 | + |
62 | 84 | es.ContextView.prototype.set = function() { |
63 | 85 | this.$.removeClass( |
64 | 86 | 'es-contextView-position-below es-contextView-position-above ' + |
65 | 87 | 'es-contextView-position-left es-contextView-position-right ' + |
66 | 88 | 'es-contextView-position-start es-contextView-position-end' |
67 | 89 | ); |
| 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() { |
68 | 100 | var selection = this.surfaceView.getModel().getSelection(), |
69 | | - position, |
70 | 101 | offset; |
| 102 | + this.position = null; |
71 | 103 | if ( selection.from < selection.to ) { |
72 | 104 | var $lastRange = this.surfaceView.$.find( '.es-contentView-range:visible:last' ); |
73 | 105 | if ( $lastRange.length ) { |
74 | 106 | offset = $lastRange.offset(); |
75 | | - position = new es.Position( |
| 107 | + this.position = new es.Position( |
76 | 108 | offset.left + $lastRange.width(), offset.top + $lastRange.height() |
77 | 109 | ); |
78 | 110 | this.$.addClass( 'es-contextView-position-end' ); |
— | — | @@ -80,43 +112,91 @@ |
81 | 113 | var $firstRange = this.surfaceView.$.find( '.es-contentView-range:visible:first' ); |
82 | 114 | if ( $firstRange.length ) { |
83 | 115 | offset = $firstRange.offset(); |
84 | | - position = new es.Position( offset.left, offset.top ); |
| 116 | + this.position = new es.Position( offset.left, offset.top ); |
85 | 117 | this.$.addClass( 'es-contextView-position-start' ); |
86 | 118 | } |
87 | 119 | } |
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 } ); |
115 | 122 | this.$icon.fadeIn( 'fast' ); |
| 123 | + } else { |
| 124 | + this.$icon.hide(); |
116 | 125 | } |
117 | 126 | }; |
118 | 127 | |
| 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 | + |
119 | 155 | es.ContextView.prototype.clear = function() { |
120 | | - this.$panels.hide().children().hide(); |
| 156 | + if ( this.inspector ) { |
| 157 | + this.closeInspector(); |
| 158 | + } |
121 | 159 | this.$icon.hide(); |
122 | | - this.menuView.hide(); |
| 160 | + this.menuView.close(); |
123 | 161 | }; |
| 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 @@ |
103 | 103 | return this.$.css( { 'top': position.top, 'left': position.left } ); |
104 | 104 | }; |
105 | 105 | |
106 | | -es.MenuView.prototype.show = function() { |
| 106 | +es.MenuView.prototype.open = function() { |
107 | 107 | this.$.show(); |
108 | 108 | }; |
109 | 109 | |
110 | | -es.MenuView.prototype.toggle = function() { |
111 | | - this.$.toggle(); |
| 110 | +es.MenuView.prototype.close = function() { |
| 111 | + this.$.hide(); |
112 | 112 | }; |
113 | 113 | |
114 | | -es.MenuView.prototype.hide = function() { |
115 | | - this.$.hide(); |
| 114 | +es.MenuView.prototype.isOpen = function() { |
| 115 | + return this.$.is( ':visible' ); |
116 | 116 | }; |
117 | 117 | |
118 | 118 | es.MenuView.prototype.onSelect = function( item, event ) { |
Index: trunk/extensions/VisualEditor/modules/es/views/es.SurfaceView.js |
— | — | @@ -19,7 +19,7 @@ |
20 | 20 | this.model = model; |
21 | 21 | this.currentSelection = new es.Range(); |
22 | 22 | this.documentView = new es.DocumentView( this.model.getDocument(), this ); |
23 | | - this.contextView = new es.ContextView( this ); |
| 23 | + this.contextView = null; |
24 | 24 | this.$ = $container |
25 | 25 | .addClass( 'es-surfaceView' ) |
26 | 26 | .append( this.documentView.$ ); |
— | — | @@ -146,10 +146,12 @@ |
147 | 147 | } ); |
148 | 148 | $window.scroll( function() { |
149 | 149 | _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 | + } |
154 | 156 | } |
155 | 157 | } ); |
156 | 158 | |
— | — | @@ -163,6 +165,14 @@ |
164 | 166 | |
165 | 167 | /* Methods */ |
166 | 168 | |
| 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 | + |
167 | 177 | es.SurfaceView.prototype.annotate = function( method, annotation ) { |
168 | 178 | if ( method === 'toggle' ) { |
169 | 179 | var annotations = this.getAnnotations(); |
— | — | @@ -275,10 +285,12 @@ |
276 | 286 | _this.showCursor(); |
277 | 287 | _this.documentView.clearSelection( _this.currentSelection ); |
278 | 288 | } |
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 | + } |
283 | 295 | } |
284 | 296 | _this.updateSelectionTimeout = undefined; |
285 | 297 | } |
— | — | @@ -417,9 +429,11 @@ |
418 | 430 | if ( e.which === 1 ) { // left mouse button |
419 | 431 | this.mouse.selectingMode = this.mouse.selectedRange = null; |
420 | 432 | 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 | + } |
424 | 438 | } |
425 | 439 | }; |
426 | 440 | |
Index: trunk/extensions/VisualEditor/modules/es/views/es.ToolbarView.js |
— | — | @@ -2,6 +2,9 @@ |
3 | 3 | es.ToolbarView = function( $container, surfaceView, config ) { |
4 | 4 | // Inheritance TODO: Do we still need it? |
5 | 5 | es.EventEmitter.call( this ); |
| 6 | + if ( !surfaceView ) { |
| 7 | + return; |
| 8 | + } |
6 | 9 | |
7 | 10 | // References for use in closures |
8 | 11 | var _this = this, |
— | — | @@ -15,7 +18,6 @@ |
16 | 19 | this.$.after( this.$spacer ); |
17 | 20 | this.tools = []; |
18 | 21 | |
19 | | - |
20 | 22 | // Events |
21 | 23 | /* |
22 | 24 | * This code is responsible for switching toolbar into floating mode when scrolling (with |
— | — | @@ -88,4 +90,4 @@ |
89 | 91 | } |
90 | 92 | }; |
91 | 93 | |
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 @@ |
442 | 442 | window.surfaceModel = new es.SurfaceModel( window.doc ); |
443 | 443 | window.surfaceView = new es.SurfaceView( $( '#es-editor' ), window.surfaceModel ); |
444 | 444 | window.toolbarView = new es.ToolbarView( $( '#es-toolbar' ), window.surfaceView ); |
| 445 | + window.contextView = new es.ContextView( window.surfaceView ); |
445 | 446 | window.surfaceModel.select( new es.Range( 1, 1 ) ); |
446 | 447 | |
447 | 448 | var $modeButtons = $( '.es-modes-button' ), |