mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-24 02:38:40 +00:00
Build out ve.Surface constructor to support multiple editor instances
Now setting up multiple toolbars per config Tools & Modes are now configurable per toolbar per instance Base elements are created on demand and no longer id specific Note: There are some bugs with multiple instances. Change-Id: Id0bbbca2d1b76fd2db3f3b0f9abd90194930b610
This commit is contained in:
parent
56d6757f67
commit
94479bd79d
|
@ -24,7 +24,7 @@
|
|||
overflow-y: scroll;
|
||||
background-color: white;
|
||||
}
|
||||
#es-base {
|
||||
.es-base {
|
||||
margin: 2em;
|
||||
margin-top: 0em;
|
||||
-webkit-box-shadow: 0 0.25em 1.5em 0 #dddddd;
|
||||
|
@ -35,15 +35,15 @@
|
|||
-o-border-radius: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
#es-panes {
|
||||
.es-panes {
|
||||
border: solid 1px #cccccc;
|
||||
border-top: none;
|
||||
}
|
||||
#es-editor, .es-showData #es-editor {
|
||||
.es-editor, .es-showData .es-editor {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
#es-toolbar {
|
||||
.es-toolbar {
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
-o-border-radius: 0;
|
||||
|
@ -57,12 +57,12 @@
|
|||
-o-border-top-left-radius: 0.25em;
|
||||
border-top-left-radius: 0.25em;
|
||||
}
|
||||
#es-toolbar.float {
|
||||
.es-toolbar.float {
|
||||
left: 2em;
|
||||
right: 2em;
|
||||
top: 0;
|
||||
}
|
||||
#es-docs {
|
||||
.es-docs {
|
||||
margin-left: 2.5em;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -6,129 +6,4 @@
|
|||
<div id="es-docs-label">Example documents:</div>
|
||||
<ul id="es-docs-list" ></ul>
|
||||
</div>
|
||||
<div id="es-base" style="visibility: hidden">
|
||||
<div id="es-toolbar-wrapper">
|
||||
<div id="es-toolbar" class="es-toolbar">
|
||||
<div id="es-modes" class="es-modes">
|
||||
<div id="es-mode-wikitext" class="es-modes-button" title="<?php echo $modeWikitext ?>"></div>
|
||||
<div id="es-mode-json" class="es-modes-button" title="<?php echo $modeJson ?>"></div>
|
||||
<div id="es-mode-html" class="es-modes-button" title="<?php echo $modeHtml?>"></div>
|
||||
<div id="es-mode-render" class="es-modes-button" title="<?php echo $modeRender ?>"></div>
|
||||
<div id="es-mode-history" class="es-modes-button" title="<?php echo $modeHistory ?>"></div>
|
||||
<div id="es-mode-help" class="es-modes-button" title="<?php echo $modeHelp ?>"></div>
|
||||
</div>
|
||||
<div style="clear:both"></div>
|
||||
<div id="es-toolbar-shadow"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="es-panes">
|
||||
<div id="es-visual">
|
||||
<div id="es-editor"></div>
|
||||
</div>
|
||||
<div id="es-panels">
|
||||
<div id="es-panel-wikitext" class="es-panel es-code"></div>
|
||||
<div id="es-panel-json" class="es-panel es-code"></div>
|
||||
<div id="es-panel-html" class="es-panel es-code"></div>
|
||||
<div id="es-panel-render" class="es-panel es-render"></div>
|
||||
<div id="es-panel-history" class="es-panel es-code"></div>
|
||||
<div id="es-panel-help" class="es-panel">
|
||||
<div class="es-help-title">Keyboard Shortcuts</div>
|
||||
<div class="es-help-shortcuts-title">Clipboard</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +
|
||||
<span class="es-help-key">C</span>
|
||||
</span>
|
||||
Copy selected text
|
||||
</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +
|
||||
<span class="es-help-key">X</span>
|
||||
</span>
|
||||
Cut selected text
|
||||
</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +
|
||||
<span class="es-help-key">V</span>
|
||||
</span>
|
||||
Paste text at the cursor
|
||||
</div>
|
||||
<div class="es-help-shortcuts-title">History navigation</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +
|
||||
<span class="es-help-key">Z</span>
|
||||
</span>
|
||||
Undo
|
||||
</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +
|
||||
<span class="es-help-key">Y</span>
|
||||
</span>
|
||||
Redo
|
||||
</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +
|
||||
<span class="es-help-key">⇧</span> +
|
||||
<span class="es-help-key">Z</span>
|
||||
</span>
|
||||
Redo
|
||||
</div>
|
||||
<div class="es-help-shortcuts-title">Formatting</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +
|
||||
<span class="es-help-key">B</span>
|
||||
</span>
|
||||
Make selected text bold
|
||||
</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +
|
||||
<span class="es-help-key">I</span>
|
||||
</span>
|
||||
Make selected text italic
|
||||
</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +
|
||||
<span class="es-help-key">K</span>
|
||||
</span>
|
||||
Make selected text a link
|
||||
</div>
|
||||
<div class="es-help-shortcuts-title">Selection</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">⇧</span> +
|
||||
<span class="es-help-key">Arrow</span>
|
||||
</span>
|
||||
Adjust selection
|
||||
</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌥</span> +
|
||||
<span class="es-help-key">Arrow</span>
|
||||
</span>
|
||||
Move cursor by words or blocks
|
||||
</div>
|
||||
<div class="es-help-shortcut">
|
||||
<span class="es-help-keys">
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌥</span> +
|
||||
<span class="es-help-key">⇧</span> +
|
||||
<span class="es-help-key">Arrow</span>
|
||||
</span>
|
||||
Adjust selection by words or blocks
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear:both"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="paste" contenteditable="true" style="height: 1px; width: 1px; display: none; opacity: 0; position: absolute;"></div>
|
||||
|
||||
<!-- /VisualEditor Sandbox -->
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#es-toolbar {
|
||||
.es-toolbar {
|
||||
border: solid 1px #cccccc;
|
||||
position: relative;
|
||||
-webkit-border-radius: 0.25em;
|
||||
|
@ -14,7 +14,7 @@
|
|||
-o-user-select;
|
||||
user-select: none;
|
||||
}
|
||||
#es-toolbar-wrapper.float #es-toolbar {
|
||||
.es-toolbar-wrapper.float .es-toolbar {
|
||||
top: 0;
|
||||
position: fixed;
|
||||
-webkit-border-radius: 0;
|
||||
|
@ -24,7 +24,7 @@
|
|||
z-index: 100;
|
||||
border-top: none;
|
||||
}
|
||||
#es-toolbar-shadow {
|
||||
.es-toolbar-shadow {
|
||||
background-image: url(../ve/ui/styles/images/toolbar-shadow.png);
|
||||
background-position: top left;
|
||||
background-repeat: repeat-x;
|
||||
|
@ -39,15 +39,15 @@
|
|||
transition: opacity 500ms ease-in-out;
|
||||
opacity: 0.125;
|
||||
}
|
||||
#es-toolbar.float #es-toolbar-shadow {
|
||||
.es-toolbar.float .es-toolbar-shadow {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.es-showData #es-editor {
|
||||
.es-showData .es-editor {
|
||||
border-right: solid 1px #cccccc;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.es-showData #es-visual {
|
||||
.es-showData .es-visual {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
float: left;
|
||||
|
@ -55,10 +55,10 @@
|
|||
overflow: hidden;
|
||||
margin-left: -1px;
|
||||
}
|
||||
#es-panels {
|
||||
.es-panels {
|
||||
display: none;
|
||||
}
|
||||
.es-showData #es-panels {
|
||||
.es-showData .es-panels {
|
||||
display: block;
|
||||
float: right;
|
||||
width: 50%;
|
||||
|
@ -108,7 +108,7 @@
|
|||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
#es-toolbar .es-toolbarGroups {
|
||||
.es-toolbar .es-toolbarGroups {
|
||||
float: left;
|
||||
}
|
||||
.es-modes {
|
||||
|
@ -149,38 +149,38 @@
|
|||
-moz-box-shadow: inset 0px 1px 4px 0px rgba(0, 0, 0, 0.07);
|
||||
box-shadow: inset 0px 1px 4px 0px rgba(0, 0, 0, 0.07);
|
||||
}
|
||||
#es-mode-wikitext:before {
|
||||
.es-mode-wikitext:before {
|
||||
background-image: url(../ve/ui/styles/images/wikitext.png);
|
||||
}
|
||||
|
||||
#es-mode-json:before {
|
||||
.es-mode-json:before {
|
||||
background-image: url(../ve/ui/styles/images/json.png);
|
||||
}
|
||||
|
||||
#es-mode-html:before {
|
||||
.es-mode-html:before {
|
||||
background-image: url(../ve/ui/styles/images/html.png);
|
||||
}
|
||||
|
||||
#es-mode-render:before {
|
||||
.es-mode-render:before {
|
||||
background-image: url(../ve/ui/styles/images/render.png);
|
||||
}
|
||||
|
||||
#es-mode-history:before {
|
||||
.es-mode-history:before {
|
||||
background-image: url(../ve/ui/styles/images/history.png);
|
||||
}
|
||||
|
||||
#es-mode-help:before {
|
||||
.es-mode-help:before {
|
||||
background-image: url(../ve/ui/styles/images/help.png);
|
||||
}
|
||||
|
||||
#es-panel-history div {
|
||||
.es-panel-history div {
|
||||
border-bottom: solid 1px #dddddd;
|
||||
padding: 0.5em 0;
|
||||
color: #666666;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
#es-panel-history div.es-panel-history-active {
|
||||
.es-panel-history div.es-panel-history-active {
|
||||
color: #000000;
|
||||
background-color: white;
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ $(document).ready( function() {
|
|||
'end': 17
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -169,7 +169,7 @@ $(document).ready( function() {
|
|||
'end': 17
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -189,7 +189,7 @@ $(document).ready( function() {
|
|||
'end': 17
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -209,7 +209,7 @@ $(document).ready( function() {
|
|||
'end': 17
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -229,7 +229,7 @@ $(document).ready( function() {
|
|||
'end': 17
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -249,7 +249,7 @@ $(document).ready( function() {
|
|||
'end': 17
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -446,7 +446,7 @@ $(document).ready( function() {
|
|||
'type': 'paragraph',
|
||||
'content': { 'text': 'Test 4444' }
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
'type': 'listItem',
|
||||
|
@ -458,7 +458,7 @@ $(document).ready( function() {
|
|||
'type': 'paragraph',
|
||||
'content': { 'text': 'Test 55555' }
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
'type': 'listItem',
|
||||
|
@ -470,7 +470,7 @@ $(document).ready( function() {
|
|||
'type': 'paragraph',
|
||||
'content': { 'text': 'Test 666666' }
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -502,150 +502,34 @@ $(document).ready( function() {
|
|||
]
|
||||
}
|
||||
};
|
||||
|
||||
/* Create a new instance of VE, optional third param for ve configuration options */
|
||||
var sandboxEditor = new ve.Surface('sandbox', wikidoms['Wikipedia article'] /*, options */),
|
||||
surfaceModel = sandboxEditor.getSurfaceModel(),
|
||||
documentModel = sandboxEditor.getDocumentModel(),
|
||||
editorID = sandboxEditor.getID();
|
||||
|
||||
/*
|
||||
* This code is responsible for switching toolbar into floating mode when scrolling (with
|
||||
* keyboard or mouse).
|
||||
*/
|
||||
var $toolbarWrapper = $( '#es-toolbar-wrapper' ),
|
||||
$toolbar = $( '#es-toolbar' ),
|
||||
$window = $( window );
|
||||
$window.scroll( function() {
|
||||
var toolbarWrapperOffset = $toolbarWrapper.offset();
|
||||
if ( $window.scrollTop() > toolbarWrapperOffset.top ) {
|
||||
if ( !$toolbarWrapper.hasClass( 'float' ) ) {
|
||||
var left = toolbarWrapperOffset.left,
|
||||
right = $window.width() - $toolbarWrapper.outerWidth() - left;
|
||||
$toolbarWrapper.css( 'height', $toolbarWrapper.height() ).addClass( 'float' );
|
||||
$toolbar.css( { 'left': left, 'right': right } );
|
||||
}
|
||||
} else {
|
||||
if ( $toolbarWrapper.hasClass( 'float' ) ) {
|
||||
$toolbarWrapper.css( 'height', 'auto' ).removeClass( 'float' );
|
||||
$toolbar.css( { 'left': 0, 'right': 0 } );
|
||||
/* Sandbox integration hack. Allows both MW integration and demo pages to work */
|
||||
if ( $('#content').length === 0 ) {
|
||||
$( 'body' ).append(
|
||||
$( '<div />' ).attr( 'id', 'content' )
|
||||
);
|
||||
}
|
||||
|
||||
/* Sandbox config object. */
|
||||
var options = {
|
||||
toolbars: {
|
||||
top: {
|
||||
/* What modes this toolbar will have */
|
||||
modes: ['wikitext', 'json', 'html', 'render', 'history', 'help']
|
||||
}
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
var $modeButtons = $( '.es-modes-button' ),
|
||||
$panels = $( '.es-panel' ),
|
||||
$base = $( '#es-base' ),
|
||||
currentMode = null,
|
||||
modes = {
|
||||
'wikitext': {
|
||||
'$': $( '#es-mode-wikitext' ),
|
||||
'$panel': $( '#es-panel-wikitext' ),
|
||||
'update': function() {
|
||||
this.$panel.text(
|
||||
ve.dm.WikitextSerializer.stringify( documentModel.getPlainObject() )
|
||||
);
|
||||
}
|
||||
},
|
||||
'json': {
|
||||
'$': $( '#es-mode-json' ),
|
||||
'$panel': $( '#es-panel-json' ),
|
||||
'update': function() {
|
||||
this.$panel.text( ve.dm.JsonSerializer.stringify( documentModel.getPlainObject(), {
|
||||
'indentWith': ' '
|
||||
} ) );
|
||||
}
|
||||
},
|
||||
'html': {
|
||||
'$': $( '#es-mode-html' ),
|
||||
'$panel': $( '#es-panel-html' ),
|
||||
'update': function() {
|
||||
this.$panel.text(
|
||||
ve.dm.HtmlSerializer.stringify( documentModel.getPlainObject() )
|
||||
);
|
||||
}
|
||||
},
|
||||
'render': {
|
||||
'$': $( '#es-mode-render' ),
|
||||
'$panel': $( '#es-panel-render' ),
|
||||
'update': function() {
|
||||
this.$panel.html(
|
||||
ve.dm.HtmlSerializer.stringify( documentModel.getPlainObject() )
|
||||
);
|
||||
}
|
||||
},
|
||||
'history': {
|
||||
'$': $( '#es-mode-history' ),
|
||||
'$panel': $( '#es-panel-history' ),
|
||||
'update': function() {
|
||||
var history = surfaceModel.getHistory(),
|
||||
i = history.length,
|
||||
end = Math.max( 0, i - 25 ),
|
||||
j,
|
||||
k,
|
||||
ops,
|
||||
events = '',
|
||||
z = 0,
|
||||
operations,
|
||||
data;
|
||||
|
||||
while ( --i >= end ) {
|
||||
z++;
|
||||
operations = [];
|
||||
for ( j = 0; j < history[i].stack.length; j++) {
|
||||
ops = history[i].stack[j].getOperations().slice(0);
|
||||
for ( k = 0; k < ops.length; k++ ) {
|
||||
data = ops[k].data || ops[k].length;
|
||||
if ( ve.isArray( data ) ) {
|
||||
data = data[0];
|
||||
if ( ve.isArray( data ) ) {
|
||||
data = data[0];
|
||||
}
|
||||
}
|
||||
if ( typeof data !== 'string' && typeof data !== 'number' ) {
|
||||
data = '-';
|
||||
}
|
||||
ops[k] = ops[k].type.substr( 0, 3 ) + '(' + data + ')';
|
||||
}
|
||||
operations.push('[' + ops.join( ', ' ) + ']');
|
||||
}
|
||||
events += '<div' + (z === surfaceModel.undoIndex ? ' class="es-panel-history-active"' : '') + '>' + operations.join(', ') + '</div>';
|
||||
}
|
||||
|
||||
this.$panel.html( events );
|
||||
}
|
||||
},
|
||||
'help': {
|
||||
'$': $( '#es-mode-help' ),
|
||||
'$panel': $( '#es-panel-help' ),
|
||||
'update': function() {}
|
||||
}
|
||||
};
|
||||
$.each( modes, function( name, mode ) {
|
||||
mode.$.click( function() {
|
||||
var disable = $(this).hasClass( 'es-modes-button-down' );
|
||||
var visible = $base.hasClass( 'es-showData' );
|
||||
$modeButtons.removeClass( 'es-modes-button-down' );
|
||||
$panels.hide();
|
||||
if ( disable ) {
|
||||
if ( visible ) {
|
||||
$base.removeClass( 'es-showData' );
|
||||
$window.resize();
|
||||
}
|
||||
currentMode = null;
|
||||
} else {
|
||||
$(this).addClass( 'es-modes-button-down' );
|
||||
mode.$panel.show();
|
||||
if ( !visible ) {
|
||||
$base.addClass( 'es-showData' );
|
||||
$window.resize();
|
||||
}
|
||||
mode.update.call( mode );
|
||||
currentMode = mode;
|
||||
}
|
||||
} );
|
||||
} );
|
||||
/*
|
||||
Create Sandbox instance of VE
|
||||
Attach to #content element
|
||||
*/
|
||||
var sandboxEditor = new ve.Surface( '#content', wikidoms['Wikipedia article'], options ),
|
||||
surfaceModel = sandboxEditor.getSurfaceModel(),
|
||||
documentModel = sandboxEditor.getDocumentModel(),
|
||||
parent = sandboxEditor.getParent();
|
||||
|
||||
/* Sandbox links above the editor */
|
||||
var $docsList = $( '#es-docs-list' );
|
||||
$.each( wikidoms, function( title, wikidom ) {
|
||||
$docsList.append(
|
||||
|
@ -667,8 +551,8 @@ $(document).ready( function() {
|
|||
);
|
||||
surfaceModel.purgeHistory();
|
||||
|
||||
if ( currentMode ) {
|
||||
currentMode.update.call( currentMode );
|
||||
if ( sandboxEditor.currentMode ) {
|
||||
sandboxEditor.currentMode.update.call( sandboxEditor.currentMode );
|
||||
}
|
||||
return false;
|
||||
} )
|
||||
|
@ -676,19 +560,8 @@ $(document).ready( function() {
|
|||
);
|
||||
} );
|
||||
|
||||
surfaceModel.on( 'transact', function() {
|
||||
if ( currentMode ) {
|
||||
currentMode.update.call( currentMode );
|
||||
}
|
||||
} );
|
||||
surfaceModel.on( 'select', function() {
|
||||
if ( currentMode === modes.history ) {
|
||||
currentMode.update.call( currentMode );
|
||||
}
|
||||
} );
|
||||
|
||||
/* Sandbox Warning Message */
|
||||
$( '#es-docs' ).css( { 'visibility': 'visible' } );
|
||||
$( '#es-base' ).css( { 'visibility': 'visible' } );
|
||||
// Show the warning that this software is experimental
|
||||
// TODO: Use a cookie to remember the warning has been dismissed
|
||||
$( '#es-warning' ).show();
|
||||
|
@ -696,5 +569,5 @@ $(document).ready( function() {
|
|||
$(this).parent().slideUp();
|
||||
return false;
|
||||
} );
|
||||
$( '#es-mode-wikitext' ).click();
|
||||
$( '.es-mode-wikitext' ).click();
|
||||
} );
|
||||
|
|
|
@ -5,36 +5,450 @@
|
|||
*
|
||||
* @class
|
||||
* @constructor
|
||||
* @param {String} id Unique name of editor instance
|
||||
* @param {String} parent Selector of element to attach to
|
||||
* @param {Array} data Document data
|
||||
* @param {Object} options Configuration options
|
||||
*/
|
||||
ve.Surface = function( id, data, options ) {
|
||||
ve.Surface = function( parent, data, options ) {
|
||||
// Properties
|
||||
this.id = id;
|
||||
this.options = ve.extendObject( {
|
||||
this.parent = parent;
|
||||
this.modes = {};
|
||||
this.currentMode = null;
|
||||
/* Extend VE configuration recursively */
|
||||
this.options = ve.extendObject( true, {
|
||||
// Default options
|
||||
toolbars: {
|
||||
'top': [{ 'name': 'history', 'items' : ['undo', 'redo'] },
|
||||
{ 'name': 'textStyle', 'items' : ['format'] },
|
||||
{ 'name': 'textStyle', 'items' : ['bold', 'italic', 'link', 'clear'] },
|
||||
{ 'name': 'list', 'items' : ['number', 'bullet', 'outdent', 'indent'] }]
|
||||
top: {
|
||||
tools: [{ 'name': 'history', 'items' : ['undo', 'redo'] },
|
||||
{ 'name': 'textStyle', 'items' : ['format'] },
|
||||
{ 'name': 'textStyle', 'items' : ['bold', 'italic', 'link', 'clear'] },
|
||||
{ 'name': 'list', 'items' : ['number', 'bullet', 'outdent', 'indent'] }]
|
||||
}
|
||||
},
|
||||
//TODO: i18n
|
||||
modes: {
|
||||
wikitext: 'Toggle wikitext view',
|
||||
json: 'Toggle JSON view',
|
||||
html: 'Toggle HTML view',
|
||||
render: 'Toggle preview',
|
||||
history: 'Toggle transaction history view',
|
||||
help: 'Toggle help view'
|
||||
}
|
||||
|
||||
}, options );
|
||||
|
||||
|
||||
// A place to store element references
|
||||
this.$base = null;
|
||||
this.$surface = null;
|
||||
this.toolbarWrapper = {};
|
||||
|
||||
/* Register Document and Surface Models */
|
||||
this.documentModel = ve.dm.DocumentNode.newFromPlainObject( data );
|
||||
this.surfaceModel = new ve.dm.Surface( this.documentModel );
|
||||
|
||||
//TODO: Find source of breakage when view element is not #es-editor
|
||||
this.view = new ve.ce.Surface( $( '#es-editor' ), this.surfaceModel );
|
||||
this.context = new ve.ui.Context( this.view );
|
||||
|
||||
//TODO: Configure toolbar based on this.options.
|
||||
this.toolbar = new ve.ui.Toolbar( $( '#es-toolbar' ), this.view, this.options.toolbars.top );
|
||||
// Setup VE DOM Skeleton
|
||||
this.setupBaseElements();
|
||||
|
||||
// Setup Surface View
|
||||
this.setupSurfaceView();
|
||||
|
||||
// Setup toolbars based on this.options
|
||||
this.setupToolbars();
|
||||
|
||||
// Setup various toolbar modes and panels
|
||||
this.setupModes();
|
||||
|
||||
// Registration
|
||||
ve.instances.push( this );
|
||||
console.log (this);
|
||||
};
|
||||
|
||||
/* Setup Methods */
|
||||
|
||||
ve.Surface.prototype.setupBaseElements = function() {
|
||||
// Make new base element
|
||||
this.$base = $('<div />')
|
||||
.attr( 'class', 'es-base' )
|
||||
.append(
|
||||
$('<div />').attr('class', 'es-panes')
|
||||
.append(
|
||||
$('<div />').attr('class', 'es-visual')
|
||||
).append(
|
||||
$('<div />').attr('class', 'es-panels')
|
||||
).append(
|
||||
$('<div />').attr('style', 'clear:both')
|
||||
)
|
||||
).append(
|
||||
$('<div />').attr( {
|
||||
'id': 'paste', //TODO: make 'paste' in surface stateful and remove this attrib
|
||||
'class': 'paste',
|
||||
'contenteditable': 'true',
|
||||
'style': 'height:1px;width:1px;display:none;opacity:0;position:absolute;'
|
||||
})
|
||||
);
|
||||
// Attach the base the the parent
|
||||
$( this.getParent() ).append( this.$base );
|
||||
};
|
||||
|
||||
ve.Surface.prototype.setupSurfaceView = function() {
|
||||
this.$surface = $('<div />').attr('class', 'es-editor');
|
||||
this.$base.find('.es-visual').append( this.$surface );
|
||||
|
||||
/* Instantiate surface layer */
|
||||
this.view = new ve.ce.Surface( $( '.es-editor' ), this.getSurfaceModel() );
|
||||
this.context = new ve.ui.Context( this.view );
|
||||
};
|
||||
|
||||
ve.Surface.prototype.setupToolbars = function() {
|
||||
var _this = this;
|
||||
|
||||
// Build each toolbar
|
||||
$.each( this.options.toolbars, function(name, config) {
|
||||
if ( config !== null ) {
|
||||
if(name === 'top') {
|
||||
// Append toolbar wrapper at the top, just above .es-panes
|
||||
_this.toolbarWrapper[name] = $('<div />')
|
||||
.attr('class', 'es-toolbar-wrapper')
|
||||
.append(
|
||||
$('<div />').attr('class', 'es-toolbar')
|
||||
.append(
|
||||
$('<div />').attr('class', 'es-modes')
|
||||
).append(
|
||||
$('<div />').attr('style', 'clear:both')
|
||||
).append(
|
||||
$('<div />').attr('class', 'es-toolbar-shadow')
|
||||
)
|
||||
);
|
||||
|
||||
_this.$base.find('.es-panes').before( _this.toolbarWrapper[name] );
|
||||
}
|
||||
// Instantiate the toolbar
|
||||
_this['toolbar-' + name] = new ve.ui.Toolbar( _this.$base.find( '.es-toolbar' ), _this.view, config.tools );
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Setup sticky toolbar
|
||||
this.makeMainEditorToolbarFloat();
|
||||
};
|
||||
|
||||
/*
|
||||
* This code is responsible for switching toolbar into floating mode when scrolling (with
|
||||
* keyboard or mouse).
|
||||
* TODO: Determine if this would be better in ui.toolbar vs here.
|
||||
* TODO: This needs to be refactored so that it only works on the main editor top tool bar.
|
||||
*/
|
||||
ve.Surface.prototype.makeMainEditorToolbarFloat = function() {
|
||||
if ( !this.toolbarWrapper.top ) {
|
||||
return;
|
||||
}
|
||||
var $toolbarWrapper = this.toolbarWrapper.top,
|
||||
$toolbar = $toolbarWrapper.find('.es-toolbar');
|
||||
$window = $( window );
|
||||
|
||||
$window.scroll( function() {
|
||||
var toolbarWrapperOffset = $toolbarWrapper.offset();
|
||||
if ( $window.scrollTop() > toolbarWrapperOffset.top ) {
|
||||
if ( !$toolbarWrapper.hasClass( 'float' ) ) {
|
||||
var left = toolbarWrapperOffset.left,
|
||||
right = $window.width() - $toolbarWrapper.outerWidth() - left;
|
||||
$toolbarWrapper.css( 'height', $toolbarWrapper.height() ).addClass( 'float' );
|
||||
$toolbar.css( { 'left': left, 'right': right } );
|
||||
}
|
||||
} else {
|
||||
if ( $toolbarWrapper.hasClass( 'float' ) ) {
|
||||
$toolbarWrapper.css( 'height', 'auto' ).removeClass( 'float' );
|
||||
$toolbar.css( { 'left': 0, 'right': 0 } );
|
||||
}
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
ve.Surface.prototype.setupModes = function(){
|
||||
var _this = this;
|
||||
var activeModes = [];
|
||||
|
||||
// Loop through toolbar config to build modes
|
||||
$.each( _this.options.toolbars, function(name, toolbar){
|
||||
//if toolbar has modes
|
||||
if( toolbar.modes && toolbar.modes.length > 0 ) {
|
||||
for(var i=0;i<=toolbar.modes.length -1;i++) {
|
||||
$( _this.toolbarWrapper[name] )
|
||||
.find('.es-modes')
|
||||
.append(
|
||||
$('<div />').attr({
|
||||
'class': 'es-modes-button es-mode-' + toolbar.modes[i],
|
||||
'title': _this.options.modes[toolbar.modes[i]]
|
||||
})
|
||||
);
|
||||
if( !activeModes[mode] ) {
|
||||
activeModes.push( toolbar.modes[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Build elements in #es-panels for each activeMode
|
||||
if ( activeModes.length > 0 ) {
|
||||
for (var mode in activeModes) {
|
||||
var renderType = '';
|
||||
switch( activeModes[mode] ) {
|
||||
case 'render':
|
||||
renderType = 'es-render';
|
||||
break;
|
||||
case 'help':
|
||||
renderType = '';
|
||||
break;
|
||||
default:
|
||||
renderType = 'es-code';
|
||||
break;
|
||||
}
|
||||
_this.$base
|
||||
.find('.es-panels')
|
||||
.append(
|
||||
$('<div />').attr({
|
||||
'class': 'es-panel es-panel-' + activeModes[mode] + ' ' + renderType
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
/*
|
||||
Define this.modes
|
||||
Called after bulding elements.
|
||||
*/
|
||||
this.defineModes();
|
||||
|
||||
//Bind Mode events
|
||||
$.each( this.modes, function( name, mode ) {
|
||||
mode.$.click( function() {
|
||||
var disable = $(this).hasClass( 'es-modes-button-down' );
|
||||
var visible = _this.$base.hasClass( 'es-showData' );
|
||||
$('.es-modes-button').removeClass( 'es-modes-button-down' );
|
||||
$('.es-panel').hide();
|
||||
if ( disable ) {
|
||||
if ( visible ) {
|
||||
_this.$base.removeClass( 'es-showData' );
|
||||
$( window ).resize();
|
||||
}
|
||||
_this.currentMode = null;
|
||||
} else {
|
||||
$(this).addClass( 'es-modes-button-down' );
|
||||
mode.$panel.show();
|
||||
if ( !visible ) {
|
||||
_this.$base.addClass( 'es-showData' );
|
||||
$( window ).resize();
|
||||
}
|
||||
mode.update.call( mode );
|
||||
_this.currentMode = mode;
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
/* Bind some surface events for modes */
|
||||
this.getSurfaceModel().on( 'transact', function() {
|
||||
if ( _this.currentMode ) {
|
||||
_this.currentMode.update.call( _this.currentMode );
|
||||
}
|
||||
} );
|
||||
this.getSurfaceModel().on( 'select', function() {
|
||||
if ( _this.currentMode === _this.modes.history ) {
|
||||
_this.currentMode.update.call( _this.currentMode );
|
||||
}
|
||||
} );
|
||||
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
Define modes
|
||||
TODO: possibly extend this object via the config
|
||||
*/
|
||||
ve.Surface.prototype.defineModes = function() {
|
||||
var _this = this;
|
||||
this.modes = {
|
||||
'wikitext': {
|
||||
'$': _this.$base.find( '.es-mode-wikitext' ),
|
||||
'$panel': _this.$base.find( '.es-panel-wikitext' ),
|
||||
'update': function() {
|
||||
this.$panel.text(
|
||||
ve.dm.WikitextSerializer.stringify( _this.getDocumentModel().getPlainObject() )
|
||||
);
|
||||
}
|
||||
},
|
||||
'json': {
|
||||
'$': _this.$base.find( '.es-mode-json' ),
|
||||
'$panel': _this.$base.find( '.es-panel-json' ),
|
||||
'update': function() {
|
||||
this.$panel.text( ve.dm.JsonSerializer.stringify( _this.getDocumentModel().getPlainObject(), {
|
||||
'indentWith': ' '
|
||||
} ) );
|
||||
}
|
||||
},
|
||||
'html': {
|
||||
'$': _this.$base.find( '.es-mode-html' ),
|
||||
'$panel': _this.$base.find( '.es-panel-html' ),
|
||||
'update': function() {
|
||||
this.$panel.text(
|
||||
ve.dm.HtmlSerializer.stringify( _this.getDocumentModel().getPlainObject() )
|
||||
);
|
||||
}
|
||||
},
|
||||
'render': {
|
||||
'$': _this.$base.find( '.es-mode-render' ),
|
||||
'$panel': _this.$base.find( '.es-panel-render' ),
|
||||
'update': function() {
|
||||
this.$panel.html(
|
||||
ve.dm.HtmlSerializer.stringify( _this.getDocumentModel().getPlainObject() )
|
||||
);
|
||||
}
|
||||
},
|
||||
'history': {
|
||||
'$': _this.$base.find( '.es-mode-history' ),
|
||||
'$panel': _this.$base.find( '.es-panel-history' ),
|
||||
'update': function() {
|
||||
var history = _this.getSurfaceModel().getHistory(),
|
||||
i = history.length,
|
||||
end = Math.max( 0, i - 25 ),
|
||||
j,
|
||||
k,
|
||||
ops,
|
||||
events = '',
|
||||
z = 0,
|
||||
operations,
|
||||
data;
|
||||
|
||||
while ( --i >= end ) {
|
||||
z++;
|
||||
operations = [];
|
||||
for ( j = 0; j < history[i].stack.length; j++) {
|
||||
ops = history[i].stack[j].getOperations().slice(0);
|
||||
for ( k = 0; k < ops.length; k++ ) {
|
||||
data = ops[k].data || ops[k].length;
|
||||
if ( ve.isArray( data ) ) {
|
||||
data = data[0];
|
||||
if ( ve.isArray( data ) ) {
|
||||
data = data[0];
|
||||
}
|
||||
}
|
||||
if ( typeof data !== 'string' && typeof data !== 'number' ) {
|
||||
data = '-';
|
||||
}
|
||||
ops[k] = ops[k].type.substr( 0, 3 ) + '(' + data + ')';
|
||||
}
|
||||
operations.push('[' + ops.join( ', ' ) + ']');
|
||||
}
|
||||
events += '<div' + (z === _this.getSurfaceModel().undoIndex ? ' class="es-panel-history-active"' : '') + '>' + operations.join(', ') + '</div>';
|
||||
}
|
||||
|
||||
this.$panel.html( events );
|
||||
}
|
||||
},
|
||||
'help': {
|
||||
'$': _this.$base.find( '.es-mode-help' ),
|
||||
'$panel': _this.$base.find( '.es-panel-help' ),
|
||||
'update': function() {
|
||||
//TODO: Make this less ugly,
|
||||
//HOW?: Create api to register help items so that they may be generated here.
|
||||
/*jshint multistr:true */
|
||||
this.$panel.html('\
|
||||
<div class="es-help-title">Keyboard Shortcuts</div>\
|
||||
<div class="es-help-shortcuts-title">Clipboard</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +\
|
||||
<span class="es-help-key">C</span>\
|
||||
</span>\
|
||||
Copy selected text\
|
||||
</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +\
|
||||
<span class="es-help-key">X</span>\
|
||||
</span>\
|
||||
Cut selected text\
|
||||
</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +\
|
||||
<span class="es-help-key">V</span>\
|
||||
</span>\
|
||||
Paste text at the cursor\
|
||||
</div>\
|
||||
<div class="es-help-shortcuts-title">History navigation</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +\
|
||||
<span class="es-help-key">Z</span>\
|
||||
</span>\
|
||||
Undo\
|
||||
</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +\
|
||||
<span class="es-help-key">Y</span>\
|
||||
</span>\
|
||||
Redo\
|
||||
</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +\
|
||||
<span class="es-help-key">⇧</span> +\
|
||||
<span class="es-help-key">Z</span>\
|
||||
</span>\
|
||||
Redo\
|
||||
</div>\
|
||||
<div class="es-help-shortcuts-title">Formatting</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +\
|
||||
<span class="es-help-key">B</span>\
|
||||
</span>\
|
||||
Make selected text bold\
|
||||
</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +\
|
||||
<span class="es-help-key">I</span>\
|
||||
</span>\
|
||||
Make selected text italic\
|
||||
</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌘</span> +\
|
||||
<span class="es-help-key">K</span>\
|
||||
</span>\
|
||||
Make selected text a link\
|
||||
</div>\
|
||||
<div class="es-help-shortcuts-title">Selection</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">⇧</span> +\
|
||||
<span class="es-help-key">Arrow</span>\
|
||||
</span>\
|
||||
Adjust selection\
|
||||
</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌥</span> +\
|
||||
<span class="es-help-key">Arrow</span>\
|
||||
</span>\
|
||||
Move cursor by words or blocks\
|
||||
</div>\
|
||||
<div class="es-help-shortcut">\
|
||||
<span class="es-help-keys">\
|
||||
<span class="es-help-key">Ctrl <span class="es-help-key-or">or</span> ⌥</span> +\
|
||||
<span class="es-help-key">⇧</span> +\
|
||||
<span class="es-help-key">Arrow</span>\
|
||||
</span>\
|
||||
Adjust selection by words or blocks\
|
||||
</div>');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/* Get Methods */
|
||||
|
||||
ve.Surface.prototype.getSurfaceModel = function() {
|
||||
return this.surfaceModel;
|
||||
};
|
||||
|
@ -43,6 +457,14 @@ ve.Surface.prototype.getDocumentModel = function() {
|
|||
return this.documentModel;
|
||||
};
|
||||
|
||||
ve.Surface.prototype.getID = function() {
|
||||
return this.id;
|
||||
ve.Surface.prototype.getView = function() {
|
||||
return this.view;
|
||||
};
|
||||
|
||||
ve.Surface.prototype.getContext = function() {
|
||||
return this.context;
|
||||
};
|
||||
|
||||
ve.Surface.prototype.getParent = function() {
|
||||
return this.parent;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue