Index: trunk/extensions/VisualEditor/modules/es/tools/es.FormatDropdownTool.js |
— | — | @@ -1,101 +1,123 @@ |
2 | 2 | es.FormatDropdownTool = function( toolbar, name ) {
|
3 | | - es.DropdownTool.call( this, toolbar, name );
|
4 | | -
|
5 | | - this.formats = [
|
| 3 | + // Inheritance
|
| 4 | + es.DropdownTool.call( this, toolbar, name, [
|
6 | 5 | {
|
7 | | - 'name': 'Paragraph',
|
| 6 | + 'name': 'paragraph',
|
| 7 | + 'label': 'Paragraph',
|
8 | 8 | 'type' : 'paragraph'
|
9 | 9 | },
|
10 | 10 | {
|
11 | | - 'name': 'Heading Level 1',
|
| 11 | + 'name': 'heading-1',
|
| 12 | + 'label': 'Heading Level 1',
|
12 | 13 | 'type' : 'heading',
|
13 | 14 | 'attributes': { 'level': 1 }
|
14 | 15 | },
|
15 | 16 | {
|
16 | | - 'name': 'Heading Level 2',
|
| 17 | + 'name': 'heading-2',
|
| 18 | + 'label': 'Heading Level 2',
|
17 | 19 | 'type' : 'heading',
|
18 | 20 | 'attributes': { 'level': 2 }
|
19 | 21 | },
|
20 | 22 | {
|
21 | | - 'name': 'Heading Level 3',
|
| 23 | + 'name': 'heading-3',
|
| 24 | + 'label': 'Heading Level 3',
|
22 | 25 | 'type' : 'heading',
|
23 | 26 | 'attributes': { 'level': 3 }
|
24 | 27 | },
|
25 | 28 | {
|
26 | | - 'name': 'Heading Level 4',
|
| 29 | + 'name': 'heading-4',
|
| 30 | + 'label': 'Heading Level 4',
|
27 | 31 | 'type' : 'heading',
|
28 | 32 | 'attributes': { 'level': 4 }
|
29 | 33 | },
|
30 | 34 | {
|
31 | | - 'name': 'Heading Level 5',
|
| 35 | + 'name': 'heading-5',
|
| 36 | + 'label': 'Heading Level 5',
|
32 | 37 | 'type' : 'heading',
|
33 | 38 | 'attributes': { 'level': 5 }
|
34 | 39 | },
|
35 | 40 | {
|
36 | | - 'name': 'Heading Level 6',
|
| 41 | + 'name': 'heading-6',
|
| 42 | + 'label': 'Heading Level 6',
|
37 | 43 | 'type' : 'heading',
|
38 | 44 | 'attributes': { 'level': 6 }
|
39 | 45 | },
|
40 | 46 | {
|
41 | | - 'name': 'Preformatted',
|
| 47 | + 'name': 'pre',
|
| 48 | + 'label': 'Preformatted',
|
42 | 49 | 'type' : 'pre'
|
43 | 50 | }
|
44 | | - ];
|
45 | | -
|
46 | | - this.$select.append( '<option>' );
|
| 51 | + ] );
|
47 | 52 |
|
48 | | - for ( var i = 0; i < this.formats.length; i++ ) {
|
49 | | - $( '<option>' )
|
50 | | - .val( i )
|
51 | | - .html( this.formats[i].name )
|
52 | | - .appendTo( this.$select );
|
53 | | - }
|
| 53 | + var _this = this;
|
| 54 | + this.$.bind( {
|
| 55 | + 'mousedown': function( e ) {
|
| 56 | + if ( e.button === 0 ) {
|
| 57 | + e.preventDefault();
|
| 58 | + return false;
|
| 59 | + }
|
| 60 | + },
|
| 61 | + 'mouseup': function( e ) {
|
| 62 | + if ( e.button === 0 ) {
|
| 63 | + _this.menuView.setPosition( es.Position.newFromElementPagePosition( _this.$ ) );
|
| 64 | + _this.menuView.toggle();
|
| 65 | + }
|
| 66 | + }
|
| 67 | + } );
|
54 | 68 | };
|
55 | 69 |
|
56 | | -es.FormatDropdownTool.prototype.onChange = function() {
|
57 | | - var index = this.$select.val();
|
58 | | - if ( index in this.formats ) {
|
59 | | - var txs = this.toolbar.surfaceView.model.getDocument().prepareLeafConversion(
|
60 | | - this.toolbar.surfaceView.currentSelection,
|
61 | | - this.formats[index].type,
|
62 | | - this.formats[index].attributes
|
63 | | - )
|
64 | | - for ( var i = 0; i < txs.length; i++ ) {
|
65 | | - this.toolbar.surfaceView.model.transact( txs[i] );
|
66 | | - }
|
| 70 | +/* Methods */
|
| 71 | +
|
| 72 | +es.FormatDropdownTool.prototype.onSelect = function( item ) {
|
| 73 | + var txs = this.toolbar.surfaceView.model.getDocument().prepareLeafConversion(
|
| 74 | + this.toolbar.surfaceView.currentSelection,
|
| 75 | + item.type,
|
| 76 | + item.attributes
|
| 77 | + );
|
| 78 | + for ( var i = 0; i < txs.length; i++ ) {
|
| 79 | + this.toolbar.surfaceView.model.transact( txs[i] );
|
67 | 80 | }
|
68 | 81 | };
|
69 | 82 |
|
70 | 83 | es.FormatDropdownTool.prototype.updateState = function( annotations, nodes ) {
|
71 | | - var format = {
|
72 | | - 'type': nodes[0].getElementType(),
|
73 | | - 'attributes': nodes[0].getElement().attributes
|
74 | | - };
|
75 | | -
|
76 | | - for( var i = 1; i < nodes.length; i++ ) {
|
77 | | - if ( format.type != nodes[i].getElementType()
|
78 | | - || !es.compareObjects( format.attributes, nodes[i].element.attributes ) ) {
|
| 84 | + // Get type and attributes of the first node
|
| 85 | + var i,
|
| 86 | + format = {
|
| 87 | + 'type': nodes[0].getElementType(),
|
| 88 | + 'attributes': nodes[0].getElement().attributes
|
| 89 | + };
|
| 90 | + // Look for mismatches, in which case format should be null
|
| 91 | + for ( i = 1; i < nodes.length; i++ ) {
|
| 92 | + if ( format.type != nodes[i].getElementType() ||
|
| 93 | + !es.compareObjects( format.attributes, nodes[i].element.attributes ) ) {
|
79 | 94 | format = null;
|
80 | 95 | break;
|
81 | 96 | }
|
82 | 97 | }
|
83 | 98 |
|
84 | 99 | if ( format === null ) {
|
85 | | - this.$select.val( null );
|
| 100 | + this.$.text( '' );
|
86 | 101 | } else {
|
87 | | - for ( var i = 0; i < this.formats.length; i++ ) {
|
88 | | - if ( format.type === this.formats[i].type
|
89 | | - && es.compareObjects( format.attributes, this.formats[i].attributes ) ) {
|
90 | | - this.$select.val( i );
|
91 | | - break;
|
| 102 | + var items = this.menuView.getItems();
|
| 103 | + for ( i = 0; i < items.length; i++ ) {
|
| 104 | + if (
|
| 105 | + format.type === items[i].type &&
|
| 106 | + es.compareObjects( format.attributes, items[i].attributes )
|
| 107 | + ) {
|
| 108 | + this.$.text( items[i].label );
|
| 109 | + break;
|
92 | 110 | }
|
93 | 111 | }
|
94 | 112 | }
|
95 | 113 | };
|
96 | 114 |
|
| 115 | +/* Registration */
|
| 116 | +
|
97 | 117 | es.Tool.tools.format = {
|
98 | | - constructor: es.FormatDropdownTool,
|
99 | | - name: 'format'
|
| 118 | + 'constructor': es.FormatDropdownTool,
|
| 119 | + 'name': 'format'
|
100 | 120 | };
|
101 | 121 |
|
102 | | -es.extendClass( es.FormatDropdownTool, es.DropdownTool ); |
\ No newline at end of file |
| 122 | +/* Inheritance */
|
| 123 | +
|
| 124 | +es.extendClass( es.FormatDropdownTool, es.DropdownTool );
|
Index: trunk/extensions/VisualEditor/modules/es/tools/es.DropdownTool.js |
— | — | @@ -1,28 +1,35 @@ |
2 | | -es.DropdownTool = function( toolbar, name ) {
|
| 2 | +es.DropdownTool = function( toolbar, name, items ) {
|
| 3 | + // Inheritance
|
3 | 4 | es.Tool.call( this, toolbar, name );
|
4 | 5 |
|
5 | | - // for es.extendClass
|
| 6 | + // Early exit when extending via es.extendClass
|
6 | 7 | if ( !name ) {
|
7 | 8 | return;
|
8 | 9 | }
|
9 | | -
|
10 | | - this.$.addClass( 'es-toolbarDropdownTool' ).addClass( 'es-toolbarDropdownTool-' + name );
|
11 | 10 |
|
12 | | - this.$select = $( '<select>' );
|
13 | | - this.$.append( this.$select );
|
14 | | -
|
| 11 | + // Properties
|
15 | 12 | var _this = this;
|
| 13 | + this.menuView = new es.MenuView( items, function( item ) {
|
| 14 | + _this.onSelect( item );
|
| 15 | + _this.$.text( item.label );
|
| 16 | + } );
|
16 | 17 |
|
17 | | - this.$.bind( {
|
18 | | - 'change': function( e ) {
|
19 | | - _this.onChange( e );
|
| 18 | + $( document ).add( this.toolbar.surfaceView.$ ).mousedown( function( e ) {
|
| 19 | + if ( e.button === 0 ) {
|
| 20 | + _this.menuView.hide();
|
20 | 21 | }
|
21 | 22 | } );
|
22 | 23 |
|
| 24 | + // DOM Changes
|
| 25 | + this.$.addClass( 'es-toolbarDropdownTool' ).addClass( 'es-toolbarDropdownTool-' + name );
|
23 | 26 | };
|
24 | 27 |
|
25 | | -es.DropdownTool.prototype.onChange = function() {
|
26 | | - throw 'DropdownTool.onChange not implemented in this subclass:' + this.constructor;
|
| 28 | +/* Methods */
|
| 29 | +
|
| 30 | +es.DropdownTool.prototype.onSelect = function( item ) {
|
| 31 | + throw 'DropdownTool.onSelect not implemented in this subclass:' + this.constructor;
|
27 | 32 | };
|
28 | 33 |
|
29 | | -es.extendClass( es.DropdownTool, es.Tool ); |
\ No newline at end of file |
| 34 | +/* Inheritance */
|
| 35 | +
|
| 36 | +es.extendClass( es.DropdownTool, es.Tool );
|
Index: trunk/extensions/VisualEditor/modules/es/styles/es.ToolbarView.css |
— | — | @@ -88,6 +88,19 @@ |
89 | 89 | background-image: url(../images/clear.png); |
90 | 90 | } |
91 | 91 | .es-toolbarDropdownTool { |
| 92 | + border-color: #dddddd; |
| 93 | + -webkit-border-radius: 0.25em; |
| 94 | + -moz-border-radius: 0.25em; |
| 95 | + -o-border-radius: 0.25em; |
| 96 | + background-color: white; |
| 97 | + font-size: 0.8em; |
| 98 | + line-height: 24px; |
| 99 | + padding-left: 0.75em; |
| 100 | + padding-right: 24px; |
| 101 | + background-image: url(../images/arrow-down.png); |
| 102 | + background-position: 9em center; |
| 103 | + background-repeat: no-repeat; |
92 | 104 | } |
93 | | -.es-toolbarDropdownTool-formatting { |
94 | | -} |
\ No newline at end of file |
| 105 | +.es-toolbarDropdownTool-format { |
| 106 | + width: 8em; |
| 107 | +} |
Index: trunk/extensions/VisualEditor/modules/es/styles/es.ContextView.css |
— | — | @@ -19,6 +19,7 @@ |
20 | 20 | -webkit-box-shadow: 0 0.25em 1em 0 rgba(0,0,0,0.25); |
21 | 21 | -moz-box-shadow: 0 0.25em 1em 0 rgba(0,0,0,0.25); |
22 | 22 | box-shadow: 0 0.25em 1em 0 rgba(0,0,0,0.25); |
| 23 | + z-index: 4; |
23 | 24 | } |
24 | 25 | |
25 | 26 | .es-contextView-position-start .es-contextView-icon { |
Index: trunk/extensions/VisualEditor/modules/es/styles/es.MenuView.css |
— | — | @@ -11,6 +11,7 @@ |
12 | 12 | -moz-box-shadow: 0 0.25em 1em 0 rgba(0,0,0,0.25); |
13 | 13 | box-shadow: 0 0.25em 1em 0 rgba(0,0,0,0.25); |
14 | 14 | padding: 0.33em 0; |
| 15 | + z-index: 3; |
15 | 16 | } |
16 | 17 | |
17 | 18 | .es-menuView-item { |
Index: trunk/extensions/VisualEditor/modules/es/views/es.ContextView.js |
— | — | @@ -18,28 +18,30 @@ |
19 | 19 | this.surfaceView, |
20 | 20 | [{ 'name': 'textStyle', 'items' : [ 'bold', 'italic', 'formatting', 'clear' ] }] |
21 | 21 | ); |
22 | | - this.menu = new es.MenuView( [ |
23 | | - // Example menu items |
24 | | - { 'name': 'tools', '$': this.$toolbar }, |
25 | | - '-', |
26 | | - { 'name': 'link', 'label': 'Link to...', 'callback': function() { |
27 | | - _this.menu.hide(); |
28 | | - _this.$panels |
29 | | - .show() |
30 | | - .find( '[rel="link"]' ) |
| 22 | + this.menuView = new es.MenuView( [ |
| 23 | + // Example menu items |
| 24 | + { 'name': 'tools', '$': this.$toolbar }, |
| 25 | + '-', |
| 26 | + { 'name': 'link', 'label': 'Link to...', 'callback': function( item ) { |
| 27 | + _this.menuView.hide(); |
| 28 | + _this.$panels |
31 | 29 | .show() |
32 | | - .end() |
33 | | - .find( '[rel="link"] input:first' ) |
34 | | - .focus(); |
35 | | - } }, |
36 | | - '-', |
37 | | - { 'name': 'copy', 'label': 'Copy' }, |
38 | | - { 'name': 'cut', 'label': 'Cut' }, |
39 | | - { 'name': 'paste', 'label': 'Paste' } |
40 | | - ] ); |
41 | | - this.$.append( this.menu.$ ); |
| 30 | + .find( '[rel="link"]' ) |
| 31 | + .show() |
| 32 | + .end() |
| 33 | + .find( '[rel="link"] input:first' ) |
| 34 | + .focus(); |
| 35 | + } }, |
| 36 | + '-', |
| 37 | + { 'name': 'copy', 'label': 'Copy' }, |
| 38 | + { 'name': 'cut', 'label': 'Cut' }, |
| 39 | + { 'name': 'paste', 'label': 'Paste' } |
| 40 | + ], |
| 41 | + null, |
| 42 | + this.$ |
| 43 | + ); |
42 | 44 | this.$icon = $( '<div class="es-contextView-icon"></div>' ).appendTo( this.$ ); |
43 | | - |
| 45 | + |
44 | 46 | // Example panel |
45 | 47 | this.$panels.append( |
46 | 48 | '<div class="es-contextView-panel" rel="link">' + |
— | — | @@ -52,8 +54,18 @@ |
53 | 55 | } ); |
54 | 56 | |
55 | 57 | // Events |
56 | | - this.$icon.click( function() { |
57 | | - _this.menu.toggle(); |
| 58 | + this.$icon.bind( { |
| 59 | + 'mousedown': function( e ) { |
| 60 | + if ( e.button === 0 ) { |
| 61 | + e.preventDefault(); |
| 62 | + return false; |
| 63 | + } |
| 64 | + }, |
| 65 | + 'mouseup': function( e ) { |
| 66 | + if ( e.button === 0 ) { |
| 67 | + _this.menuView.toggle(); |
| 68 | + } |
| 69 | + } |
58 | 70 | } ); |
59 | 71 | }; |
60 | 72 | |
— | — | @@ -86,12 +98,13 @@ |
87 | 99 | } |
88 | 100 | } |
89 | 101 | if ( position ) { |
90 | | - if ( position.left + this.menu.$.width() < $( 'body' ).width() ) { |
| 102 | + if ( position.left + this.menuView.$.width() < $( 'body' ).width() ) { |
91 | 103 | this.$.addClass( 'es-contextView-position-left' ); |
92 | 104 | } else { |
93 | 105 | this.$.addClass( 'es-contextView-position-right' ); |
94 | 106 | } |
95 | | - if ( position.top + this.menu.$.height() < $( window ).height() + $( window ).scrollTop() ) { |
| 107 | + var $window = $( window ); |
| 108 | + if ( position.top + this.menuView.$.height() < $window.height() + $window.scrollTop() ) { |
96 | 109 | this.$.addClass( 'es-contextView-position-below' ); |
97 | 110 | } else { |
98 | 111 | this.$.addClass( 'es-contextView-position-above' ); |
— | — | @@ -104,5 +117,5 @@ |
105 | 118 | es.ContextView.prototype.clear = function() { |
106 | 119 | this.$panels.hide().children().hide(); |
107 | 120 | this.$icon.hide(); |
108 | | - this.menu.hide(); |
| 121 | + this.menuView.hide(); |
109 | 122 | }; |
Index: trunk/extensions/VisualEditor/modules/es/views/es.MenuView.js |
— | — | @@ -3,13 +3,16 @@ |
4 | 4 | * |
5 | 5 | * @class |
6 | 6 | * @constructor |
7 | | - * @param {jQuery} $overlay DOM selection to add nodes to |
| 7 | + * @param {Object[]} items List of items to append initially |
| 8 | + * @param {Function} callback Function to call if an item doesn't have it's own callback |
| 9 | + * @param {jQuery} [$overlay=$( 'body' )] DOM selection to add nodes to |
8 | 10 | */ |
9 | | -es.MenuView = function( items, $overlay ) { |
| 11 | +es.MenuView = function( items, callback, $overlay ) { |
10 | 12 | // Properties |
11 | 13 | this.$ = $( '<div class="es-menuView"></div>' ).appendTo( $overlay || $( 'body' ) ); |
12 | 14 | this.items = []; |
13 | 15 | this.autoNamedBreaks = 0; |
| 16 | + this.callback = callback; |
14 | 17 | |
15 | 18 | // Items |
16 | 19 | if ( es.isArray( items ) ) { |
— | — | @@ -91,6 +94,14 @@ |
92 | 95 | } |
93 | 96 | }; |
94 | 97 | |
| 98 | +es.MenuView.prototype.getItems = function() { |
| 99 | + return this.items; |
| 100 | +}; |
| 101 | + |
| 102 | +es.MenuView.prototype.setPosition = function( position ) { |
| 103 | + return this.$.css( { 'top': position.top, 'left': position.left } ); |
| 104 | +}; |
| 105 | + |
95 | 106 | es.MenuView.prototype.show = function() { |
96 | 107 | this.$.show(); |
97 | 108 | }; |
— | — | @@ -105,7 +116,9 @@ |
106 | 117 | |
107 | 118 | es.MenuView.prototype.onSelect = function( item, event ) { |
108 | 119 | if ( typeof item.callback === 'function' ) { |
109 | | - item.callback(); |
| 120 | + item.callback( item ); |
| 121 | + } else if ( typeof this.callback === 'function' ) { |
| 122 | + this.callback( item ); |
110 | 123 | } |
111 | 124 | this.hide(); |
112 | 125 | }; |