mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-27 12:16:51 +00:00
Merge branch 'dmrewrite' of ssh://review/mediawiki/extensions/VisualEditor into dmrewrite
This commit is contained in:
commit
8a35e6eafe
|
@ -33,10 +33,9 @@ class VisualEditorHooks {
|
|||
return true;
|
||||
}
|
||||
/**
|
||||
* Allow edits to the namespace only by admins
|
||||
* Code used from Extension:NamespaceProtection
|
||||
*
|
||||
*/
|
||||
public static function canUserEditPage( &$title, &$user, $action, &$result ){
|
||||
public static function namespaceProtection( &$title, &$user, $action, &$result ){
|
||||
global $wgUser, $wgNamespaceProtection;
|
||||
|
||||
if ( array_key_exists( $title->mNamespace, $wgNamespaceProtection ) ) {
|
||||
|
|
|
@ -97,10 +97,10 @@ $wgResourceModules += array(
|
|||
// ve
|
||||
'jquery/jquery.json.js',
|
||||
've2/ve.js',
|
||||
've2/ve.NodeFactory.js',
|
||||
've2/ve.EventEmitter.js',
|
||||
've2/ve.Factory.js',
|
||||
've2/ve.Position.js',
|
||||
've2/ve.Range.js',
|
||||
've2/ve.EventEmitter.js',
|
||||
've2/ve.Node.js',
|
||||
've2/ve.BranchNode.js',
|
||||
've2/ve.LeafNode.js',
|
||||
|
@ -110,14 +110,17 @@ $wgResourceModules += array(
|
|||
// dm
|
||||
've2/dm/ve.dm.js',
|
||||
've2/dm/ve.dm.NodeFactory.js',
|
||||
've2/dm/ve.dm.AnnotationFactory.js',
|
||||
've2/dm/ve.dm.Node.js',
|
||||
've2/dm/ve.dm.BranchNode.js',
|
||||
've2/dm/ve.dm.LeafNode.js',
|
||||
've2/dm/ve.dm.Annotation.js',
|
||||
've2/dm/ve.dm.TransactionProcessor.js',
|
||||
've2/dm/ve.dm.Transaction.js',
|
||||
've2/dm/ve.dm.Surface.js',
|
||||
've2/dm/ve.dm.Document.js',
|
||||
've2/dm/ve.dm.DocumentSynchronizer.js',
|
||||
've2/dm/ve.dm.Converter.js',
|
||||
've2/dm/ve.dm.HTMLConverter.js',
|
||||
|
||||
've2/dm/nodes/ve.dm.AlienInlineNode.js',
|
||||
|
@ -136,6 +139,9 @@ $wgResourceModules += array(
|
|||
've2/dm/nodes/ve.dm.TableRowNode.js',
|
||||
've2/dm/nodes/ve.dm.TextNode.js',
|
||||
|
||||
've2/dm/annotations/ve.dm.LinkAnnotation.js',
|
||||
've2/dm/annotations/ve.dm.TextStyleAnnotation.js',
|
||||
|
||||
've/dm/serializers/ve.dm.AnnotationSerializer.js',
|
||||
've/dm/serializers/ve.dm.HtmlSerializer.js',
|
||||
've/dm/serializers/ve.dm.JsonSerializer.js',
|
||||
|
@ -236,7 +242,7 @@ $wgAPIModules['ve-parsoid'] = 'ApiVisualEditor';
|
|||
// Integration Hooks
|
||||
$wgAutoloadClasses['VisualEditorHooks'] = $dir . 'VisualEditor.hooks.php';
|
||||
$wgHooks['BeforePageDisplay'][] = 'VisualEditorHooks::onPageDisplay';
|
||||
$wgHooks['userCan'][] = 'VisualEditorHooks::canUserEditPage';
|
||||
$wgHooks['userCan'][] = 'VisualEditorHooks::namespaceProtection';
|
||||
|
||||
// API for retrieving wikidom parse results
|
||||
$wgAutoloadClasses['ApiQueryParseTree'] = $dir . 'api/ApiQueryParseTree.php';
|
||||
|
|
|
@ -89,10 +89,10 @@ include( '../../modules/sandbox/base.php' );
|
|||
<script src="../../modules/jquery/jquery.js"></script>
|
||||
<script src="../../modules/jquery/jquery.json.js"></script>
|
||||
<script src="../../modules/ve2/ve.js"></script>
|
||||
<script src="../../modules/ve2/ve.NodeFactory.js"></script>
|
||||
<script src="../../modules/ve2/ve.EventEmitter.js"></script>
|
||||
<script src="../../modules/ve2/ve.Factory.js"></script>
|
||||
<script src="../../modules/ve2/ve.Position.js"></script>
|
||||
<script src="../../modules/ve2/ve.Range.js"></script>
|
||||
<script src="../../modules/ve2/ve.EventEmitter.js"></script>
|
||||
<script src="../../modules/ve2/ve.Node.js"></script>
|
||||
<script src="../../modules/ve2/ve.BranchNode.js"></script>
|
||||
<script src="../../modules/ve2/ve.LeafNode.js"></script>
|
||||
|
@ -102,9 +102,11 @@ include( '../../modules/sandbox/base.php' );
|
|||
<!-- dm -->
|
||||
<script src="../../modules/ve2/dm/ve.dm.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.NodeFactory.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.AnnotationFactory.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Node.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.BranchNode.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.LeafNode.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Annotation.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.TransactionProcessor.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Transaction.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Surface.js"></script>
|
||||
|
@ -112,6 +114,7 @@ include( '../../modules/sandbox/base.php' );
|
|||
<script src="../../modules/ve2/dm/ve.dm.DocumentSynchronizer.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Transaction.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.TransactionProcessor.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Converter.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.HTMLConverter.js"></script>
|
||||
|
||||
<script src="../../modules/ve2/dm/nodes/ve.dm.AlienInlineNode.js"></script>
|
||||
|
@ -130,6 +133,9 @@ include( '../../modules/sandbox/base.php' );
|
|||
<script src="../../modules/ve2/dm/nodes/ve.dm.TableRowNode.js"></script>
|
||||
<script src="../../modules/ve2/dm/nodes/ve.dm.TextNode.js"></script>
|
||||
|
||||
<script src="../../modules/ve2/dm/annotations/ve.dm.LinkAnnotation.js"></script>
|
||||
<script src="../../modules/ve2/dm/annotations/ve.dm.TextStyleAnnotation.js"></script>
|
||||
|
||||
<script src="../../modules/ve/dm/serializers/ve.dm.AnnotationSerializer.js"></script>
|
||||
<script src="../../modules/ve/dm/serializers/ve.dm.HtmlSerializer.js"></script>
|
||||
<script src="../../modules/ve/dm/serializers/ve.dm.JsonSerializer.js"></script>
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
padding: 0.25em;
|
||||
height: 22px;
|
||||
margin-right: 0.125em;
|
||||
border: solid 1px transparent;
|
||||
}
|
||||
.ve-action-button:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
display: block;
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
}
|
||||
.ve-action-button:hover {
|
||||
border-color: #eeeeee;
|
||||
|
@ -29,9 +29,101 @@
|
|||
.ve-action-button:active,
|
||||
.ve-action-button-down {
|
||||
border-color: #dddddd;
|
||||
background-position: top left;
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0px 1px 4px 0px rgba(0, 0, 0, 0.07);
|
||||
-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);
|
||||
}
|
||||
|
||||
/* Save dialog styles */
|
||||
#ve-saveDialog {
|
||||
top: 0px;
|
||||
right: 2.5em;
|
||||
width: 35em;
|
||||
}
|
||||
|
||||
#ve-saveDialog > .ve-dialog-divider {
|
||||
border-top:1px solid #dddddd;
|
||||
padding: 10px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#ve-saveDialog br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
#ve-saveDialog > .ve-dialog-divider > .ve-dialog-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#ve-saveDialog > .ve-dialog-divider > input[type='text'] {
|
||||
width: 96%;
|
||||
font-size: 12px;
|
||||
padding: 4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#ve-saveDialog > .ve-dialog-divider > input[type='checkbox'] {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.ve-closeBtn {
|
||||
width: 22px;
|
||||
background-image: url(../ve2/ui/styles/images/close.png);
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
/* mini save button */
|
||||
.ve-saveBtn {
|
||||
right: .025em;
|
||||
width: 22px;
|
||||
background-image: url(../ve2/ui/styles/images/close.png);
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
/* inspector styles */
|
||||
.es-inspector-savebutton {
|
||||
padding-right: 24px;
|
||||
border:1px solid transparent;
|
||||
border-radius: 0.125em;
|
||||
-webkit-border-radius: 0.125em;
|
||||
-moz-border-radius: 0.125em;
|
||||
-o-border-radius: 0.125em;
|
||||
/* need new button */
|
||||
/* @embed */
|
||||
background-image: url(../ve2/ui/styles/images/save.png);
|
||||
background-position: center right;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.doSaveBtn {
|
||||
position: absolute;
|
||||
border: 1px solid rgb(196,229,154);
|
||||
margin-top: 10px;
|
||||
right: 10px;
|
||||
font-size: 12px;
|
||||
padding: 5px 10px;
|
||||
background-image: url(../ve2/ui/styles/images/close.png);
|
||||
background-position: center right;
|
||||
background-repeat: no-repeat;
|
||||
/* Fancy CSS background */
|
||||
background-image: linear-gradient(bottom, rgb(195,229,154) 0%, rgb(240,251,225) 100%);
|
||||
background-image: -o-linear-gradient(bottom, rgb(195,229,154) 0%, rgb(240,251,225) 100%);
|
||||
background-image: -moz-linear-gradient(bottom, rgb(195,229,154) 0%, rgb(240,251,225) 100%);
|
||||
background-image: -webkit-linear-gradient(bottom, rgb(195,229,154) 0%, rgb(240,251,225) 100%);
|
||||
background-image: -ms-linear-gradient(bottom, rgb(195,229,154) 0%, rgb(240,251,225) 100%);
|
||||
background-image: -webkit-gradient(
|
||||
linear,
|
||||
left bottom,
|
||||
left top,
|
||||
color-stop(0, rgb(195,229,154)),
|
||||
color-stop(1, rgb(240,251,225))
|
||||
);
|
||||
}
|
||||
.doSaveBtn > div.doSaveBtnIcon {
|
||||
height:2em;
|
||||
width:2em;
|
||||
float:right;
|
||||
background: transparent;
|
||||
background-image: url(../ve2/ui/styles/images/accept.png);
|
||||
background-position: center right;
|
||||
background-repeat: no-repeat;
|
||||
}
|
|
@ -8,19 +8,10 @@
|
|||
pageName = mw.config.get( 'wgPageName' ),
|
||||
validNamespace = mw.config.get('wgCanonicalNamespace') === 'VisualEditor' ? true: false;
|
||||
|
||||
this.$content = $('#content');
|
||||
// Store content padding so that it can be restored.
|
||||
this.contentPadding = this.$content.css('padding');
|
||||
this.mainEditor = null;
|
||||
|
||||
this.$spinner = $('<div />')
|
||||
.attr({
|
||||
'id': 've-loader-spinner',
|
||||
'class': 'mw-ajax-loader'
|
||||
}).css({
|
||||
'height': this.$content.height() + 'px',
|
||||
'width': (this.$content.width() -20 ) + 'px'
|
||||
});
|
||||
this.$content = $('#content');
|
||||
// modify / stash content styles
|
||||
this.prepareContentStyles();
|
||||
|
||||
// On VisualEditor namespace ?
|
||||
if ( validNamespace ) {
|
||||
|
@ -80,39 +71,39 @@
|
|||
}
|
||||
}
|
||||
};
|
||||
$editor = $('<div id="ve-editor"></div>');
|
||||
this.$editor = $('<div id="ve-editor"></div>');
|
||||
this.$spinner.hide();
|
||||
this.$content
|
||||
.css('padding', '0px 0px 0px 1px')
|
||||
.append( $editor );
|
||||
|
||||
this.$content.css({
|
||||
'padding':'0px 0px 0px 1px'
|
||||
}).append( this.$editor );
|
||||
|
||||
|
||||
this.mainEditor = new ve.Surface( '#ve-editor', $html[0], options );
|
||||
|
||||
$editor.find('.es-panes')
|
||||
this.$editor.find('.es-panes')
|
||||
.css('padding', this.contentPadding );
|
||||
|
||||
// Save BTN
|
||||
$editor.find('.es-modes')
|
||||
this.$editor.find('.es-modes')
|
||||
.append(
|
||||
$('<div />')
|
||||
.attr('class', 've-action-button')
|
||||
.attr('class', 've-action-button es-inspector-savebutton')
|
||||
.text('Save')
|
||||
.click(function(){
|
||||
// show save dialog
|
||||
_this.save();
|
||||
_this.showSaveDialog();
|
||||
// show/hide dialog
|
||||
_this.$dialog.toggle();
|
||||
})
|
||||
).append(
|
||||
$('<div />')
|
||||
.attr('class', 've-action-button')
|
||||
.text('X')
|
||||
.attr('class', 've-action-button ve-closeBtn')
|
||||
.click(function(){
|
||||
// back to read mode
|
||||
_this.cleanup();
|
||||
_this.mainEditor = null;
|
||||
})
|
||||
);
|
||||
this.initSaveDialog();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -177,9 +168,9 @@
|
|||
var _this = this;
|
||||
_this.showSpinner();
|
||||
// Save
|
||||
|
||||
_this.getParsoidWikitextAndSave( function( content ){
|
||||
// cleanup
|
||||
_this.$dialog.toggle();
|
||||
_this.cleanup();
|
||||
// load saved page
|
||||
_this.$content
|
||||
|
@ -187,13 +178,107 @@
|
|||
});
|
||||
};
|
||||
|
||||
veCore.prototype.showSaveDialog = function(){
|
||||
veCore.prototype.initSaveDialog = function(){
|
||||
var _this = this;
|
||||
this.$dialog =
|
||||
$('<div />')
|
||||
.attr({
|
||||
'id': 've-saveDialog',
|
||||
'class': 'es-inspector'
|
||||
}).append(
|
||||
$('<div />')
|
||||
.attr('class', 'es-inspector-title')
|
||||
.text('Save your changes')
|
||||
).append(
|
||||
$('<div />')
|
||||
.attr('class', 'es-inspector-button ve-saveBtn')
|
||||
.click(function(){
|
||||
_this.$dialog.toggle();
|
||||
})
|
||||
).append(
|
||||
$('<div />')
|
||||
.attr('class', 've-dialog-divider')
|
||||
.append(
|
||||
$('<div />')
|
||||
.text("Describe what you changed")
|
||||
).append(
|
||||
$('<input />')
|
||||
.attr({
|
||||
'type':'text'
|
||||
})
|
||||
).append(
|
||||
$('<br />')
|
||||
).append(
|
||||
$('<div />')
|
||||
.attr('class', 've-dialog-left')
|
||||
.append(
|
||||
$('<input />')
|
||||
.attr({
|
||||
'type': 'checkbox',
|
||||
'name': 'chkMinorEdit',
|
||||
'id': 'chkMinorEdit'
|
||||
})
|
||||
).append(
|
||||
$('<label />')
|
||||
.attr('for', 'chkMinorEdit')
|
||||
// i18n
|
||||
.html('This is a <a href="/wiki/Minor_edit">minor edit</a>')
|
||||
).append(
|
||||
$('<br />')
|
||||
).append(
|
||||
$('<input />')
|
||||
.attr({
|
||||
'type': 'checkbox',
|
||||
'name': 'chkWatchlist',
|
||||
'id': 'chkWatchlist'
|
||||
})
|
||||
).append(
|
||||
$('<label />')
|
||||
.attr('for', 'chkWatchlist')
|
||||
// i18n
|
||||
.text('Watch this page')
|
||||
)
|
||||
).append(
|
||||
$('<div />')
|
||||
.attr('class', 've-action-button es-inspector-savebutton doSaveBtn')
|
||||
.text('Save page')
|
||||
.click(function(){
|
||||
_this.save();
|
||||
})
|
||||
.append(
|
||||
$('<div />')
|
||||
.attr('class', 'doSaveBtnIcon')
|
||||
)
|
||||
).append(
|
||||
$('<br />')
|
||||
)
|
||||
).append(
|
||||
$('<div />')
|
||||
.attr('class', 've-dialog-divider')
|
||||
.append(
|
||||
$("<p />")
|
||||
// TODO: Complete text and i18n
|
||||
.text('By editing this page, yadda yadda yadda')
|
||||
)
|
||||
);
|
||||
this.$editor
|
||||
.find('.es-inspector-savebutton')
|
||||
.after ( this.$dialog );
|
||||
|
||||
};
|
||||
|
||||
veCore.prototype.showSpinner = function(){
|
||||
var _this = this;
|
||||
//remove it
|
||||
|
||||
this.$spinner = $('<div />')
|
||||
.attr({
|
||||
'id': 've-loader-spinner',
|
||||
'class': 'mw-ajax-loader'
|
||||
}).css({
|
||||
'height': this.$content.height() + 'px',
|
||||
'width': (this.$content.width() -20 ) + 'px'
|
||||
});
|
||||
|
||||
_this.$spinner.remove();
|
||||
//hide all of the #content element children
|
||||
_this.$content
|
||||
|
@ -206,8 +291,35 @@
|
|||
);
|
||||
};
|
||||
|
||||
/* Make a backup of the #content div styles
|
||||
In order to tuck the toolbar close under the tabs,
|
||||
We need to change the padding before inserting the toolbar
|
||||
Additionally, the transitions must be removed so that
|
||||
when adjusting the padding, there is no animated transition.
|
||||
*/
|
||||
veCore.prototype.prepareContentStyles = function(){
|
||||
// Store Padding and transitions
|
||||
this.contentPadding = this.$content.css('padding');
|
||||
this.contentTransition = {
|
||||
'transition': this.$content.css('transition'),
|
||||
'transition-property': this.$content.css('transition-property'),
|
||||
'-moz-transition': this.$content.css('-moz-transition'),
|
||||
'-webkit-transition': this.$content.css('-webkit-transition'),
|
||||
'-o-transition': this.$content.css('-o-transition')
|
||||
};
|
||||
// Squash transitions
|
||||
this.$content.css({
|
||||
'transition': 'none',
|
||||
'transition-property': 'none',
|
||||
'-moz-transition': 'none',
|
||||
'-webkit-transition': 'none',
|
||||
'-o-transition': 'color 0 ease-in'
|
||||
});
|
||||
};
|
||||
|
||||
//back to view
|
||||
veCore.prototype.cleanup = function(){
|
||||
this.mainEditor = null;
|
||||
this.selectTab( 'ca-view' );
|
||||
this.$content
|
||||
.find('#mw-content-text, #bodyContent, #firstHeading').show()
|
||||
|
|
|
@ -56,12 +56,6 @@ ve.ce.TextNode.htmlCharacters = {
|
|||
* @member
|
||||
*/
|
||||
ve.ce.TextNode.annotationRenderers = {
|
||||
'object/hook': {
|
||||
'open': function( data ) {
|
||||
return '<span class="ve-ce-content-format-object">' + data.html;
|
||||
},
|
||||
'close': '</span>'
|
||||
},
|
||||
'textStyle/bold': {
|
||||
'open': '<b>',
|
||||
'close': '</b>'
|
||||
|
@ -138,8 +132,14 @@ ve.ce.TextNode.prototype.getHtml = function() {
|
|||
htmlChars = ve.ce.TextNode.htmlCharacters,
|
||||
renderers = ve.ce.TextNode.annotationRenderers,
|
||||
out = '',
|
||||
i,
|
||||
j,
|
||||
hash,
|
||||
left = '',
|
||||
right,
|
||||
open,
|
||||
close,
|
||||
index,
|
||||
leftPlain,
|
||||
rightPlain,
|
||||
hashStack = [],
|
||||
|
@ -151,9 +151,9 @@ ve.ce.TextNode.prototype.getHtml = function() {
|
|||
|
||||
for ( var hash in annotations ) {
|
||||
annotation = annotations[hash];
|
||||
out += typeof renderers[annotation.type].open === 'function'
|
||||
? renderers[annotation.type].open( annotation.data )
|
||||
: renderers[annotation.type].open;
|
||||
out += typeof renderers[annotation.type].open === 'function' ?
|
||||
renderers[annotation.type].open( annotation.data ) :
|
||||
renderers[annotation.type].open;
|
||||
hashStack.push( hash );
|
||||
annotationStack[hash] = annotation;
|
||||
}
|
||||
|
@ -166,9 +166,9 @@ ve.ce.TextNode.prototype.getHtml = function() {
|
|||
|
||||
for ( var hash in annotations ) {
|
||||
annotation = annotations[hash];
|
||||
out += typeof renderers[annotation.type].close === 'function'
|
||||
? renderers[annotation.type].close( annotation.data )
|
||||
: renderers[annotation.type].close;
|
||||
out += typeof renderers[annotation.type].close === 'function' ?
|
||||
renderers[annotation.type].close( annotation.data ) :
|
||||
renderers[annotation.type].close;
|
||||
|
||||
// new version
|
||||
hashStack.pop();
|
||||
|
@ -187,18 +187,18 @@ ve.ce.TextNode.prototype.getHtml = function() {
|
|||
return out;
|
||||
};
|
||||
|
||||
for ( var i = 0; i < data.length; i++ ) {
|
||||
for ( i = 0; i < data.length; i++ ) {
|
||||
right = data[i];
|
||||
leftPlain = typeof left === 'string';
|
||||
rightPlain = typeof right === 'string';
|
||||
|
||||
if ( !leftPlain && rightPlain ) {
|
||||
// [formatted][plain]
|
||||
var close = {};
|
||||
for ( var j = hashStack.length - 1; j >= 0; j-- ) {
|
||||
close = {};
|
||||
for ( j = hashStack.length - 1; j >= 0; j-- ) {
|
||||
close[hashStack[j]] = annotationStack[hashStack[j]];
|
||||
}
|
||||
out += closeAnnotations( close );
|
||||
out += closeAnnotations( close );
|
||||
} else if ( leftPlain && !rightPlain ) {
|
||||
// [plain][formatted]
|
||||
out += openAnnotations( right[1] );
|
||||
|
@ -207,24 +207,24 @@ ve.ce.TextNode.prototype.getHtml = function() {
|
|||
|
||||
// setting index to undefined is is necessary to it does not use value from
|
||||
// the previous iteration
|
||||
var open = {},
|
||||
index = undefined;
|
||||
open = {},
|
||||
index = undefined;
|
||||
|
||||
for ( var hash in left[1] ) {
|
||||
for ( hash in left[1] ) {
|
||||
if ( !( hash in right[1] ) ) {
|
||||
index = ( index === undefined )
|
||||
? hashStack.indexOf( hash )
|
||||
: Math.min( index, hashStack.indexOf( hash ) );
|
||||
index = ( index === undefined ) ?
|
||||
hashStack.indexOf( hash ) :
|
||||
Math.min( index, hashStack.indexOf( hash ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( index !== undefined ) {
|
||||
var close = {};
|
||||
for ( var j = hashStack.length - 1; j >= index; j-- ) {
|
||||
close = {};
|
||||
for ( j = hashStack.length - 1; j >= index; j-- ) {
|
||||
close[hashStack[j]] = annotationStack[hashStack[j]];
|
||||
}
|
||||
|
||||
for ( var j = index; j < hashStack.length; j++ ) {
|
||||
for ( j = index; j < hashStack.length; j++ ) {
|
||||
if ( hashStack[j] in right[1] && hashStack[j] in left[1] ) {
|
||||
open[hashStack[j]] = annotationStack[hashStack[j]];
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ ve.ce.TextNode.prototype.getHtml = function() {
|
|||
out += closeAnnotations( close );
|
||||
}
|
||||
|
||||
for ( var hash in right[1] ) {
|
||||
for ( hash in right[1] ) {
|
||||
if ( !( hash in left[1] ) ) {
|
||||
open[hash] = right[1][hash];
|
||||
}
|
||||
|
@ -246,11 +246,11 @@ ve.ce.TextNode.prototype.getHtml = function() {
|
|||
left = right;
|
||||
}
|
||||
|
||||
var close = {};
|
||||
for ( var j = hashStack.length - 1; j >= 0; j-- ) {
|
||||
close = {};
|
||||
for ( j = hashStack.length - 1; j >= 0; j-- ) {
|
||||
close[hashStack[j]] = annotationStack[hashStack[j]];
|
||||
}
|
||||
out += closeAnnotations( close );
|
||||
out += closeAnnotations( close );
|
||||
|
||||
return out;
|
||||
};
|
||||
|
@ -261,4 +261,4 @@ ve.ce.nodeFactory.register( 'text', ve.ce.TextNode );
|
|||
|
||||
/* Inheritance */
|
||||
|
||||
ve.extendClass( ve.ce.TextNode, ve.ce.LeafNode );
|
||||
ve.extendClass( ve.ce.TextNode, ve.ce.LeafNode );
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
* ContentEditable node factory.
|
||||
*
|
||||
* @class
|
||||
* @extends {ve.NodeFactory}
|
||||
* @extends {ve.Factory}
|
||||
* @constructor
|
||||
*/
|
||||
ve.ce.NodeFactory = function() {
|
||||
// Inheritance
|
||||
ve.NodeFactory.call( this );
|
||||
ve.Factory.call( this );
|
||||
};
|
||||
|
||||
/* Methods */
|
||||
|
@ -28,7 +28,7 @@ ve.ce.NodeFactory.prototype.canNodeBeSplit = function( type ) {
|
|||
|
||||
/* Inheritance */
|
||||
|
||||
ve.extendClass( ve.ce.NodeFactory, ve.NodeFactory );
|
||||
ve.extendClass( ve.ce.NodeFactory, ve.Factory );
|
||||
|
||||
/* Initialization */
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ ve.ce.Surface = function( $container, model ) {
|
|||
'mouseup': this.proxy( this.onMouseUp ),
|
||||
'mousemove': this.proxy( this.onMouseMove ),
|
||||
'cut copy': this.proxy( this.onCutCopy ),
|
||||
'beforepaste paste': this.proxy( this.onPaste ),
|
||||
'beforepaste paste': this.proxy( this.onPaste )
|
||||
} );
|
||||
|
||||
// Initialization
|
||||
|
@ -51,12 +51,19 @@ ve.ce.Surface.prototype.onKeyDown = function( e ) {
|
|||
var offset = this.getOffset( rangySel.anchorNode, rangySel.anchorOffset );
|
||||
console.log( 'onKeyDown', 'offset', offset );
|
||||
|
||||
var relativeContentOffset,
|
||||
relativeStructuralOffset,
|
||||
relativeStructuralOffsetNode,
|
||||
hasSlug;
|
||||
|
||||
switch ( e.which ) {
|
||||
case 37: // left arrow key
|
||||
var relativeContentOffset = this.documentView.model.getRelativeContentOffset( offset, -1 );
|
||||
var relativeStructuralOffset = this.documentView.model.getRelativeStructuralOffset( offset - 1, -1, true );
|
||||
var relativeStructuralOffsetNode = this.documentView.documentNode.getNodeFromOffset( relativeStructuralOffset );
|
||||
var hasSlug = relativeStructuralOffsetNode.hasSlugAtOffset( relativeStructuralOffset );
|
||||
relativeContentOffset = this.documentView.model.getRelativeContentOffset( offset, -1 );
|
||||
relativeStructuralOffset =
|
||||
this.documentView.model.getRelativeStructuralOffset( offset - 1, -1, true );
|
||||
relativeStructuralOffsetNode =
|
||||
this.documentView.documentNode.getNodeFromOffset( relativeStructuralOffset );
|
||||
hasSlug = relativeStructuralOffsetNode.hasSlugAtOffset( relativeStructuralOffset );
|
||||
if ( hasSlug ) {
|
||||
if ( relativeContentOffset > offset ) {
|
||||
this.showCursor( relativeStructuralOffset );
|
||||
|
@ -67,12 +74,13 @@ ve.ce.Surface.prototype.onKeyDown = function( e ) {
|
|||
this.showCursor( relativeContentOffset );
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
case 39: // right arrow key
|
||||
var relativeContentOffset = this.documentView.model.getRelativeContentOffset( offset, 1 );
|
||||
var relativeStructuralOffset = this.documentView.model.getRelativeStructuralOffset( offset + 1, 1, true );
|
||||
var relativeStructuralOffsetNode = this.documentView.documentNode.getNodeFromOffset( relativeStructuralOffset );
|
||||
var hasSlug = relativeStructuralOffsetNode.hasSlugAtOffset( relativeStructuralOffset );
|
||||
relativeContentOffset = this.documentView.model.getRelativeContentOffset( offset, 1 );
|
||||
relativeStructuralOffset =
|
||||
this.documentView.model.getRelativeStructuralOffset( offset + 1, 1, true );
|
||||
relativeStructuralOffsetNode =
|
||||
this.documentView.documentNode.getNodeFromOffset( relativeStructuralOffset );
|
||||
hasSlug = relativeStructuralOffsetNode.hasSlugAtOffset( relativeStructuralOffset );
|
||||
if ( hasSlug ) {
|
||||
if ( relativeContentOffset < offset ) {
|
||||
this.showCursor( relativeStructuralOffset );
|
||||
|
@ -83,7 +91,6 @@ ve.ce.Surface.prototype.onKeyDown = function( e ) {
|
|||
this.showCursor( relativeContentOffset );
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -108,13 +115,16 @@ ve.ce.Surface.prototype.onMouseDown = function( e ) {
|
|||
var closestLeafOffset = closestLeafModel.getRoot().getOffsetFromNode( closestLeafModel );
|
||||
console.log( 'onMouseDown', 'closestLeafOffset', closestLeafOffset );
|
||||
|
||||
var nearestContentOffset = this.documentView.model.getNearestContentOffset( closestLeafOffset, -1 );
|
||||
var nearestContentOffset =
|
||||
this.documentView.model.getNearestContentOffset( closestLeafOffset, -1 );
|
||||
console.log( 'onMouseDown', 'nearestContentOffset', nearestContentOffset );
|
||||
|
||||
var nearestStructuralOffset = this.documentView.model.getNearestStructuralOffset( closestLeafOffset, 0, true );
|
||||
var nearestStructuralOffset =
|
||||
this.documentView.model.getNearestStructuralOffset( closestLeafOffset, 0, true );
|
||||
console.log( 'onMouseDown', 'nearestStructuralOffset', nearestStructuralOffset );
|
||||
|
||||
var nearestStructuralOffsetNode = this.documentView.documentNode.getNodeFromOffset( nearestStructuralOffset );
|
||||
var nearestStructuralOffsetNode =
|
||||
this.documentView.documentNode.getNodeFromOffset( nearestStructuralOffset );
|
||||
console.log( 'onMouseDown', 'nearestStructuralOffsetNode', nearestStructuralOffsetNode );
|
||||
|
||||
var hasSlug = nearestStructuralOffsetNode.hasSlugAtOffset( nearestStructuralOffset );
|
||||
|
@ -193,7 +203,9 @@ ve.ce.Surface.prototype.onPaste = function( e ) {
|
|||
pasteData = ( _this.clipboard[key] ) ? _this.clipboard[key] : pasteString.split('');
|
||||
|
||||
// transact
|
||||
var tx = ve.dm.Transaction.newFromInsertion( _this.documentView.model, insertionPoint, pasteData );
|
||||
var tx = ve.dm.Transaction.newFromInsertion(
|
||||
_this.documentView.model, insertionPoint, pasteData
|
||||
);
|
||||
ve.dm.TransactionProcessor.commit( _this.documentView.model, tx );
|
||||
|
||||
// place cursor
|
||||
|
@ -280,14 +292,19 @@ ve.ce.Surface.prototype.getOffset = function( DOMnode, DOMoffset ) {
|
|||
|
||||
|
||||
/**
|
||||
* Based on a given offset returns DOM node and offset that can be used to place a cursor (with rangy)
|
||||
* Based on a given offset returns DOM node and offset that can be used to place a cursor.
|
||||
*
|
||||
* The results of this function are meant to be used with rangy.
|
||||
*
|
||||
* @method
|
||||
* @param offset {Integer} Linear model offset
|
||||
* @returns {Object} Object containing a node and offset property where node is an HTML element and
|
||||
* offset is the position within the element
|
||||
*/
|
||||
ve.ce.Surface.prototype.getNodeAndOffset = function( offset ) {
|
||||
var node = this.documentView.getNodeFromOffset( offset ),
|
||||
startOffset = this.documentView.getDocumentNode().getOffsetFromNode( node ) + ( ( node.isWrapped() ) ? 1 : 0 ),
|
||||
startOffset = this.documentView.getDocumentNode().getOffsetFromNode( node ) +
|
||||
( ( node.isWrapped() ) ? 1 : 0 ),
|
||||
current = [node.$.contents(), 0],
|
||||
stack = [current],
|
||||
item,
|
||||
|
|
42
modules/ve2/dm/annotations/ve.dm.LinkAnnotation.js
Normal file
42
modules/ve2/dm/annotations/ve.dm.LinkAnnotation.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* DataModel annotation for a link.
|
||||
*
|
||||
* @class
|
||||
* @constructor
|
||||
* @extends {ve.dm.Annotation}
|
||||
*/
|
||||
ve.dm.LinkAnnotation = function() {
|
||||
// Inheritance
|
||||
ve.dm.Annotation.call( this );
|
||||
};
|
||||
|
||||
/* Static Members */
|
||||
|
||||
/**
|
||||
* Converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.LinkAnnotation.converters = {
|
||||
'tags': 'a',
|
||||
'toHtml': function( subType, annotation ) {
|
||||
return annotation.type &&
|
||||
ve.dm.createHtmlElement( 'a', { 'data-type': 'link/' + subType } );
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
// FIXME: the parser currently doesn't output this data this way
|
||||
// Internal links get 'linkType': 'internal' in the data-mw-rt attrib, while external
|
||||
// links currently get nothing
|
||||
return { 'type': 'link/' + ( element.getAttribute( 'data-type' ) || 'unknown' ) };
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.annotationFactory.register( 'link', ve.dm.LinkAnnotation );
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.extendClass( ve.dm.LinkAnnotation, ve.dm.Annotation );
|
58
modules/ve2/dm/annotations/ve.dm.TextStyleAnnotation.js
Normal file
58
modules/ve2/dm/annotations/ve.dm.TextStyleAnnotation.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* DataModel annotation for a text style.
|
||||
*
|
||||
* @class
|
||||
* @constructor
|
||||
* @extends {ve.dm.Annotation}
|
||||
*/
|
||||
ve.dm.TextStyleAnnotation = function() {
|
||||
// Inheritance
|
||||
ve.dm.Annotation.call( this );
|
||||
};
|
||||
|
||||
/* Static Members */
|
||||
|
||||
/**
|
||||
* Converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.TextStyleAnnotation.converters = {
|
||||
'tags': ['i', 'b', 'u', 's', 'small', 'big', 'span'],
|
||||
'toHtml': function( subType, annotation ) {
|
||||
return annotation.type && ve.dm.createHtmlElement( ( {
|
||||
'italic': 'i',
|
||||
'bold': 'b',
|
||||
'underline': 'u',
|
||||
'strike': 's',
|
||||
'small': 'small',
|
||||
'big': 'big',
|
||||
'span': 'span'
|
||||
// TODO: Add other supported HTML inline elements to this list
|
||||
} )[subType] );
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return {
|
||||
'type': 'textStyle/' + ( {
|
||||
'i': 'italic',
|
||||
'b': 'bold',
|
||||
'u': 'underline',
|
||||
's': 'strike',
|
||||
'small': 'small',
|
||||
'big': 'big',
|
||||
'span': 'span'
|
||||
// TODO: Add other supported HTML inline elements to this list
|
||||
} )[tag]
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.annotationFactory.register( 'textStyle', ve.dm.TextStyleAnnotation );
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.extendClass( ve.dm.TextStyleAnnotation, ve.dm.Annotation );
|
|
@ -29,6 +29,9 @@ ve.dm.AlienBlock.rules = {
|
|||
'parentNodeTypes': null
|
||||
};
|
||||
|
||||
// This is a special node, no converter registration is required
|
||||
ve.dm.AlienBlock.converters = null;
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'alienBlock', ve.dm.AlienBlock );
|
||||
|
|
|
@ -29,6 +29,9 @@ ve.dm.AlienInline.rules = {
|
|||
'parentNodeTypes': null
|
||||
};
|
||||
|
||||
// This is a special node, no converter registration is required
|
||||
ve.dm.AlienInline.converters = null;
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'alienInline', ve.dm.AlienInline );
|
||||
|
|
|
@ -29,6 +29,23 @@ ve.dm.DefinitionListItemNode.rules = {
|
|||
'parentNodeTypes': ['definitionList']
|
||||
};
|
||||
|
||||
/**
|
||||
* Node converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.DefinitionListItemNode.converters = {
|
||||
'tags': 'dl',
|
||||
'toHtml': function( type, element ) {
|
||||
return ve.dm.createHtmlElement( 'dl' );
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return { 'type': 'definitionList' };
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'definitionListItem', ve.dm.DefinitionListItemNode );
|
||||
|
|
|
@ -29,6 +29,29 @@ ve.dm.DefinitionListNode.rules = {
|
|||
'parentNodeTypes': null
|
||||
};
|
||||
|
||||
/**
|
||||
* Node converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.DefinitionListNode.converters = {
|
||||
'tags': ['dt', 'dd'],
|
||||
'toHtml': function( type, element ) {
|
||||
return element.attributes && ( {
|
||||
'term': ve.dm.createHtmlElement( 'dt' ),
|
||||
'definition': ve.dm.createHtmlElement( 'dd' )
|
||||
} )[element.attributes['style']];
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return ( {
|
||||
'dt': { 'type': 'definitionList', 'attributes': { 'style': 'term' } },
|
||||
'dd': { 'type': 'definitionList', 'attributes': { 'style': 'definition' } }
|
||||
} )[tag];
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'definitionList', ve.dm.DefinitionListNode );
|
||||
|
|
|
@ -29,10 +29,15 @@ ve.dm.DocumentNode.rules = {
|
|||
'parentNodeTypes': []
|
||||
};
|
||||
|
||||
// This is a special node, no converter registration is required
|
||||
ve.dm.DocumentNode.converters = null;
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'document', ve.dm.DocumentNode );
|
||||
|
||||
// This is a special node, no converter registration is required
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.extendClass( ve.dm.DocumentNode, ve.dm.BranchNode );
|
||||
|
|
|
@ -29,6 +29,37 @@ ve.dm.HeadingNode.rules = {
|
|||
'parentNodeTypes': null
|
||||
};
|
||||
|
||||
/**
|
||||
* Node converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.HeadingNode.converters = {
|
||||
'tags': ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
||||
'toHtml': function( type, element ) {
|
||||
return element.attributes && ( {
|
||||
1: ve.dm.createHtmlElement( 'h1' ),
|
||||
2: ve.dm.createHtmlElement( 'h2' ),
|
||||
3: ve.dm.createHtmlElement( 'h3' ),
|
||||
4: ve.dm.createHtmlElement( 'h4' ),
|
||||
5: ve.dm.createHtmlElement( 'h5' ),
|
||||
6: ve.dm.createHtmlElement( 'h6' )
|
||||
} )[element.attributes['level']];
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return ( {
|
||||
'h1': { 'type': 'heading', 'attributes': { 'level': 1 } },
|
||||
'h2': { 'type': 'heading', 'attributes': { 'level': 2 } },
|
||||
'h3': { 'type': 'heading', 'attributes': { 'level': 3 } },
|
||||
'h4': { 'type': 'heading', 'attributes': { 'level': 4 } },
|
||||
'h5': { 'type': 'heading', 'attributes': { 'level': 5 } },
|
||||
'h6': { 'type': 'heading', 'attributes': { 'level': 6 } }
|
||||
} )[tag];
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'heading', ve.dm.HeadingNode );
|
||||
|
|
|
@ -29,6 +29,23 @@ ve.dm.ImageNode.rules = {
|
|||
'parentNodeTypes': null
|
||||
};
|
||||
|
||||
/**
|
||||
* Node converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.ImageNode.converters = {
|
||||
'tags': 'img',
|
||||
'toHtml': function( type, element ) {
|
||||
return ve.dm.createHtmlElement( 'img' );
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return { 'type': 'image' };
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'image', ve.dm.ImageNode );
|
||||
|
|
|
@ -29,6 +29,23 @@ ve.dm.ListItemNode.rules = {
|
|||
'parentNodeTypes': ['list']
|
||||
};
|
||||
|
||||
/**
|
||||
* Node converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.ListItemNode.converters = {
|
||||
'tags': 'li',
|
||||
'toHtml': function( type, element ) {
|
||||
return ve.dm.createHtmlElement( 'li' );
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return { 'type': 'listItem' };
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'listItem', ve.dm.ListItemNode );
|
||||
|
|
|
@ -29,6 +29,29 @@ ve.dm.ListNode.rules = {
|
|||
'parentNodeTypes': null
|
||||
};
|
||||
|
||||
/**
|
||||
* Node converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.ListNode.converters = {
|
||||
'tags': ['ul', 'ol'],
|
||||
'toHtml': function( type, element ) {
|
||||
return element.attributes && ( {
|
||||
'bullet': ve.dm.createHtmlElement( 'ul' ),
|
||||
'number': ve.dm.createHtmlElement( 'ol' )
|
||||
} )[element.attributes['style']];
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return ( {
|
||||
'ul': { 'type': 'list', 'attributes': { 'style': 'bullet' } },
|
||||
'ol': { 'type': 'list', 'attributes': { 'style': 'number' } }
|
||||
} )[tag];
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'list', ve.dm.ListNode );
|
||||
|
|
|
@ -29,6 +29,23 @@ ve.dm.ParagraphNode.rules = {
|
|||
'parentNodeTypes': null
|
||||
};
|
||||
|
||||
/**
|
||||
* Node converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.ParagraphNode.converters = {
|
||||
'tags': 'p',
|
||||
'toHtml': function( type, element ) {
|
||||
return ve.dm.createHtmlElement( 'p' );
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return { 'type': 'paragraph' };
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'paragraph', ve.dm.ParagraphNode );
|
||||
|
|
|
@ -29,6 +29,23 @@ ve.dm.PreformattedNode.rules = {
|
|||
'parentNodeTypes': null
|
||||
};
|
||||
|
||||
/**
|
||||
* Node converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.PreformattedNode.converters = {
|
||||
'tags': 'pre',
|
||||
'toHtml': function( type, element ) {
|
||||
return ve.dm.createHtmlElement( 'pre' );
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return { 'type': 'preformatted' };
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'preformatted', ve.dm.PreformattedNode );
|
||||
|
|
|
@ -29,6 +29,23 @@ ve.dm.TableCellNode.rules = {
|
|||
'parentNodeTypes': ['tableRow']
|
||||
};
|
||||
|
||||
/**
|
||||
* Node converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.TableCellNode.converters = {
|
||||
'tags': 'td',
|
||||
'toHtml': function( type, element ) {
|
||||
return ve.dm.createHtmlElement( 'td' );
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return { 'type': 'tableCell' };
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'tableCell', ve.dm.TableCellNode );
|
||||
|
|
|
@ -29,6 +29,23 @@ ve.dm.TableNode.rules = {
|
|||
'parentNodeTypes': null
|
||||
};
|
||||
|
||||
/**
|
||||
* Node converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.TableNode.converters = {
|
||||
'tags': 'table',
|
||||
'toHtml': function( type, element ) {
|
||||
return ve.dm.createHtmlElement( 'table' );
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return { 'type': 'table' };
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'table', ve.dm.TableNode );
|
||||
|
|
|
@ -29,6 +29,23 @@ ve.dm.TableRowNode.rules = {
|
|||
'parentNodeTypes': ['table']
|
||||
};
|
||||
|
||||
/**
|
||||
* Node converters.
|
||||
*
|
||||
* @see {ve.dm.Converter}
|
||||
* @static
|
||||
* @member
|
||||
*/
|
||||
ve.dm.TableRowNode.converters = {
|
||||
'tags': 'tr',
|
||||
'toHtml': function( type, element ) {
|
||||
return ve.dm.createHtmlElement( 'tr' );
|
||||
},
|
||||
'toData': function( tag, element ) {
|
||||
return { 'type': 'tableRow' };
|
||||
}
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'tableRow', ve.dm.TableRowNode );
|
||||
|
|
|
@ -28,6 +28,9 @@ ve.dm.TextNode.rules = {
|
|||
'parentNodeTypes': null
|
||||
};
|
||||
|
||||
// This is a special node, no converter registration is required
|
||||
ve.dm.TextNode.converters = null;
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.dm.nodeFactory.register( 'text', ve.dm.TextNode );
|
||||
|
|
8
modules/ve2/dm/ve.dm.Annotation.js
Normal file
8
modules/ve2/dm/ve.dm.Annotation.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* Generic DataModel annotation.
|
||||
*
|
||||
* @class
|
||||
* @constructor
|
||||
*/
|
||||
ve.dm.Annotation = function() {
|
||||
};
|
19
modules/ve2/dm/ve.dm.AnnotationFactory.js
Normal file
19
modules/ve2/dm/ve.dm.AnnotationFactory.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* DataModel annotation factory.
|
||||
*
|
||||
* @class
|
||||
* @extends {ve.Factory}
|
||||
* @constructor
|
||||
*/
|
||||
ve.dm.AnnotationFactory = function() {
|
||||
// Inheritance
|
||||
ve.Factory.call( this );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.extendClass( ve.dm.AnnotationFactory, ve.Factory );
|
||||
|
||||
/* Initialization */
|
||||
|
||||
ve.dm.annotationFactory = new ve.dm.AnnotationFactory();
|
83
modules/ve2/dm/ve.dm.Converter.js
Normal file
83
modules/ve2/dm/ve.dm.Converter.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* Converter between HTML DOM and VisualEditor linear data.
|
||||
*
|
||||
* @class
|
||||
* @constructor
|
||||
* @param {Object} options Conversion options
|
||||
*/
|
||||
ve.dm.Converter = function( nodeFactory, annotationFactory ) {
|
||||
// Properties
|
||||
this.nodeFactory = nodeFactory;
|
||||
this.annotationFactory = annotationFactory;
|
||||
this.elements = { 'toHtml': {}, 'toData': {}, 'types': {} };
|
||||
this.annotations = { 'toHtml': {}, 'toData': {} };
|
||||
|
||||
// Events
|
||||
this.nodeFactory.addListenerMethod( this, 'register', 'onNodeRegister' );
|
||||
this.annotationFactory.addListenerMethod( this, 'register', 'onAnnotationRegister' );
|
||||
};
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Responds to register events from the node factory.
|
||||
*
|
||||
* If a node is special; such as document, alienInline, alienBlock and text; it's converters data
|
||||
* should be set to null, as to distinguish it from a new node type that someone has simply
|
||||
* forgotten to implement converters for.
|
||||
*
|
||||
* @method
|
||||
* @param {String} type Node type
|
||||
* @param {Function} constructor Node constructor
|
||||
* @throws 'Missing conversion data in node implementation of {type}'
|
||||
*/
|
||||
ve.dm.Converter.prototype.onNodeRegister = function( type, constructor ) {
|
||||
if ( constructor.converters === undefined ) {
|
||||
throw 'Missing conversion data in node implementation of ' + type;
|
||||
} else if ( constructor.converters !== null ) {
|
||||
var tags = constructor.converters.tags,
|
||||
toHtml = constructor.converters.toHtml,
|
||||
toData = constructor.converters.toData;
|
||||
// Convert tags to an array if needed
|
||||
if ( !ve.isArray( tags ) ) {
|
||||
tags = [tags];
|
||||
}
|
||||
// Registration
|
||||
this.elements.toHtml[type] = toHtml;
|
||||
for ( var i = 0; i < tags.length; i++ ) {
|
||||
this.elements.toData[tags[i]] = toData;
|
||||
this.elements.types[tags[i]] = type;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Responds to register events from the annotation factory.
|
||||
*
|
||||
* @method
|
||||
* @param {String} type Base annotation type
|
||||
* @param {Function} constructor Annotation constructor
|
||||
* @throws 'Missing conversion data in annotation implementation of {type}'
|
||||
*/
|
||||
ve.dm.Converter.prototype.onAnnotationRegister = function( type, constructor ) {
|
||||
if ( constructor.converters === undefined ) {
|
||||
throw 'Missing conversion data in annotation implementation of ' + type;
|
||||
} else if ( constructor.converters !== null ) {
|
||||
var tags = constructor.converters.tags,
|
||||
toHtml = constructor.converters.toHtml,
|
||||
toData = constructor.converters.toData;
|
||||
// Convert tags to an array if needed
|
||||
if ( !ve.isArray( tags ) ) {
|
||||
tags = [tags];
|
||||
}
|
||||
// Registration
|
||||
this.annotations.toHtml[type] = toHtml;
|
||||
for ( var i = 0; i < tags.length; i++ ) {
|
||||
this.annotations.toData[tags[i]] = toData;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Initialization */
|
||||
|
||||
ve.dm.converter = new ve.dm.Converter( ve.dm.nodeFactory, ve.dm.annotationFactory );
|
|
@ -2,12 +2,12 @@
|
|||
* DataModel node factory.
|
||||
*
|
||||
* @class
|
||||
* @extends {ve.NodeFactory}
|
||||
* @extends {ve.Factory}
|
||||
* @constructor
|
||||
*/
|
||||
ve.dm.NodeFactory = function() {
|
||||
// Inheritance
|
||||
ve.NodeFactory.call( this );
|
||||
ve.Factory.call( this );
|
||||
};
|
||||
|
||||
/* Methods */
|
||||
|
@ -18,7 +18,7 @@ ve.dm.NodeFactory = function() {
|
|||
* @method
|
||||
* @param {String} type Node type
|
||||
* @returns {String[]|null} List of node types allowed as children or null if any type is allowed
|
||||
* @throws 'Unknown node type'
|
||||
* @throws 'Unknown node type: {type}'
|
||||
*/
|
||||
ve.dm.NodeFactory.prototype.getChildNodeTypes = function( type ) {
|
||||
if ( type in this.registry ) {
|
||||
|
@ -33,7 +33,7 @@ ve.dm.NodeFactory.prototype.getChildNodeTypes = function( type ) {
|
|||
* @method
|
||||
* @param {String} type Node type
|
||||
* @returns {String[]|null} List of node types allowed as parents or null if any type is allowed
|
||||
* @throws 'Unknown node type'
|
||||
* @throws 'Unknown node type: {type}'
|
||||
*/
|
||||
ve.dm.NodeFactory.prototype.getParentNodeTypes = function( type ) {
|
||||
if ( type in this.registry ) {
|
||||
|
@ -48,7 +48,7 @@ ve.dm.NodeFactory.prototype.getParentNodeTypes = function( type ) {
|
|||
* @method
|
||||
* @param {String} type Node type
|
||||
* @returns {Boolean} The node can have children
|
||||
* @throws 'Unknown node type'
|
||||
* @throws 'Unknown node type: {type}'
|
||||
*/
|
||||
ve.dm.NodeFactory.prototype.canNodeHaveChildren = function( type ) {
|
||||
if ( type in this.registry ) {
|
||||
|
@ -66,7 +66,7 @@ ve.dm.NodeFactory.prototype.canNodeHaveChildren = function( type ) {
|
|||
* @method
|
||||
* @param {String} type Node type
|
||||
* @returns {Boolean} The node can have grandchildren
|
||||
* @throws 'Unknown node type'
|
||||
* @throws 'Unknown node type: {type}'
|
||||
*/
|
||||
ve.dm.NodeFactory.prototype.canNodeHaveGrandchildren = function( type ) {
|
||||
if ( type in this.registry ) {
|
||||
|
@ -83,7 +83,7 @@ ve.dm.NodeFactory.prototype.canNodeHaveGrandchildren = function( type ) {
|
|||
* @method
|
||||
* @param {String} type Node type
|
||||
* @returns {Boolean} Whether the node has a wrapping element
|
||||
* @throws 'Unknown node type'
|
||||
* @throws 'Unknown node type: {type}'
|
||||
*/
|
||||
ve.dm.NodeFactory.prototype.isNodeWrapped = function( type ) {
|
||||
if ( type in this.registry ) {
|
||||
|
@ -98,7 +98,7 @@ ve.dm.NodeFactory.prototype.isNodeWrapped = function( type ) {
|
|||
* @method
|
||||
* @param {String} type Node type
|
||||
* @returns {Boolean} The node contains content
|
||||
* @throws 'Unknown node type'
|
||||
* @throws 'Unknown node type: {type}'
|
||||
*/
|
||||
ve.dm.NodeFactory.prototype.canNodeContainContent = function( type ) {
|
||||
if ( type in this.registry ) {
|
||||
|
@ -113,7 +113,7 @@ ve.dm.NodeFactory.prototype.canNodeContainContent = function( type ) {
|
|||
* @method
|
||||
* @param {String} type Node type
|
||||
* @returns {Boolean} The node is content
|
||||
* @throws 'Unknown node type'
|
||||
* @throws 'Unknown node type: {type}'
|
||||
*/
|
||||
ve.dm.NodeFactory.prototype.isNodeContent = function( type ) {
|
||||
if ( type in this.registry ) {
|
||||
|
@ -124,7 +124,7 @@ ve.dm.NodeFactory.prototype.isNodeContent = function( type ) {
|
|||
|
||||
/* Inheritance */
|
||||
|
||||
ve.extendClass( ve.dm.NodeFactory, ve.NodeFactory );
|
||||
ve.extendClass( ve.dm.NodeFactory, ve.Factory );
|
||||
|
||||
/* Initialization */
|
||||
|
||||
|
|
|
@ -5,4 +5,16 @@
|
|||
*/
|
||||
ve.dm = {
|
||||
//'nodeFactory': Initialized in ve.dm.NodeFactory.js
|
||||
//'converter': Initialized in ve.dm.Converter.js
|
||||
};
|
||||
|
||||
ve.dm.createHtmlElement = function( type, attributes, doc ) {
|
||||
if ( doc === undefined ) {
|
||||
doc = document;
|
||||
}
|
||||
var element = doc.createElement( type );
|
||||
for ( var key in attributes ) {
|
||||
element.setAttribute( key, attributes[key] );
|
||||
}
|
||||
return element;
|
||||
};
|
||||
|
|
|
@ -1,35 +1,42 @@
|
|||
/**
|
||||
* Generic node factory.
|
||||
* Generic object factory.
|
||||
*
|
||||
* @class
|
||||
* @abstract
|
||||
* @constructor
|
||||
* @extends {ve.EventEmitter}
|
||||
*/
|
||||
ve.NodeFactory = function() {
|
||||
ve.Factory = function() {
|
||||
// Inheritance
|
||||
ve.EventEmitter.call( this );
|
||||
|
||||
// Properties
|
||||
this.registry = [];
|
||||
};
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Register a node type with the factory.
|
||||
* Register a constructor with the factory.
|
||||
*
|
||||
* Arguments will be passed through directly to the constructor.
|
||||
* @see {ve.NodeFactory.prototype.create}
|
||||
* @see {ve.Factory.prototype.create}
|
||||
*
|
||||
* @method
|
||||
* @param {String} type Node type
|
||||
* @param {Function} constructor Node constructor subclassing ve.Node
|
||||
* @param {String} type Object type
|
||||
* @param {Function} constructor Constructor to use when creating object
|
||||
* @throws 'Constructor must be a function, cannot be a string'
|
||||
*/
|
||||
ve.NodeFactory.prototype.register = function( type, constructor ) {
|
||||
ve.Factory.prototype.register = function( type, constructor ) {
|
||||
if ( typeof constructor !== 'function' ) {
|
||||
throw 'Constructor must be a function, cannot be a ' + typeof constructor;
|
||||
}
|
||||
this.registry[type] = constructor;
|
||||
this.emit( 'register', type, constructor );
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a node based on a type.
|
||||
* Create an object based on a type.
|
||||
*
|
||||
* Type is used to look up the constructor to use, while all additional arguments are passed to the
|
||||
* constructor directly, so leaving one out will pass an undefined to the constructor.
|
||||
|
@ -40,25 +47,29 @@ ve.NodeFactory.prototype.register = function( type, constructor ) {
|
|||
* in adding more.
|
||||
*
|
||||
* @method
|
||||
* @param {String} type Node type
|
||||
* @param {String} type Object type
|
||||
* @param {Mixed} [...] Up to 2 additional arguments to pass through to the constructor
|
||||
* @returns {ve.Node} The new node object
|
||||
* @throws 'Unknown node type'
|
||||
* @returns {Object} The new object
|
||||
* @throws 'Unknown object type'
|
||||
*/
|
||||
ve.NodeFactory.prototype.create = function( type, a, b ) {
|
||||
ve.Factory.prototype.create = function( type, a, b ) {
|
||||
if ( type in this.registry ) {
|
||||
return new this.registry[type]( a, b );
|
||||
}
|
||||
throw 'Unknown node type: ' + type;
|
||||
throw 'Unknown object type: ' + type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a constructor for a given type.
|
||||
*
|
||||
* @method
|
||||
* @param {String} type Node type
|
||||
* @param {String} type Object type
|
||||
* @returns {Function|undefined} Constructor for type
|
||||
*/
|
||||
ve.NodeFactory.prototype.lookup = function( type ) {
|
||||
ve.Factory.prototype.lookup = function( type ) {
|
||||
return this.registry[type];
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.extendClass( ve.Factory, ve.EventEmitter );
|
|
@ -16,10 +16,10 @@ ve.ce.NodeFactoryNodeStub.rules = {
|
|||
test( 'canNodeBeSplit', 2, function() {
|
||||
var factory = new ve.ce.NodeFactory();
|
||||
raises( function() {
|
||||
factory.create( 'node-factory-node-stub', 23, { 'bar': 'baz' } );
|
||||
factory.canNodeBeSplit( 'node-factory-node-stub' );
|
||||
},
|
||||
/^Unknown node type: node-factory-node-stub$/,
|
||||
'throws an exception when getting allowed child nodes of a node of an unregistered type'
|
||||
'throws an exception when getting split rules for a node of an unregistered type'
|
||||
);
|
||||
factory.register( 'node-factory-node-stub', ve.ce.NodeFactoryNodeStub );
|
||||
strictEqual(
|
||||
|
|
|
@ -14,6 +14,8 @@ ve.dm.BranchNodeStub.rules = {
|
|||
'childNodeTypes': null
|
||||
};
|
||||
|
||||
ve.dm.BranchNodeStub.converters = null;
|
||||
|
||||
ve.extendClass( ve.dm.BranchNodeStub, ve.dm.BranchNode );
|
||||
|
||||
ve.dm.nodeFactory.register( 'branch-stub', ve.dm.BranchNodeStub );
|
||||
|
|
|
@ -14,6 +14,8 @@ ve.dm.LeafNodeStub.rules = {
|
|||
'childNodeTypes': []
|
||||
};
|
||||
|
||||
ve.dm.LeafNodeStub.converters = null;
|
||||
|
||||
ve.extendClass( ve.dm.LeafNodeStub, ve.dm.LeafNode );
|
||||
|
||||
ve.dm.nodeFactory.register( 'leaf-stub', ve.dm.LeafNodeStub );
|
||||
|
|
|
@ -14,6 +14,8 @@ ve.dm.NodeStub.rules = {
|
|||
'childNodeTypes': []
|
||||
};
|
||||
|
||||
ve.dm.NodeStub.converters = null;
|
||||
|
||||
ve.extendClass( ve.dm.NodeStub, ve.dm.Node );
|
||||
|
||||
ve.dm.nodeFactory.register( 'stub', ve.dm.NodeStub );
|
||||
|
|
|
@ -15,12 +15,16 @@ ve.dm.NodeFactoryNodeStub.rules = {
|
|||
'parentNodeTypes': null
|
||||
};
|
||||
|
||||
ve.dm.NodeFactoryNodeStub.converters = null;
|
||||
|
||||
ve.dm.NodeFactoryNodeStub.converters = null;
|
||||
|
||||
/* Tests */
|
||||
|
||||
test( 'getChildNodeTypes', 2, function() {
|
||||
var factory = new ve.dm.NodeFactory();
|
||||
raises( function() {
|
||||
factory.create( 'node-factory-node-stub', 23, { 'bar': 'baz' } );
|
||||
factory.getChildNodeTypes( 'node-factory-node-stub', 23, { 'bar': 'baz' } );
|
||||
},
|
||||
/^Unknown node type: node-factory-node-stub$/,
|
||||
'throws an exception when getting allowed child nodes of a node of an unregistered type'
|
||||
|
@ -36,7 +40,7 @@ test( 'getChildNodeTypes', 2, function() {
|
|||
test( 'getParentNodeTypes', 2, function() {
|
||||
var factory = new ve.dm.NodeFactory();
|
||||
raises( function() {
|
||||
factory.create( 'node-factory-node-stub', 23, { 'bar': 'baz' } );
|
||||
factory.getParentNodeTypes( 'node-factory-node-stub', 23, { 'bar': 'baz' } );
|
||||
},
|
||||
/^Unknown node type: node-factory-node-stub$/,
|
||||
'throws an exception when getting allowed parent nodes of a node of an unregistered type'
|
||||
|
@ -52,7 +56,7 @@ test( 'getParentNodeTypes', 2, function() {
|
|||
test( 'canNodeHaveChildren', 2, function() {
|
||||
var factory = new ve.dm.NodeFactory();
|
||||
raises( function() {
|
||||
factory.create( 'node-factory-node-stub', 23, { 'bar': 'baz' } );
|
||||
factory.canNodeHaveChildren( 'node-factory-node-stub', 23, { 'bar': 'baz' } );
|
||||
},
|
||||
/^Unknown node type: node-factory-node-stub$/,
|
||||
'throws an exception when checking if a node of an unregistered type can have children'
|
||||
|
@ -68,7 +72,7 @@ test( 'canNodeHaveChildren', 2, function() {
|
|||
test( 'canNodeHaveGrandchildren', 2, function() {
|
||||
var factory = new ve.dm.NodeFactory();
|
||||
raises( function() {
|
||||
factory.create( 'node-factory-node-stub', 23, { 'bar': 'baz' } );
|
||||
factory.canNodeHaveGrandchildren( 'node-factory-node-stub', 23, { 'bar': 'baz' } );
|
||||
},
|
||||
/^Unknown node type: node-factory-node-stub$/,
|
||||
'throws an exception when checking if a node of an unregistered type can have grandchildren'
|
||||
|
|
|
@ -23,26 +23,32 @@
|
|||
<script src="../../modules/ve2/ve.Node.js"></script>
|
||||
<script src="../../modules/ve2/ve.BranchNode.js"></script>
|
||||
<script src="../../modules/ve2/ve.LeafNode.js"></script>
|
||||
<script src="../../modules/ve2/ve.NodeFactory.js"></script>
|
||||
<script src="../../modules/ve2/ve.Factory.js"></script>
|
||||
<script src="../../modules/ve2/ve.Document.js"></script>
|
||||
<script src="../../modules/ve2/ve.Position.js"></script>
|
||||
<script src="../../modules/ve2/ve.Range.js"></script>
|
||||
<script src="../../modules/ve2/ve.Surface.js"></script>
|
||||
|
||||
<!-- VisualEditor DataModel -->
|
||||
|
||||
<script src="../../modules/ve2/dm/ve.dm.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.NodeFactory.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.AnnotationFactory.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Node.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.BranchNode.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.LeafNode.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Annotation.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Document.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.DocumentSynchronizer.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Transaction.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.TransactionProcessor.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Surface.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.Converter.js"></script>
|
||||
<script src="../../modules/ve2/dm/ve.dm.HTMLConverter.js"></script>
|
||||
|
||||
<!-- VisualEditor DataModel Annotations -->
|
||||
<script src="../../modules/ve2/dm/annotations/ve.dm.LinkAnnotation.js"></script>
|
||||
<script src="../../modules/ve2/dm/annotations/ve.dm.TextStyleAnnotation.js"></script>
|
||||
|
||||
<!-- VisualEditor DataModel Nodes -->
|
||||
<script src="../../modules/ve2/dm/nodes/ve.dm.AlienInlineNode.js"></script>
|
||||
<script src="../../modules/ve2/dm/nodes/ve.dm.AlienBlockNode.js"></script>
|
||||
|
@ -91,7 +97,7 @@
|
|||
<script src="ve.Node.test.js"></script>
|
||||
<script src="ve.BranchNode.test.js"></script>
|
||||
<script src="ve.LeafNode.test.js"></script>
|
||||
<script src="ve.NodeFactory.test.js"></script>
|
||||
<script src="ve.Factory.test.js"></script>
|
||||
|
||||
<!-- VisualEditor DataModel Tests -->
|
||||
<script src="dm/ve.dm.example.js"></script>
|
||||
|
|
44
tests/ve2/ve.Factory.test.js
Normal file
44
tests/ve2/ve.Factory.test.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
module( 've.Factory' );
|
||||
|
||||
/* Stubs */
|
||||
|
||||
ve.FactoryObjectStub = function( a, b ) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
};
|
||||
|
||||
/* Tests */
|
||||
|
||||
test( 'register', 1, function() {
|
||||
var factory = new ve.Factory();
|
||||
raises(
|
||||
function() {
|
||||
factory.register( 'factory-object-stub', 'not-a-function' );
|
||||
},
|
||||
/^Constructor must be a function, cannot be a string$/,
|
||||
'throws an exception when trying to register a non-function value as a constructor'
|
||||
);
|
||||
} );
|
||||
|
||||
test( 'create', 2, function() {
|
||||
var factory = new ve.Factory();
|
||||
raises(
|
||||
function() {
|
||||
factory.create( 'factory-object-stub', 23, { 'bar': 'baz' } );
|
||||
},
|
||||
/^Unknown object type: factory-object-stub$/,
|
||||
'throws an exception when trying to create a object of an unregistered type'
|
||||
);
|
||||
factory.register( 'factory-object-stub', ve.FactoryObjectStub );
|
||||
deepEqual(
|
||||
factory.create( 'factory-object-stub', 16, { 'baz': 'quux' } ),
|
||||
new ve.FactoryObjectStub( 16, { 'baz': 'quux' } ),
|
||||
'creates objects of a registered type and passes through arguments'
|
||||
);
|
||||
} );
|
||||
|
||||
test( 'lookup', 1, function() {
|
||||
var factory = new ve.Factory();
|
||||
factory.register( 'factory-object-stub', ve.FactoryObjectStub );
|
||||
strictEqual( factory.lookup( 'factory-object-stub' ), ve.FactoryObjectStub );
|
||||
} );
|
|
@ -1,42 +0,0 @@
|
|||
module( 've.NodeFactory' );
|
||||
|
||||
/* Stubs */
|
||||
|
||||
ve.NodeFactoryNodeStub = function( a, b ) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
};
|
||||
|
||||
/* Tests */
|
||||
|
||||
test( 'register', 1, function() {
|
||||
var factory = new ve.NodeFactory();
|
||||
raises( function() {
|
||||
factory.register( 'node-factory-node-stub', 'not-a-function' );
|
||||
},
|
||||
/^Constructor must be a function, cannot be a string$/,
|
||||
'throws an exception when trying to register a non-function value as a constructor'
|
||||
);
|
||||
} );
|
||||
|
||||
test( 'create', 2, function() {
|
||||
var factory = new ve.NodeFactory();
|
||||
raises( function() {
|
||||
factory.create( 'node-factory-node-stub', 23, { 'bar': 'baz' } );
|
||||
},
|
||||
/^Unknown node type: node-factory-node-stub$/,
|
||||
'throws an exception when trying to create a node of an unregistered type'
|
||||
);
|
||||
factory.register( 'node-factory-node-stub', ve.NodeFactoryNodeStub );
|
||||
deepEqual(
|
||||
factory.create( 'node-factory-node-stub', 16, { 'baz': 'quux' } ),
|
||||
new ve.NodeFactoryNodeStub( 16, { 'baz': 'quux' } ),
|
||||
'creates nodes of a registered type and passes through arguments'
|
||||
);
|
||||
} );
|
||||
|
||||
test( 'lookup', 1, function() {
|
||||
var factory = new ve.NodeFactory();
|
||||
factory.register( 'node-factory-node-stub', ve.NodeFactoryNodeStub );
|
||||
strictEqual( factory.lookup( 'node-factory-node-stub' ), ve.NodeFactoryNodeStub );
|
||||
} );
|
Loading…
Reference in a new issue