1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
/*!
* VisualEditor DataModel MWWikitextSurfaceFragment class.
*
* @copyright See AUTHORS.txt
*/
/**
* DataModel MWWikitextSurfaceFragment.
*
* @class
* @extends ve.dm.SourceSurfaceFragment
*
* @constructor
* @param {ve.dm.Document} doc
*/
ve.dm.MWWikitextSurfaceFragment = function VeDmMwWikitextSurfaceFragment() {
// Parent constructors
ve.dm.MWWikitextSurfaceFragment.super.apply( this, arguments );
};
/* Inheritance */
OO.inheritClass( ve.dm.MWWikitextSurfaceFragment, ve.dm.SourceSurfaceFragment );
/* Methods */
/**
* @inheritdoc
*/
ve.dm.MWWikitextSurfaceFragment.prototype.hasMatchingAncestor = function ( type, attributes ) {
const nodes = this.getSelectedLeafNodes();
let all = !!nodes.length;
for ( let i = 0, len = nodes.length; i < len; i++ ) {
const text = this.document.data.getText( false, nodes[ i ].getRange() );
// TODO: Use a registry to do this matching
switch ( type ) {
case 'paragraph':
// Anything but what's matched below
all = !/^([ =]|<blockquote>)/.test( text );
break;
case 'mwPreformatted':
all = text.slice( 0, 1 ) === ' ';
break;
case 'blockquote':
all = text.slice( 0, 12 ) === '<blockquote>';
break;
case 'mwHeading':
all = new RegExp( '^={' + attributes.level + '}[^=]' ).test( text ) &&
new RegExp( '[^=]={' + attributes.level + '}$' ).test( text );
break;
default:
all = false;
break;
}
if ( !all ) {
break;
}
}
return all;
};
/**
* Wrap a text selection.
*
* If the selection is already identically wrapped it will be unwrapped.
*
* @param {string} before Text to go before selection
* @param {string} after Text to go after selection
* @param {Function|string} placeholder Placeholder text to insert at an empty selection
* @param {boolean} [forceWrap=false] Force wrapping, even if matching wrapping exists
* @return {ve.dm.MWWikitextSurfaceFragment}
* @chainable
*/
ve.dm.MWWikitextSurfaceFragment.prototype.wrapText = function ( before, after, placeholder, forceWrap ) {
placeholder = OO.ui.resolveMsg( placeholder );
function unwrap( fragment ) {
const text = fragment.getText();
if (
( !before || text.slice( 0, before.length ) === before ) &&
( !after || text.slice( -after.length ) === after )
) {
fragment.unwrapText( before.length, after.length );
// Just the placeholder left, nothing meaningful was selected so just remove it
if ( fragment.getText() === placeholder ) {
fragment.removeContent();
}
return true;
}
return false;
}
if ( !forceWrap && ( unwrap( this ) || unwrap( this.adjustLinearSelection( -before.length, after.length ) ) ) ) {
return this;
} else {
if ( placeholder && this.getSelection().isCollapsed() ) {
this.insertContent( placeholder );
}
const wrappedFragment = this.clone();
const wasExcludingInsertions = this.willExcludeInsertions();
this.setExcludeInsertions( true );
this.collapseToStart().insertContent( before );
this.collapseToEnd().insertContent( after );
this.setExcludeInsertions( wasExcludingInsertions );
return wrappedFragment;
}
};
/**
* Unwrap a fixed amount of text
*
* @param {number} before Amount of text to remove from start
* @param {number} after Amount of text to remove from end
* @return {ve.dm.MWWikitextSurfaceFragment}
* @chainable
*/
ve.dm.MWWikitextSurfaceFragment.prototype.unwrapText = function ( before, after ) {
this.collapseToStart().adjustLinearSelection( 0, before ).removeContent();
this.collapseToEnd().adjustLinearSelection( -after, 0 ).removeContent();
return this;
};
/**
* @inheritdoc
*/
ve.dm.MWWikitextSurfaceFragment.prototype.convertToSource = function ( doc ) {
if ( !doc.data.hasContent() ) {
return ve.createDeferred().resolve( '' ).promise();
}
const wikitextPromise = ve.init.target.getWikitextFragment( doc, false );
// TODO: Emit an event to trigger the progress bar
const progressPromise = ve.init.target.getSurface().createProgress(
wikitextPromise, ve.msg( 'visualeditor-generating-wikitext-progress' )
).then( ( progressBar, cancelPromise ) => {
cancelPromise.fail( () => {
wikitextPromise.abort();
} );
} );
return ve.promiseAll( [ wikitextPromise, progressPromise ] ).then( ( wikitext ) => {
const deferred = ve.createDeferred();
setTimeout( () => {
if ( wikitext !== undefined ) {
deferred.resolve( wikitext );
} else {
deferred.reject();
}
}, ve.init.target.getSurface().dialogs.getTeardownDelay() );
return deferred.promise();
} );
};
/**
* @inheritdoc
*/
ve.dm.MWWikitextSurfaceFragment.prototype.convertFromSource = function ( source ) {
let parsePromise;
if ( !source ) {
parsePromise = ve.createDeferred().resolve(
ve.dm.Document.static.newBlankDocument()
).promise();
} else {
parsePromise = ve.init.target.parseWikitextFragment( source, false, this.getDocument() ).then( ( response ) => ve.dm.converter.getModelFromDom(
ve.createDocumentFromHtml( response.visualeditor.content )
) );
}
// TODO: Show progress bar without breaking WindowAction
/*
ve.init.target.getSurface().createProgress(
parsePromise, ve.msg( 'visualeditor-generating-wikitext-progress' )
).done( ( progressBar, cancelPromise ) => {
cancelPromise.fail( () => {
parsePromise.abort();
} );
} );
*/
return parsePromise;
};
|