mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/WikiEditor
synced 2024-12-24 13:54:22 +00:00
49eb154f2c
* Also fixed IE bug, where "delete window.myThing;" fails but "delete myThing;" succeeds.
632 lines
24 KiB
JavaScript
632 lines
24 KiB
JavaScript
/**
|
|
* This plugin provides a way to build a wiki-text editing user interface around a textarea.
|
|
*
|
|
* @example To intialize without any modules:
|
|
* $j( 'div#edittoolbar' ).wikiEditor();
|
|
*
|
|
* @example To initialize with one or more modules, or to add modules after it's already been initialized:
|
|
* $j( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'toolbar', { ... config ... } );
|
|
*
|
|
*/
|
|
( function( $ ) {
|
|
|
|
/**
|
|
* Global static object for wikiEditor that provides generally useful functionality to all modules and contexts.
|
|
*/
|
|
$.wikiEditor = {
|
|
/**
|
|
* For each module that is loaded, static code shared by all instances is loaded into this object organized by
|
|
* module name. The existance of a module in this object only indicates the module is available. To check if a
|
|
* module is in use by a specific context check the context.modules object.
|
|
*/
|
|
'modules': {},
|
|
/**
|
|
* A context can be extended, such as adding iframe support, on a per-wikiEditor instance basis.
|
|
*/
|
|
'extensions': {},
|
|
/**
|
|
* In some cases like with the iframe's HTML file, it's convienent to have a lookup table of all instances of the
|
|
* WikiEditor. Each context contains an instance field which contains a key that corrosponds to a reference to the
|
|
* textarea which the WikiEditor was build around. This way, by passing a simple integer you can provide a way back
|
|
* to a specific context.
|
|
*/
|
|
'instances': [],
|
|
/**
|
|
* For each browser name, an array of conditions that must be met are supplied in [operaton, value]-form where
|
|
* operation is a string containing a JavaScript compatible binary operator and value is either a number to be
|
|
* compared with $.browser.versionNumber or a string to be compared with $.browser.version. If a browser is not
|
|
* specifically mentioned, we just assume things will work.
|
|
*/
|
|
'browsers': {
|
|
// Left-to-right languages
|
|
'ltr': {
|
|
// The toolbar layout is broken in IE6
|
|
'msie': [['>=', 7]],
|
|
// Layout issues in FF < 2
|
|
'firefox': [['>=', 2]],
|
|
// Text selection bugs galore - this may be a different situation with the new iframe-based solution
|
|
'opera': [['>=', 9.6]],
|
|
// jQuery minimums
|
|
'safari': [['>=', 3]],
|
|
'chrome': [['>=', 3]],
|
|
'netscape': [['>=', 9]],
|
|
'blackberry': false,
|
|
'ipod': false,
|
|
'iphone': false
|
|
},
|
|
// Right-to-left languages
|
|
'rtl': {
|
|
// The toolbar layout is broken in IE 7 in RTL mode, and IE6 in any mode
|
|
'msie': [['>=', 8]],
|
|
// Layout issues in FF < 2
|
|
'firefox': [['>=', 2]],
|
|
// Text selection bugs galore - this may be a different situation with the new iframe-based solution
|
|
'opera': [['>=', 9.6]],
|
|
// jQuery minimums
|
|
'safari': [['>=', 3]],
|
|
'chrome': [['>=', 3]],
|
|
'netscape': [['>=', 9]],
|
|
'blackberry': false,
|
|
'ipod': false,
|
|
'iphone': false
|
|
}
|
|
},
|
|
/**
|
|
* Path to images - this is a bit messy, and it would need to change if this code (and images) gets moved into the
|
|
* core - or anywhere for that matter...
|
|
*/
|
|
'imgPath' : wgScriptPath + '/extensions/WikiEditor/modules/images/',
|
|
/**
|
|
* Checks the current browser against the browsers object to determine if the browser has been black-listed or not.
|
|
* Because these rules are often very complex, the object contains configurable operators and can check against
|
|
* either the browser version number or string. This process also involves checking if the current browser is amung
|
|
* those which we have configured as compatible or not. If the browser was not configured as comptible we just go on
|
|
* assuming things will work - the argument here is to prevent the need to update the code when a new browser comes
|
|
* to market. The assumption here is that any new browser will be built on an existing engine or be otherwise so
|
|
* similar to another existing browser that things actually do work as expected. The merrits of this argument, which
|
|
* is essentially to blacklist rather than whitelist are debateable, but at this point we've decided it's the more
|
|
* "open-web" way to go.
|
|
* @param module Module object, defaults to $.wikiEditor
|
|
*/
|
|
'isSupported': function( module ) {
|
|
// Fallback to the wikiEditor browser map if no special map is provided in the module
|
|
var mod = module && 'browsers' in module ? module : $.wikiEditor;
|
|
// Check for and make use of cached value and early opportunities to bail
|
|
if ( typeof mod.supported !== 'undefined' ) {
|
|
// Cache hit
|
|
return mod.supported;
|
|
}
|
|
// Run a browser support test and then cache and return the result
|
|
return mod.supported = $.client.test( mod.browsers );
|
|
},
|
|
/**
|
|
* Checks if a module has a specific requirement
|
|
* @param module Module object
|
|
* @param requirement String identifying requirement
|
|
*/
|
|
'isRequired': function( module, requirement ) {
|
|
if ( typeof module['req'] !== 'undefined' ) {
|
|
for ( req in module['req'] ) {
|
|
if ( module['req'][req] == requirement ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
/**
|
|
* Provides a way to extract messages from objects. Wraps the mediaWiki.msg.get() function, which
|
|
* may eventually become a wrapper for some kind of core MW functionality.
|
|
*
|
|
* @param object Object to extract messages from
|
|
* @param property String of name of property which contains the message. This should be the base name of the
|
|
* property, which means that in the case of the object { this: 'that', fooMsg: 'bar' }, passing property as 'this'
|
|
* would return the raw text 'that', while passing property as 'foo' would return the internationalized message
|
|
* with the key 'bar'.
|
|
*/
|
|
'autoMsg': function( object, property ) {
|
|
// Accept array of possible properties, of which the first one found will be used
|
|
if ( typeof property == 'object' ) {
|
|
for ( var i in property ) {
|
|
if ( property[i] in object || property[i] + 'Msg' in object ) {
|
|
property = property[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ( property in object ) {
|
|
return object[property];
|
|
} else if ( property + 'Msg' in object ) {
|
|
var p = object[property + 'Msg'];
|
|
if ( p instanceof Array && p.length >= 2 ) {
|
|
return mediaWiki.msg.get(
|
|
object[property + 'Msg' ][0],
|
|
{ 'parameters': [object[property + 'Msg' ][1]] }
|
|
);
|
|
} else {
|
|
return mediaWiki.msg.get( p );
|
|
}
|
|
} else {
|
|
return '';
|
|
}
|
|
},
|
|
/**
|
|
* Provides a way to extract a property of an object in a certain language, falling back on the property keyed as
|
|
* 'default'. If such key doesn't exist, the object itself is considered the actual value, which should ideally
|
|
* be the case so that you may use a string or object of any number of strings keyed by language with a default.
|
|
*
|
|
* @param object Object to extract property from
|
|
* @param lang Language code, defaults to wgUserLanguage
|
|
*/
|
|
'autoLang': function( object, lang ) {
|
|
return object[lang || wgUserLanguage] || object['default'] || object;
|
|
},
|
|
/**
|
|
* Provides a way to extract the path of an icon in a certain language, automatically appending a version number for
|
|
* caching purposes and prepending an image path when icon paths are relative.
|
|
*
|
|
* @param icon Icon object from e.g. toolbar config
|
|
* @param path Default icon path, defaults to $.wikiEditor.imgPath
|
|
* @param lang Language code, defaults to wgUserLanguage
|
|
*/
|
|
'autoIcon': function( icon, path, lang ) {
|
|
var src = $.wikiEditor.autoLang( icon, lang );
|
|
path = path || $.wikiEditor.imgPath;
|
|
// Prepend path if src is not absolute
|
|
if ( src.substr( 0, 7 ) != 'http://' && src.substr( 0, 8 ) != 'https://' && src[0] != '/' ) {
|
|
src = path + src;
|
|
}
|
|
return src + '?' + mediaWiki.loader.version( 'jquery.wikiEditor' );
|
|
},
|
|
/**
|
|
* Get the sprite offset for a language if available, icon for a language if available, or the default offset or icon,
|
|
* in that order of preference.
|
|
* @param icon Icon object, see autoIcon()
|
|
* @param offset Offset object
|
|
* @param path Icon path, see autoIcon()
|
|
* @param lang Language code, defaults to wgUserLanguage
|
|
*/
|
|
'autoIconOrOffset': function( icon, offset, path, lang ) {
|
|
lang = lang || wgUserLanguage;
|
|
if ( typeof offset == 'object' && lang in offset ) {
|
|
return offset[lang];
|
|
} else if ( typeof icon == 'object' && lang in icon ) {
|
|
return $.wikiEditor.autoIcon( icon, undefined, lang );
|
|
} else {
|
|
return $.wikiEditor.autoLang( offset, lang );
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* jQuery plugin that provides a way to initialize a wikiEditor instance on a textarea.
|
|
*/
|
|
$.fn.wikiEditor = function() {
|
|
|
|
// Skip any further work when running in browsers that are unsupported
|
|
if ( !$j.wikiEditor.isSupported() ) {
|
|
return $(this);
|
|
}
|
|
|
|
/* Initialization */
|
|
|
|
// The wikiEditor context is stored in the element's data, so when this function gets called again we can pick up right
|
|
// where we left off
|
|
var context = $(this).data( 'wikiEditor-context' );
|
|
// On first call, we need to set things up, but on all following calls we can skip right to the API handling
|
|
if ( !context || typeof context == 'undefined' ) {
|
|
|
|
// Star filling the context with useful data - any jQuery selections, as usual should be named with a preceding $
|
|
context = {
|
|
// Reference to the textarea element which the wikiEditor is being built around
|
|
'$textarea': $(this),
|
|
// Container for any number of mutually exclusive views that are accessible by tabs
|
|
'views': {},
|
|
// Container for any number of module-specific data - only including data for modules in use on this context
|
|
'modules': {},
|
|
// General place to shouve bits of data into
|
|
'data': {},
|
|
// Unique numeric ID of this instance used both for looking up and differentiating instances of wikiEditor
|
|
'instance': $.wikiEditor.instances.push( $(this) ) - 1,
|
|
// Array mapping elements in the textarea to character offsets
|
|
'offsets': null,
|
|
// Cache for context.fn.htmlToText()
|
|
'htmlToTextMap': {},
|
|
// The previous HTML of the iframe, stored to detect whether something really changed.
|
|
'oldHTML': null,
|
|
// Same for delayedChange()
|
|
'oldDelayedHTML': null,
|
|
// The previous selection of the iframe, stored to detect whether the selection has changed
|
|
'oldDelayedSel': null,
|
|
// Saved selection state for IE
|
|
'savedSelection': null,
|
|
// Stack of states in { html: [string] } form
|
|
'history': [],
|
|
// Current history state position - this is number of steps backwards, so it's always -1 or less
|
|
'historyPosition': -1,
|
|
/// The previous historyPosition, stored to detect if change events were due to an undo or redo action
|
|
'oldDelayedHistoryPosition': -1,
|
|
// List of extensions active on this context
|
|
'extensions': [],
|
|
};
|
|
|
|
/*
|
|
* Externally Accessible API
|
|
*
|
|
* These are available using calls to $j(selection).wikiEditor( call, data ) where selection is a jQuery selection
|
|
* of the textarea that the wikiEditor instance was built around.
|
|
*/
|
|
|
|
context.api = {
|
|
/**
|
|
* Activates a module on a specific context with optional configuration data.
|
|
*
|
|
* @param data Either a string of the name of a module to add without any additional configuration parameters,
|
|
* or an object with members keyed with module names and valued with configuration objects.
|
|
*/
|
|
'addModule': function( context, data ) {
|
|
var modules = {};
|
|
if ( typeof data == 'string' ) {
|
|
modules[data] = {};
|
|
} else if ( typeof data == 'object' ) {
|
|
modules = data;
|
|
}
|
|
for ( var module in modules ) {
|
|
// Check for the existance of an available / supported module with a matching name and a create function
|
|
if ( typeof module == 'string' && $.wikiEditor.isSupported( $.wikiEditor.modules[module] ) ) {
|
|
// Extend the context's core API with this module's own API calls
|
|
if ( 'api' in $.wikiEditor.modules[module] ) {
|
|
for ( var call in $.wikiEditor.modules[module].api ) {
|
|
// Modules may not overwrite existing API functions - first come, first serve
|
|
if ( !( call in context.api ) ) {
|
|
context.api[call] = $.wikiEditor.modules[module].api[call];
|
|
}
|
|
}
|
|
}
|
|
// Activate the module on this context
|
|
if ( 'fn' in $.wikiEditor.modules[module] && 'create' in $.wikiEditor.modules[module].fn ) {
|
|
// Add a place for the module to put it's own stuff
|
|
context.modules[module] = {};
|
|
// Tell the module to create itself on the context
|
|
$.wikiEditor.modules[module].fn.create( context, modules[module] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Event Handlers
|
|
*
|
|
* These act as filters returning false if the event should be ignored or returning true if it should be passed
|
|
* on to all modules. This is also where we can attach some extra information to the events.
|
|
*/
|
|
|
|
context.evt = {
|
|
/**
|
|
* Filters change events, which occur when the user interacts with the contents of the iframe. The goal of this
|
|
* function is to both classify the scope of changes as 'division' or 'character' and to prevent further
|
|
* processing of events which did not actually change the content of the iframe.
|
|
*/
|
|
'keydown': function( event ) {
|
|
switch ( event.which ) {
|
|
case 90: // z
|
|
case 89: // y
|
|
if ( event.which == 89 && !$.browser.msie ) {
|
|
// only handle y events for IE
|
|
return true;
|
|
} else if ( ( event.ctrlKey || event.metaKey ) && context.history.length ) {
|
|
// HistoryPosition is a negative number between -1 and -context.history.length, in other words
|
|
// it's the number of steps backwards from the latest state.
|
|
var newPosition;
|
|
if ( event.shiftKey || event.which == 89 ) {
|
|
// Redo
|
|
newPosition = context.historyPosition + 1;
|
|
} else {
|
|
// Undo
|
|
newPosition = context.historyPosition - 1;
|
|
}
|
|
// Only act if we are switching to a valid state
|
|
if ( newPosition >= ( context.history.length * -1 ) && newPosition < 0 ) {
|
|
// Make sure we run the history storing code before we make this change
|
|
context.fn.updateHistory( context.oldDelayedHTML != context.$content.html() );
|
|
context.oldDelayedHistoryPosition = context.historyPosition;
|
|
context.historyPosition = newPosition;
|
|
// Change state
|
|
// FIXME: Destroys event handlers, will be a problem with template folding
|
|
context.$content.html(
|
|
context.history[context.history.length + context.historyPosition].html
|
|
);
|
|
context.fn.purgeOffsets();
|
|
if( context.history[context.history.length + context.historyPosition].sel ) {
|
|
context.fn.setSelection( {
|
|
start: context.history[context.history.length + context.historyPosition].sel[0],
|
|
end: context.history[context.history.length + context.historyPosition].sel[1]
|
|
} );
|
|
}
|
|
}
|
|
// Prevent the browser from jumping in and doing its stuff
|
|
return false;
|
|
}
|
|
break;
|
|
// Intercept all tab events to provide consisten behavior across browsers
|
|
// Webkit browsers insert tab characters by default into the iframe rather than changing input focus
|
|
case 9: //tab
|
|
// if any modifier keys are pressed, allow the browser to do it's thing
|
|
if ( event.ctrlKey || event.altKey || event.shiftKey ) {
|
|
return true;
|
|
} else {
|
|
var $tabindexList = $j( '[tabindex]:visible' ).sort( function( a, b ) {
|
|
return a.tabIndex - b.tabIndex;
|
|
} );
|
|
for( var i=0; i < $tabindexList.length; i++ ) {
|
|
if( $tabindexList.eq( i ).attr('id') == context.$iframe.attr( 'id' ) ) {
|
|
$tabindexList.get( i + 1 ).focus();
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
break;
|
|
case 86: //v
|
|
if ( event.ctrlKey && $.browser.msie && 'paste' in context.evt ) {
|
|
//paste, intercepted for IE
|
|
context.evt.paste( event );
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/* Internal Functions */
|
|
|
|
context.fn = {
|
|
/**
|
|
* Executes core event filters as well as event handlers provided by modules.
|
|
*/
|
|
'trigger': function( name, event ) {
|
|
// Event is an optional argument, but from here on out, at least the type field should be dependable
|
|
if ( typeof event == 'undefined' ) {
|
|
event = { 'type': 'custom' };
|
|
}
|
|
// Ensure there's a place for extra information to live
|
|
if ( typeof event.data == 'undefined' ) {
|
|
event.data = {};
|
|
}
|
|
|
|
// Allow filtering to occur
|
|
if ( name in context.evt ) {
|
|
if ( !context.evt[name]( event ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
var returnFromModules = null; //they return null by default
|
|
// Pass the event around to all modules activated on this context
|
|
|
|
for ( var module in context.modules ) {
|
|
if (
|
|
module in $.wikiEditor.modules &&
|
|
'evt' in $.wikiEditor.modules[module] &&
|
|
name in $.wikiEditor.modules[module].evt
|
|
) {
|
|
var ret = $.wikiEditor.modules[module].evt[name]( context, event );
|
|
if (ret != null) {
|
|
//if 1 returns false, the end result is false
|
|
if( returnFromModules == null ) {
|
|
returnFromModules = ret;
|
|
} else {
|
|
returnFromModules = returnFromModules && ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( returnFromModules != null ) {
|
|
return returnFromModules;
|
|
} else {
|
|
return true;
|
|
}
|
|
},
|
|
/**
|
|
* Adds a button to the UI
|
|
*/
|
|
'addButton': function( options ) {
|
|
// Ensure that buttons and tabs are visible
|
|
context.$controls.show();
|
|
context.$buttons.show();
|
|
return $( '<button />' )
|
|
.text( $.wikiEditor.autoMsg( options, 'caption' ) )
|
|
.click( options.action )
|
|
.appendTo( context.$buttons );
|
|
},
|
|
/**
|
|
* Adds a view to the UI, which is accessed using a set of tabs. Views are mutually exclusive and by default a
|
|
* wikitext view will be present. Only when more than one view exists will the tabs will be visible.
|
|
*/
|
|
'addView': function( options ) {
|
|
// Adds a tab
|
|
function addTab( options ) {
|
|
// Ensure that buttons and tabs are visible
|
|
context.$controls.show();
|
|
context.$tabs.show();
|
|
// Return the newly appended tab
|
|
return $( '<div></div>' )
|
|
.attr( 'rel', 'wikiEditor-ui-view-' + options.name )
|
|
.addClass( context.view == options.name ? 'current' : null )
|
|
.append( $( '<a></a>' )
|
|
.attr( 'href', '#' )
|
|
.mousedown( function() {
|
|
// No dragging!
|
|
return false;
|
|
} )
|
|
.click( function( event ) {
|
|
context.$ui.find( '.wikiEditor-ui-view' ).hide();
|
|
context.$ui.find( '.' + $(this).parent().attr( 'rel' ) ).show();
|
|
context.$tabs.find( 'div' ).removeClass( 'current' );
|
|
$(this).parent().addClass( 'current' );
|
|
$(this).blur();
|
|
if ( 'init' in options && typeof options.init == 'function' ) {
|
|
options.init( context );
|
|
}
|
|
event.preventDefault();
|
|
return false;
|
|
} )
|
|
.text( $.wikiEditor.autoMsg( options, 'title' ) )
|
|
)
|
|
.appendTo( context.$tabs );
|
|
}
|
|
// Automatically add the previously not-needed wikitext tab
|
|
if ( !context.$tabs.children().size() ) {
|
|
addTab( { 'name': 'wikitext', 'titleMsg': 'wikieditor-wikitext-tab' } );
|
|
}
|
|
// Add the tab for the view we were actually asked to add
|
|
addTab( options );
|
|
// Return newly appended view
|
|
return $( '<div></div>' )
|
|
.addClass( 'wikiEditor-ui-view wikiEditor-ui-view-' + options.name )
|
|
.hide()
|
|
.appendTo( context.$ui );
|
|
},
|
|
/**
|
|
* Save scrollTop and cursor position for IE
|
|
*/
|
|
'saveCursorAndScrollTop': function() {
|
|
if ( $.client.name === 'msie' ) {
|
|
var IHateIE = {
|
|
'scrollTop' : context.$textarea.scrollTop(),
|
|
'pos': context.$textarea.textSelection( 'getCaretPosition', { startAndEnd: true } )
|
|
};
|
|
context.$textarea.data( 'IHateIE', IHateIE );
|
|
}
|
|
},
|
|
/**
|
|
* Restore scrollTo and cursor position for IE
|
|
*/
|
|
'restoreCursorAndScrollTop': function() {
|
|
if ( $.client.name === 'msie' ) {
|
|
var IHateIE = context.$textarea.data( 'IHateIE' );
|
|
if ( IHateIE ) {
|
|
context.$textarea.scrollTop( IHateIE.scrollTop );
|
|
context.$textarea.textSelection( 'setSelection', { start: IHateIE.pos[0], end: IHateIE.pos[1] } );
|
|
context.$textarea.data( 'IHateIE', null );
|
|
}
|
|
}
|
|
},
|
|
/**
|
|
* Save text selection for IE
|
|
*/
|
|
'saveSelection': function() {
|
|
if ( $.client.name === 'msie' ) {
|
|
context.$textarea.focus();
|
|
context.savedSelection = document.selection.createRange();
|
|
}
|
|
},
|
|
/**
|
|
* Restore text selection for IE
|
|
*/
|
|
'restoreSelection': function() {
|
|
if ( $.client.name === 'msie' && context.savedSelection !== null ) {
|
|
context.$textarea.focus();
|
|
context.savedSelection.select();
|
|
context.savedSelection = null;
|
|
}
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Base UI Construction
|
|
*
|
|
* The UI is built from several containers, the outer-most being a div classed as "wikiEditor-ui". These containers
|
|
* provide a certain amount of "free" layout, but in some situations procedural layout is needed, which is performed
|
|
* as a response to the "resize" event.
|
|
*/
|
|
|
|
// Assemble a temporary div to place over the wikiEditor while it's being constructed
|
|
/* Disabling our loading div for now
|
|
var $loader = $( '<div></div>' )
|
|
.addClass( 'wikiEditor-ui-loading' )
|
|
.append( $( '<span>' + mediaWiki.msg.get( 'wikieditor-loading' ) + '</span>' )
|
|
.css( 'marginTop', context.$textarea.height() / 2 ) );
|
|
*/
|
|
// Encapsulate the textarea with some containers for layout
|
|
context.$textarea
|
|
/* Disabling our loading div for now
|
|
.after( $loader )
|
|
.add( $loader )
|
|
*/
|
|
.wrapAll( $( '<div></div>' ).addClass( 'wikiEditor-ui' ) )
|
|
.wrapAll( $( '<div></div>' ).addClass( 'wikiEditor-ui-view wikiEditor-ui-view-wikitext' ) )
|
|
.wrapAll( $( '<div></div>' ).addClass( 'wikiEditor-ui-left' ) )
|
|
.wrapAll( $( '<div></div>' ).addClass( 'wikiEditor-ui-bottom' ) )
|
|
.wrapAll( $( '<div></div>' ).addClass( 'wikiEditor-ui-text' ) );
|
|
// Get references to some of the newly created containers
|
|
context.$ui = context.$textarea.parent().parent().parent().parent().parent();
|
|
context.$wikitext = context.$textarea.parent().parent().parent().parent();
|
|
// Add in tab and button containers
|
|
context.$wikitext
|
|
.before(
|
|
$( '<div></div>' ).addClass( 'wikiEditor-ui-controls' )
|
|
.append( $( '<div></div>' ).addClass( 'wikiEditor-ui-tabs' ).hide() )
|
|
.append( $( '<div></div>' ).addClass( 'wikiEditor-ui-buttons' ) )
|
|
)
|
|
.before( $( '<div style="clear:both;"></div>' ) );
|
|
// Get references to some of the newly created containers
|
|
context.$controls = context.$ui.find( '.wikiEditor-ui-buttons' ).hide();
|
|
context.$buttons = context.$ui.find( '.wikiEditor-ui-buttons' );
|
|
context.$tabs = context.$ui.find( '.wikiEditor-ui-tabs' );
|
|
// Clear all floating after the UI
|
|
context.$ui.after( $( '<div style="clear:both;"></div>' ) );
|
|
// Attach a right container
|
|
context.$wikitext.append( $( '<div></div>' ).addClass( 'wikiEditor-ui-right' ) );
|
|
// Attach a top container to the left pane
|
|
context.$wikitext.find( '.wikiEditor-ui-left' ).prepend( $( '<div></div>' ).addClass( 'wikiEditor-ui-top' ) );
|
|
// Setup the intial view
|
|
context.view = 'wikitext';
|
|
// Trigger the "resize" event anytime the window is resized
|
|
$( window ).resize( function( event ) { context.fn.trigger( 'resize', event ); } );
|
|
}
|
|
|
|
/* API Execution */
|
|
|
|
// Since javascript gives arguments as an object, we need to convert them so they can be used more easily
|
|
var args = $.makeArray( arguments );
|
|
|
|
// Dynamically setup core extensions for modules that are required
|
|
if ( args[0] == 'addModule' && typeof args[1] != 'undefined' ) {
|
|
var modules = args[1];
|
|
if ( typeof modules != "object" ) {
|
|
modules = {};
|
|
modules[args[1]] = '';
|
|
}
|
|
for ( module in modules ) {
|
|
// Only allow modules which are supported (and thus actually being turned on) affect the decision to extend
|
|
if ( module in $.wikiEditor.modules && $.wikiEditor.isSupported( $.wikiEditor.modules[module] ) ) {
|
|
// Activate all required core extensions on context
|
|
for ( e in $.wikiEditor.extensions ) {
|
|
if (
|
|
$.wikiEditor.isRequired( $.wikiEditor.modules[module], e ) &&
|
|
context.extensions.indexOf( e ) === -1
|
|
) {
|
|
context.extensions[context.extensions.length] = e;
|
|
$.wikiEditor.extensions[e]( context );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// There would need to be some arguments if the API is being called
|
|
if ( args.length > 0 ) {
|
|
// Handle API calls
|
|
var call = args.shift();
|
|
if ( call in context.api ) {
|
|
context.api[call]( context, typeof args[0] == 'undefined' ? {} : args[0] );
|
|
}
|
|
}
|
|
|
|
// Store the context for next time, and support chaining
|
|
return $(this).data( 'wikiEditor-context', context );
|
|
|
|
}; } )( jQuery );
|