r73055 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r73054‎ | r73055 | r73056 >
Date:03:08, 15 September 2010
Author:tparscal
Status:ok
Tags:
Comment:
Copied over all the resources needed to make WikiEditor stand on it's own two feet. Lots more to do as far as organizing and fleshing out the extensions's PHP bits.
Modified paths:
  • /trunk/extensions/WikiEditor/modules/AddMediaWizard.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/Highlight.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/Preview.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/PreviewDialog.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/Publish.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/TemplateEditor.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/Templates.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/Toc.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/Toolbar.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/images (added) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.html (added) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.preview.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.previewDialog.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.publish.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.templateEditor.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.templates.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.toc.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.js (added) (history)
  • /trunk/extensions/WikiEditor/modules/wikiEditor.css (added) (history)
  • /trunk/extensions/WikiEditor/modules/wikiEditor.dialogs.css (added) (history)
  • /trunk/extensions/WikiEditor/modules/wikiEditor.preview.css (added) (history)
  • /trunk/extensions/WikiEditor/modules/wikiEditor.previewDialog.css (added) (history)
  • /trunk/extensions/WikiEditor/modules/wikiEditor.toc.css (added) (history)
  • /trunk/extensions/WikiEditor/modules/wikiEditor.toolbar.css (added) (history)
  • /trunk/extensions/WikiEditor/tests (added) (history)

Diff [purge]

Index: trunk/extensions/WikiEditor/tests/wikiEditor.toolbar.js
@@ -0,0 +1,246 @@
 2+/**
 3+ * Test set for the edit toolbar
 4+ */
 5+var textareaId = '#wpTextbox1';
 6+var wikiEditorTests = {
 7+ // Add emoticons section
 8+ 'add_sections_toolbar': {
 9+ 'call': 'addToToolbar',
 10+ 'data': {
 11+ 'sections': {
 12+ 'emoticons': {
 13+ 'type': 'toolbar',
 14+ 'label': 'Emoticons'
 15+ }
 16+ }
 17+ },
 18+ 'test': '*[rel=emoticons].section',
 19+ 'pre': 0,
 20+ 'post': 1
 21+ },
 22+ // Add faces group to emoticons section
 23+ 'add_groups': {
 24+ 'call': 'addToToolbar',
 25+ 'data': {
 26+ 'section': 'emoticons',
 27+ 'groups': {
 28+ 'faces': {
 29+ 'label': 'Faces'
 30+ }
 31+ }
 32+ },
 33+ 'test': '*[rel=emoticons].section *[rel=faces].group',
 34+ 'pre': 0,
 35+ 'post': 1
 36+ },
 37+ // Add smile tool to faces group of emoticons section
 38+ 'add_tools': {
 39+ 'call': 'addToToolbar',
 40+ 'data': {
 41+ 'section': 'emoticons',
 42+ 'group': 'faces',
 43+ 'tools': {
 44+ 'smile': {
 45+ label: 'Smile!',
 46+ type: 'button',
 47+ icon: 'http://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Gnome-face-smile.svg/22px-Gnome-face-smile.svg.png',
 48+ action: {
 49+ type: 'encapsulate',
 50+ options: {
 51+ pre: ":)"
 52+ }
 53+ }
 54+ }
 55+ }
 56+ },
 57+ 'test': '*[rel=emoticons].section *[rel=faces].group *[rel=smile].tool',
 58+ 'pre': 0,
 59+ 'post': 1
 60+ },
 61+ // Add info section
 62+ 'add_sections_booklet': {
 63+ 'call': 'addToToolbar',
 64+ 'data': {
 65+ 'sections': {
 66+ 'info': {
 67+ 'type': 'booklet',
 68+ 'label': 'Info'
 69+ }
 70+ }
 71+ },
 72+ 'test': '*[rel=info].section',
 73+ 'pre': 0,
 74+ 'post': 1
 75+ },
 76+ // Add info section
 77+ 'add_pages_table': {
 78+ 'call': 'addToToolbar',
 79+ 'data': {
 80+ 'section': 'info',
 81+ 'pages': {
 82+ 'colors': {
 83+ 'layout': 'table',
 84+ 'label': 'Colors',
 85+ 'headings': [
 86+ { text: 'Name' },
 87+ { text: 'Temperature' },
 88+ { text: 'Swatch' }
 89+ ]
 90+ }
 91+ }
 92+ },
 93+ 'test': '*[rel=info].section *[rel=colors].page',
 94+ 'pre': 0,
 95+ 'post': 1
 96+ },
 97+ // Add colors rows
 98+ 'add_rows': {
 99+ 'call': 'addToToolbar',
 100+ 'data': {
 101+ 'section': 'info',
 102+ 'page': 'colors',
 103+ 'rows': [
 104+ {
 105+ 'name': { text: 'Red' },
 106+ 'temp': { text: 'Warm' },
 107+ 'swatch': { html: '<div style="width:10px;height:10px;background-color:red;">' }
 108+ },
 109+ {
 110+ 'name': { text: 'Blue' },
 111+ 'temp': { text: 'Cold' },
 112+ 'swatch': { html: '<div style="width:10px;height:10px;background-color:blue;">' }
 113+ },
 114+ {
 115+ 'name': { text: 'Silver' },
 116+ 'temp': { text: 'Neutral' },
 117+ 'swatch': { html: '<div style="width:10px;height:10px;background-color:silver;">' }
 118+ }
 119+ ]
 120+ },
 121+ 'test': '*[rel=info].section *[rel=colors].page tr td',
 122+ 'pre': 0,
 123+ 'post': 9
 124+ },
 125+ // Add
 126+ 'add_pages_characters': {
 127+ 'call': 'addToToolbar',
 128+ 'data': {
 129+ 'section': 'info',
 130+ 'pages': {
 131+ 'emoticons': {
 132+ 'layout': 'characters',
 133+ 'label': 'Emoticons'
 134+ },
 135+ 'removeme': {
 136+ 'layout': 'characters',
 137+ 'label': 'Remove Me!'
 138+ }
 139+ }
 140+ },
 141+ 'test': '*[rel=info].section *[rel=emoticons].page',
 142+ 'pre': 0,
 143+ 'post': 1
 144+ },
 145+ // Add
 146+ 'add_characters': {
 147+ 'call': 'addToToolbar',
 148+ 'data': {
 149+ 'section': 'info',
 150+ 'page': 'emoticons',
 151+ 'characters': [ ':)', ':))', ':(', '<3', ';)' ]
 152+ },
 153+ 'test': '*[rel=info].section *[rel=emoticons].page *[rel=":)"]',
 154+ 'pre': 0,
 155+ 'post': 1
 156+ },
 157+ // Remove page
 158+ 'remove_page': {
 159+ 'call': 'removeFromToolbar',
 160+ 'data': {
 161+ 'section': 'info',
 162+ 'page': 'removeme'
 163+ },
 164+ 'test': '*[rel=info].section *[rel=removeme].page',
 165+ 'pre': 1,
 166+ 'post': 0
 167+ },
 168+ // Remove :)) from emoticon characters
 169+ 'remove_character': {
 170+ 'call': 'removeFromToolbar',
 171+ 'data': {
 172+ 'section': 'info',
 173+ 'page': 'emoticons',
 174+ 'character': ':))'
 175+ },
 176+ 'test': '*[rel=info].section *[rel=emoticons].page a[rel=":))"]',
 177+ 'pre': 1,
 178+ 'post': 0
 179+ },
 180+ // Remove row from colors table of info section
 181+ 'remove_row': {
 182+ 'call': 'removeFromToolbar',
 183+ 'data': {
 184+ 'section': 'info',
 185+ 'page': 'colors',
 186+ 'row': 0
 187+ },
 188+ 'test': '*[rel=info].section *[rel=colors].page tr td',
 189+ 'pre': 9,
 190+ 'post': 6
 191+ }
 192+};
 193+$j(document).ready( function() {
 194+ var button = $j( '<button>Run wikiEditor Tests!</button>' )
 195+ .css( {
 196+ 'position': 'fixed',
 197+ 'bottom': 0,
 198+ 'right': 0,
 199+ 'width': '100%',
 200+ 'backgroundColor': '#333333',
 201+ 'opacity': 0.75,
 202+ 'color': '#DDDDDD',
 203+ 'padding': '0.5em',
 204+ 'border': 'none',
 205+ 'display': 'none'
 206+ } )
 207+ .click( function() {
 208+ if ( $j(this).attr( 'enabled' ) == 'false' ) {
 209+ $j(this).slideUp( 'fast' );
 210+ return false;
 211+ }
 212+ var messages = [ 'Running tests for wikiEditor API' ];
 213+ var $target = $j( textareaId );
 214+ var $ui = $target.data( 'wikiEditor-context' ).$ui;
 215+ var passes = 0;
 216+ var tests = 0;
 217+ for ( test in wikiEditorTests ) {
 218+ var pre = $ui.find( wikiEditorTests[test].test ).size() ==
 219+ wikiEditorTests[test].pre;
 220+ messages.push ( test + '-pre: ' + ( pre ? 'PASS' : 'FAIL' ) );
 221+ $target.wikiEditor(
 222+ wikiEditorTests[test].call,
 223+ wikiEditorTests[test].data
 224+ );
 225+ var post = $ui.find( wikiEditorTests[test].test ).size() ==
 226+ wikiEditorTests[test].post;
 227+ messages.push ( test + '-post: ' + ( post ? 'PASS' : 'FAIL' ) );
 228+ if ( pre && post ) {
 229+ passes++;
 230+ }
 231+ tests++;
 232+ }
 233+ if ( window.console !== undefined ) {
 234+ for ( message in messages ) {
 235+ console.log( messages[message] );
 236+ }
 237+ }
 238+ $j(this)
 239+ .attr( 'title', messages.join( " | " ) )
 240+ .text( passes + ' / ' + tests + ' were successful' )
 241+ .css( 'backgroundColor', passes < tests ? 'red' : 'green' )
 242+ .attr( 'enabled', 'false' )
 243+ .blur();
 244+ } )
 245+ .appendTo( $j( 'body' ) );
 246+ setTimeout( function() { button.slideDown( 'fast' ) }, 2000 );
 247+} );
Property changes on: trunk/extensions/WikiEditor/tests/wikiEditor.toolbar.js
___________________________________________________________________
Added: svn:eol-style
1248 + native
Index: trunk/extensions/WikiEditor/modules/wikiEditor.preview.css
@@ -0,0 +1,23 @@
 2+/* CSS for wikiEditor preview plugin */
 3+
 4+.wikiEditor-preview-loading {
 5+ padding: 1em;
 6+ background-color: white;
 7+}
 8+.wikiEditor-preview-loading span {
 9+ color: #666666;
 10+}
 11+.wikiEditor-preview-spinner {
 12+ padding-right: 1em;
 13+}
 14+.wikiEditor-preview-contents {
 15+ padding: 1em;
 16+ background-color: white;
 17+}
 18+#wikiEditor-0-preview-dialog .wikiEditor-ui-loading {
 19+ overflow: hidden;
 20+ border: none;
 21+}
 22+.ui-dialog .ui-dialog-buttonpane {
 23+ margin: 0 !important;
 24+}
\ No newline at end of file
Property changes on: trunk/extensions/WikiEditor/modules/wikiEditor.preview.css
___________________________________________________________________
Added: svn:eol-style
125 + native
Index: trunk/extensions/WikiEditor/modules/Publish.js
@@ -0,0 +1,12 @@
 2+/* JavaScript for WikiEditor Publish module */
 3+
 4+$j(document).ready( function() {
 5+ // Check preferences for publish
 6+ if ( !wgWikiEditorEnabledModules.publish ) {
 7+ return true;
 8+ }
 9+ // Add the publish module
 10+ if ( $j.fn.wikiEditor ) {
 11+ $j( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'publish' );
 12+ }
 13+});
Property changes on: trunk/extensions/WikiEditor/modules/Publish.js
___________________________________________________________________
Added: svn:eol-style
114 + native
Index: trunk/extensions/WikiEditor/modules/wikiEditor.dialogs.css
@@ -0,0 +1,309 @@
 2+/* wikiEditor dialogs module */
 3+
 4+.wikiEditor-toolbar-dialog table {
 5+ margin-top: 0.75em;
 6+}
 7+.wikiEditor-toolbar-dialog table td {
 8+ padding: 0.5em;
 9+ height: 3em;
 10+ overflow: visible;
 11+}
 12+/* Put suggestions (default z-index 99) on top of dialogs (z-index 1002) */
 13+div.suggestions {
 14+ z-index: 1099;
 15+}
 16+.wikiEditor-toolbar-dialog .ui-dialog-titlebar-close:hover {
 17+ text-decoration: none;
 18+}
 19+.wikiEditor-toolbar-dialog .ui-dialog-content .status-invalid input {
 20+ border: 2px solid red;
 21+ padding: 2px 1px;
 22+}
 23+.wikiEditor-toolbar-dialog .ui-dialog-titlebar {
 24+ padding: 0.9em 1.4em 0.6em !important;
 25+}
 26+.wikiEditor-toolbar-dialog table td {
 27+ padding: 0 !important;
 28+}
 29+.wikiEditor-toolbar-dialog .ui-dialog-buttonpane button {
 30+ -moz-border-radius: 4px;
 31+ -webkit-border-radius: 4px;
 32+ padding: 0.2em 0.6em 0.15em !important;
 33+ margin: 0.5em 0 0.5em 0.4em !important;
 34+ border: 1px solid #a6a6a6 !important;
 35+ background: #f2f2f2 url( ../images/wikiEditor/dialogs/button_off.png?1) repeat-x scroll 50% 100% !important;
 36+}
 37+.wikiEditor-toolbar-dialog .ui-dialog-buttonpane button:hover {
 38+ border-color: #6e7273;
 39+ background: #e1e1e1 url( ../images/wikiEditor/dialogs/button_over.png?1) repeat-x scroll 50% 100% !important;
 40+}
 41+.wikiEditor-toolbar-dialog .ui-dialog-buttonpane button:active,
 42+.wikiEditor-toolbar-dialog .ui-dialog-buttonpane button:focus {
 43+ border-color: #707271;
 44+ background: #bfbfbf url( ../images/wikiEditor/dialogs/button_down.png?1) repeat-x scroll 50% 100% !important;
 45+}
 46+.wikiEditor-toolbar-dialog .ui-dialog-buttonpane button.disabled {
 47+ color: #7f7f7f;
 48+ border-color: #cccccc;
 49+ background: #f2f2f2 url( ../images/wikiEditor/dialogs/button_disabled.png?1) repeat-x scroll 50% 100% !important;
 50+}
 51+/* Disables the annoying dashed border Firefox puts on active buttons */
 52+.wikiEditor-toolbar-dialog .ui-dialog-buttonpane button::-moz-focus-inner {
 53+ border: 0;
 54+}
 55+.wikiEditor-toolbar-dialog .ui-widget-header {
 56+ background: #f0f0f0 url( ../images/wikiEditor/dialogs/titlebar_fade.png?1) repeat-x scroll 50% 100% !important;
 57+}
 58+/* FIXME: Should just update the icon sprite if we're keeping this X */
 59+.wikiEditor-toolbar-dialog .ui-icon-closethick {
 60+ background: url( ../images/wikiEditor/dialogs/close_x.png?1) no-repeat 50% 50% !important;
 61+}
 62+.wikiEditor-toolbar-dialog .ui-dialog-buttonpane {
 63+ margin-top: 0 !important;
 64+ padding:0.3em 1.4em 0.5em 1.4em !important;
 65+}
 66+
 67+.wikiEditor-toolbar-dialog .ui-dialog-content fieldset{
 68+ border: none !important;
 69+ margin: 0 !important;
 70+ padding: 0 !important;
 71+}
 72+.wikiEditor-toolbar-dialog .ui-widget-header {
 73+ border-bottom:1px solid #6bc8f3 !important;
 74+}
 75+.wikiEditor-toolbar-dialog .ui-dialog-content input[type=text] {
 76+ -moz-box-sizing: border-box;
 77+ -ms-box-sizing: border-box;
 78+ -webkit-box-sizing: border-box;
 79+ -khtml-box-sizing: border-box;
 80+}
 81+.wikiEditor-toolbar-dialog .ui-dialog-content input[type="radio"],
 82+.wikiEditor-toolbar-dialog .ui-dialog-content input[type="checkbox"] {
 83+ margin-left: 0;
 84+}
 85+.wikiEditor-toolbar-dialog .ui-dialog-titlebar-close {
 86+ padding: 0;
 87+}
 88+body.ltr .wikiEditor-toolbar-dialog .ui-dialog-titlebar-close {
 89+ right: 0.9em;
 90+}
 91+.wikieditor-toolbar-field-wrapper {
 92+ padding: 0 0 25px 0;
 93+}
 94+.wikieditor-toolbar-floated-field-wrapper {
 95+ float: left;
 96+ margin-right: 2em;
 97+}
 98+.wikieditor-toolbar-dialog-hint {
 99+ color: #999999;
 100+}
 101+.wikiEditor-toolbar-dialog,
 102+.wikiEditor-toolbar-dialog .ui-widget-content {
 103+ border: none !important;
 104+}
 105+/* Table Dialog */
 106+
 107+#wikieditor-toolbar-table-dialog fieldset {
 108+ width: 218px;
 109+ padding: 0;
 110+ float: left;
 111+}
 112+body.rtl #wikieditor-toolbar-table-dialog fieldset {
 113+ float: right;
 114+}
 115+#wikieditor-toolbar-table-dialog .wikieditor-toolbar-table-preview-wrapper {
 116+ width: 330px;
 117+ padding: 0;
 118+ float: right;
 119+}
 120+body.rtl #wikieditor-toolbar-table-dialog .wikieditor-toolbar-table-preview-wrapper {
 121+ float: left;
 122+}
 123+body.rtl .wikiEditor-toolbar-dialog .wikieditor-toolbar-table-preview-wrapper table {
 124+ margin-left: 1em;
 125+ margin-right: 0;
 126+}
 127+.wikieditor-toolbar-table-preview-content * {
 128+ cursor: default;
 129+}
 130+.wikiEditor-toolbar-dialog .wikieditor-toolbar-table-preview-wrapper table {
 131+ width: 100% !important;
 132+}
 133+.wikiEditor-toolbar-dialog .wikieditor-toolbar-table-preview-content table td {
 134+ padding: 10px 4px !important;
 135+ height: auto !important;
 136+}
 137+.wikiEditor-toolbar-dialog .wikieditor-toolbar-table-preview-content table th {
 138+ padding: 7px 3px !important;
 139+}
 140+.wikieditor-toolbar-table-dimension-fields .wikieditor-toolbar-field-wrapper {
 141+ float: left;
 142+ margin-right: 20px;
 143+ vertical-align: bottom;
 144+}
 145+body.rtl .wikieditor-toolbar-table-dimension-fields .wikieditor-toolbar-field-wrapper {
 146+ float: right;
 147+ margin-right: 0px;
 148+ margin-left: 20px;
 149+}
 150+.wikiEditor-toolbar-dialog .ui-dialog-content {
 151+ padding: 30px 20px 0 !important;
 152+}
 153+.wikieditor-toolbar-dialog-wrapper {
 154+ width: 100%;
 155+}
 156+/* REPLACE Dialog */
 157+
 158+/* Insert Link Dialog */
 159+#wikieditor-toolbar-link-int-target-status {
 160+ float: right;
 161+}
 162+#wikieditor-toolbar-link-int-target,
 163+#wikieditor-toolbar-link-int-text {
 164+ width: 100%;
 165+}
 166+#wikieditor-toolbar-tool-link-int-target-label {
 167+ float: left;
 168+ line-height: 1.7em;
 169+}
 170+#wikieditor-toolbar-link-int-target-status-loading {
 171+ line-height: 1.7em;
 172+}
 173+#wikieditor-toolbar-link-int-target-status-exists,
 174+#wikieditor-toolbar-link-int-target-status-notexists,
 175+#wikieditor-toolbar-link-int-target-status-invalid,
 176+#wikieditor-toolbar-link-int-target-status-external {
 177+ padding-left: 30px;
 178+ background-position: 0 50%;
 179+ background-repeat: no-repeat;
 180+}
 181+#wikieditor-toolbar-link-int-target-status-exists {
 182+ background-image: url( ../images/wikiEditor/dialogs/insert-link-exists.png?1);
 183+}
 184+#wikieditor-toolbar-link-int-target-status-notexists {
 185+ background-image: url( ../images/wikiEditor/dialogs/insert-link-notexists.png?1);
 186+}
 187+#wikieditor-toolbar-link-int-target-status-invalid {
 188+ background-image: url( ../images/wikiEditor/dialogs/insert-link-invalid.png?1);
 189+}
 190+#wikieditor-toolbar-link-int-target-status-external {
 191+ background-image: url( ../images/wikiEditor/dialogs/insert-link-external.png?1);
 192+}
 193+
 194+/* Reference Dialog */
 195+#wikieditor-toolbar-reference-dialog label {
 196+ float: left;
 197+ line-height: 1.7em;
 198+}
 199+#wikieditor-toolbar-reference-text {
 200+ width: 100%;
 201+}
 202+/* RTL Changes */
 203+body.rtl .wikiEditor-toolbar-dialog .ui-dialog-buttonpane button {
 204+ float: left;
 205+ margin: 0.5em 0.4em 0.5em 0 !important;
 206+}
 207+body.rtl .wikiEditor-toolbar-dialog .ui-dialog-titlebar-close {
 208+ left: 0.9em;
 209+ right: auto;
 210+}
 211+body.rtl .wikiEditor-toolbar-dialog .ui-dialog-title {
 212+ float:right;
 213+}
 214+body.rtl #wikieditor-toolbar-link-int-target-status {
 215+ float: left;
 216+}
 217+body.rtl #wikieditor-toolbar-tool-link-int-target-label {
 218+ float: right;
 219+}
 220+body.rtl .wikieditor-toolbar-floated-field-wrapper {
 221+ float: right;
 222+ margin-right: 0;
 223+ margin-left: 2em;
 224+}
 225+body.rtl #wikieditor-toolbar-link-int-target-status-exists,
 226+body.rtl #wikieditor-toolbar-link-int-target-status-notexists,
 227+body.rtl #wikieditor-toolbar-link-int-target-status-invalid,
 228+body.rtl #wikieditor-toolbar-link-int-target-status-external {
 229+ padding-left: 0;
 230+ padding-right: 30px;
 231+ background-position: 100% 50%;
 232+}
 233+body.rtl #wikieditor-toolbar-link-int-target-status-external {
 234+ background-image: url( ../images/wikiEditor/dialogs/insert-link-external-rtl.png?1);
 235+}
 236+body.rtl #wikieditor-toolbar-reference-dialog label {
 237+ float: right;
 238+}
 239+/* Template Editor Dialogs */
 240+.wikiEditor-template-dialog-fields label {
 241+ text-transform: capitalize;
 242+ float: left;
 243+ width: 25%;
 244+ line-height: 2.25em;
 245+}
 246+.wikiEditor-template-dialog-fields textarea {
 247+ float: right;
 248+ width: 70%;
 249+ line-height: 1.5em;
 250+ height: 1.5em;
 251+}
 252+.wikiEditor-template-dialog-fields .wikiEditor-template-dialog-field-wrapper {
 253+ padding: 0.75em 0.33em;
 254+ border-bottom: dashed 1px silver;
 255+ clear: both;
 256+}
 257+.wikiEditor-template-dialog-fields .wikiEditor-template-dialog-field-wrapper:first-child {
 258+ padding-top: 0;
 259+}
 260+.wikiEditor-template-dialog-fields .wikiEditor-template-dialog-field-wrapper:last-child {
 261+ border-bottom: none;
 262+}
 263+
 264+/* Self Clearing Floats */
 265+.wikieditor-toolbar-table-dimension-fields:after,
 266+.wikieditor-toolbar-dialog-wrapper:after {
 267+ visibility: hidden;
 268+ display: block;
 269+ font-size: 0;
 270+ content: " ";
 271+ clear: both;
 272+ height: 0;
 273+}
 274+.wikieditor-toolbar-table-dimension-fields,
 275+.wikieditor-toolbar-dialog-wrapper {
 276+ display: inline-table;
 277+}
 278+/* Hides from IE-mac \*/
 279+* html .wikieditor-toolbar-table-dimension-fields,
 280+* html .wikieditor-toolbar-dialog-wrapper {
 281+ height: 1%;
 282+}
 283+.wikieditor-toolbar-table-dimension-fields,
 284+.wikieditor-toolbar-dialog-wrapper {
 285+ display: block;
 286+}
 287+/* End hide from IE-mac */
 288+
 289+.wikiEditor-toolbar-dialog .ui-dialog-buttonpane {
 290+ border-top: 1px solid #cccccc !important;
 291+}
 292+.wikiEditor-toolbar-dialog .ui-dialog-content {
 293+ padding-bottom: 1em !important;
 294+}
 295+
 296+/* Edit dialog */
 297+.wikiEditor-dialog-editoptions {
 298+ margin-top: 15px;
 299+}
 300+
 301+/* Publish dialog */
 302+.wikiEditor-publish-dialog-copywarn {
 303+ margin-top: 0.5em;
 304+}
 305+.wikiEditor-publish-dialog-summary {
 306+ margin-top: 1.5em;
 307+}
 308+.wikiEditor-publish-dialog-options {
 309+ margin-top: 1.5em;
 310+}
\ No newline at end of file
Property changes on: trunk/extensions/WikiEditor/modules/wikiEditor.dialogs.css
___________________________________________________________________
Added: svn:eol-style
1311 + native
Index: trunk/extensions/WikiEditor/modules/Highlight.js
@@ -0,0 +1,12 @@
 2+/* JavaScript for WikiEditor Highlight module */
 3+
 4+$j(document).ready( function() {
 5+ // Check preferences for highlight
 6+ if ( !wgWikiEditorEnabledModules.toc ) { //HACK
 7+ return true;
 8+ }
 9+ // Add the highlight module
 10+ if ( $j.fn.wikiEditor ) {
 11+ $j( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'highlight' );
 12+ }
 13+});
Property changes on: trunk/extensions/WikiEditor/modules/Highlight.js
___________________________________________________________________
Added: svn:eol-style
114 + native
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.preview.js
@@ -0,0 +1,170 @@
 2+/* Preview module for wikiEditor */
 3+( function( $ ) { $.wikiEditor.modules.preview = {
 4+
 5+/**
 6+ * Compatability map
 7+ */
 8+'browsers': {
 9+ // Left-to-right languages
 10+ 'ltr': {
 11+ 'msie': [['>=', 7]],
 12+ 'firefox': [['>=', 3]],
 13+ 'opera': [['>=', 9.6]],
 14+ 'safari': [['>=', 4]]
 15+ },
 16+ // Right-to-left languages
 17+ 'rtl': {
 18+ 'msie': [['>=', 8]],
 19+ 'firefox': [['>=', 3]],
 20+ 'opera': [['>=', 9.6]],
 21+ 'safari': [['>=', 4]]
 22+ }
 23+},
 24+/**
 25+ * Internally used functions
 26+ */
 27+fn: {
 28+ /**
 29+ * Creates a preview module within a wikiEditor
 30+ * @param context Context object of editor to create module in
 31+ * @param config Configuration object to create module from
 32+ */
 33+ create: function( context, config ) {
 34+ if ( 'initialized' in context.modules.preview ) {
 35+ return;
 36+ }
 37+ context.modules.preview = {
 38+ 'initialized': true,
 39+ 'previewText': null,
 40+ 'changesText': null
 41+ };
 42+ context.modules.preview.$preview = context.fn.addView( {
 43+ 'name': 'preview',
 44+ 'titleMsg': 'wikieditor-preview-tab',
 45+ 'init': function( context ) {
 46+ // Gets the latest copy of the wikitext
 47+ var wikitext = context.fn.getContents();
 48+ // Aborts when nothing has changed since the last preview
 49+ if ( context.modules.preview.previewText == wikitext ) {
 50+ return;
 51+ }
 52+ context.modules.preview.$preview.find( '.wikiEditor-preview-contents' ).empty();
 53+ context.modules.preview.$preview.find( '.wikiEditor-preview-loading' ).show();
 54+ $.post(
 55+ wgScriptPath + '/api.php',
 56+ {
 57+ 'action': 'parse',
 58+ 'title': wgPageName,
 59+ 'text': wikitext,
 60+ 'prop': 'text',
 61+ 'pst': '',
 62+ 'format': 'json'
 63+ },
 64+ function( data ) {
 65+ if (
 66+ typeof data.parse == 'undefined' ||
 67+ typeof data.parse.text == 'undefined' ||
 68+ typeof data.parse.text['*'] == 'undefined'
 69+ ) {
 70+ return;
 71+ }
 72+ context.modules.preview.previewText = wikitext;
 73+ context.modules.preview.$preview.find( '.wikiEditor-preview-loading' ).hide();
 74+ context.modules.preview.$preview.find( '.wikiEditor-preview-contents' )
 75+ .html( data.parse.text['*'] )
 76+ .find( 'a:not([href^=#])' ).click( function() { return false; } );
 77+ },
 78+ 'json'
 79+ );
 80+ }
 81+ } );
 82+
 83+ context.$changesTab = context.fn.addView( {
 84+ 'name': 'changes',
 85+ 'titleMsg': 'wikieditor-preview-changes-tab',
 86+ 'init': function( context ) {
 87+ // Gets the latest copy of the wikitext
 88+ var wikitext = context.fn.getContents();
 89+ // Aborts when nothing has changed since the last time
 90+ if ( context.modules.preview.changesText == wikitext ) {
 91+ return;
 92+ }
 93+ context.$changesTab.find( 'table.diff tbody' ).empty();
 94+ context.$changesTab.find( '.wikiEditor-preview-loading' ).show();
 95+
 96+ // Call the API. First PST the input, then diff it
 97+ var postdata = {
 98+ 'action': 'parse',
 99+ 'onlypst': '',
 100+ 'text': wikitext,
 101+ 'format': 'json'
 102+ };
 103+
 104+ $.post( wgScriptPath + '/api.php', postdata, function( data ) {
 105+ try {
 106+ var postdata2 = {
 107+ 'action': 'query',
 108+ 'indexpageids': '',
 109+ 'prop': 'revisions',
 110+ 'titles': wgPageName,
 111+ 'rvdifftotext': data.parse.text['*'],
 112+ 'rvprop': '',
 113+ 'format': 'json'
 114+ };
 115+ var section = $( '[name=wpSection]' ).val();
 116+ if ( section != '' )
 117+ postdata['rvsection'] = section;
 118+
 119+ $.post( wgScriptPath + '/api.php', postdata2, function( data ) {
 120+ // Add diff CSS
 121+ if ( $( 'link[href=' + stylepath + '/common/diff.css]' ).size() == 0 ) {
 122+ $( 'head' ).append( $( '<link />' ).attr( {
 123+ 'rel': 'stylesheet',
 124+ 'type': 'text/css',
 125+ 'href': stylepath + '/common/diff.css'
 126+ } ) );
 127+ }
 128+ try {
 129+ var diff = data.query.pages[data.query.pageids[0]]
 130+ .revisions[0].diff['*'];
 131+ context.$changesTab.find( 'table.diff tbody' )
 132+ .html( diff );
 133+ context.$changesTab
 134+ .find( '.wikiEditor-preview-loading' ).hide();
 135+ context.modules.preview.changesText = wikitext;
 136+ } catch ( e ) { } // "blah is undefined" error, ignore
 137+ }, 'json'
 138+ );
 139+ } catch( e ) { } // "blah is undefined" error, ignore
 140+ }, 'json' );
 141+ }
 142+ } );
 143+
 144+ var loadingMsg = mw.usability.getMsg( 'wikieditor-preview-loading' );
 145+ context.modules.preview.$preview
 146+ .add( context.$changesTab )
 147+ .append( $( '<div />' )
 148+ .addClass( 'wikiEditor-preview-loading' )
 149+ .append( $( '<img />' )
 150+ .addClass( 'wikiEditor-preview-spinner' )
 151+ .attr( {
 152+ 'src': $.wikiEditor.imgPath + 'dialogs/loading.gif',
 153+ 'valign': 'absmiddle',
 154+ 'alt': loadingMsg,
 155+ 'title': loadingMsg
 156+ } )
 157+ )
 158+ .append(
 159+ $( '<span></span>' ).text( loadingMsg )
 160+ )
 161+ )
 162+ .append( $( '<div />' )
 163+ .addClass( 'wikiEditor-preview-contents' )
 164+ );
 165+ context.$changesTab.find( '.wikiEditor-preview-contents' )
 166+ .html( '<table class="diff"><col class="diff-marker" /><col class="diff-content" />' +
 167+ '<col class="diff-marker" /><col class="diff-content" /><tbody /></table>' );
 168+ }
 169+}
 170+
 171+}; } )( jQuery );
Property changes on: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.preview.js
___________________________________________________________________
Added: svn:eol-style
1172 + native
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.templateEditor.js
@@ -0,0 +1,864 @@
 2+/* TemplateEditor module for wikiEditor */
 3+( function( $ ) { $.wikiEditor.modules.templateEditor = {
 4+/**
 5+ * Name mappings, dirty hack which will be removed once "TemplateInfo" extension is more fully supported
 6+ */
 7+'nameMappings': { //keep these all lowercase to navigate web of redirects
 8+ "infobox skyscraper": "building_name",
 9+ "infobox settlement": "official_name"
 10+},
 11+
 12+
 13+/**
 14+ * Compatability map
 15+ */
 16+'browsers': {
 17+ // Left-to-right languages
 18+ 'ltr': {
 19+ 'msie': [['>=', 8]],
 20+ 'firefox': [['>=', 3]],
 21+ 'opera': [['>=', 10]],
 22+ 'safari': [['>=', 4]]
 23+ },
 24+ // Right-to-left languages
 25+ 'rtl': {
 26+ 'msie': false,
 27+ 'firefox': [['>=', 3]],
 28+ 'opera': [['>=', 10]],
 29+ 'safari': [['>=', 4]]
 30+ }
 31+},
 32+/**
 33+ * Core Requirements
 34+ */
 35+'req': [ 'iframe' ],
 36+/**
 37+ * Event handlers
 38+ */
 39+evt: {
 40+
 41+ mark: function( context, event ) {
 42+ // The markers returned by this function are skipped on realchange, so don't regenerate them in that case
 43+ if ( context.modules.highlight.currentScope == 'realchange' ) {
 44+ return;
 45+ }
 46+
 47+ // Get references to the markers and tokens from the current context
 48+ var markers = context.modules.highlight.markers;
 49+ var tokenArray = context.modules.highlight.tokenArray;
 50+ // Collect matching level 0 template call boundaries from the tokenArray
 51+ var level = 0;
 52+ var tokenIndex = 0;
 53+ while ( tokenIndex < tokenArray.length ){
 54+ while ( tokenIndex < tokenArray.length && tokenArray[tokenIndex].label != 'TEMPLATE_BEGIN' ) {
 55+ tokenIndex++;
 56+ }
 57+ //open template
 58+ if ( tokenIndex < tokenArray.length ) {
 59+ var beginIndex = tokenIndex;
 60+ var endIndex = -1; //no match found
 61+ var openTemplates = 1;
 62+ var templatesMatched = false;
 63+ while ( tokenIndex < tokenArray.length - 1 && endIndex == -1 ) {
 64+ tokenIndex++;
 65+ if ( tokenArray[tokenIndex].label == 'TEMPLATE_BEGIN' ) {
 66+ openTemplates++;
 67+ } else if ( tokenArray[tokenIndex].label == 'TEMPLATE_END' ) {
 68+ openTemplates--;
 69+ if ( openTemplates == 0 ) {
 70+ endIndex = tokenIndex;
 71+ } //we can stop looping
 72+ }
 73+ }//while finding template ending
 74+ if ( endIndex != -1 ) {
 75+ markers.push( {
 76+ start: tokenArray[beginIndex].offset,
 77+ end: tokenArray[endIndex].offset,
 78+ type: 'template',
 79+ anchor: 'wrap',
 80+ afterWrap: function( node ) {
 81+ // Generate model
 82+ var model = $.wikiEditor.modules.templateEditor.fn.updateModel( $( node ) );
 83+ if ( model.isCollapsible() ) {
 84+ $.wikiEditor.modules.templateEditor.fn.wrapTemplate( $( node ) );
 85+ $.wikiEditor.modules.templateEditor.fn.bindTemplateEvents( $( node ) );
 86+ } else {
 87+ $( node ).addClass( 'wikiEditor-template-text' );
 88+ }
 89+ },
 90+ beforeUnwrap: function( node ) {
 91+ if ( $( node ).parent().hasClass( 'wikiEditor-template' ) ) {
 92+ $.wikiEditor.modules.templateEditor.fn.unwrapTemplate( $( node ) );
 93+ }
 94+ },
 95+ onSkip: function( node ) {
 96+ if ( $( node ).html() == $( node ).data( 'oldHTML' ) ) {
 97+ // No change
 98+ return;
 99+ }
 100+
 101+ // Text changed, regenerate model
 102+ var model = $.wikiEditor.modules.templateEditor.fn.updateModel( $( node ) );
 103+
 104+ // Update template name if needed
 105+ if ( $( node ).parent().hasClass( 'wikiEditor-template' ) ) {
 106+ var $label = $( node ).parent().find( '.wikiEditor-template-label' );
 107+ var displayName = $.wikiEditor.modules.templateEditor.fn.getTemplateDisplayName( model );
 108+ if ( $label.text() != displayName ) {
 109+ $label.text( displayName );
 110+ }
 111+ }
 112+
 113+ // Wrap or unwrap the template if needed
 114+ if ( $( node ).parent().hasClass( 'wikiEditor-template' ) &&
 115+ !model.isCollapsible() ) {
 116+ $.wikiEditor.modules.templateEditor.fn.unwrapTemplate( $( node ) );
 117+ } else if ( !$( node ).parent().hasClass( 'wikiEditor-template' ) &&
 118+ model.isCollapsible() ) {
 119+ $.wikiEditor.modules.templateEditor.fn.wrapTemplate( $( node ) );
 120+ $.wikiEditor.modules.templateEditor.fn.bindTemplateEvents( $( node ) );
 121+ }
 122+ },
 123+ getAnchor: function( ca1, ca2 ) {
 124+ return $( ca1.parentNode ).is( 'span.wikiEditor-template-text' ) ?
 125+ ca1.parentNode : null;
 126+ },
 127+ context: context,
 128+ skipDivision: 'realchange'
 129+ } );
 130+ } else { //else this was an unmatched opening
 131+ tokenArray[beginIndex].label = 'TEMPLATE_FALSE_BEGIN';
 132+ tokenIndex = beginIndex;
 133+ }
 134+ }//if opentemplates
 135+ }
 136+ }, //mark
 137+
 138+ keydown: function( context, event ) {
 139+ // Reset our ignoreKeypress variable if it's set to true
 140+ if ( context.$iframe.data( 'ignoreKeypress' ) ) {
 141+ context.$iframe.data( 'ignoreKeypress', false );
 142+ }
 143+ var $evtElem = event.jQueryNode;
 144+ if ( $evtElem.hasClass( 'wikiEditor-template-label' ) ) {
 145+ // Allow anything if the command or control key are depressed
 146+ if ( event.ctrlKey || event.metaKey ) return true;
 147+ switch ( event.which ) {
 148+ case 13: // Enter
 149+ $evtElem.click();
 150+ event.preventDefault();
 151+ return false;
 152+ case 32: // Space
 153+ $evtElem.parent().siblings( '.wikiEditor-template-expand' ).click();
 154+ event.preventDefault();
 155+ return false;
 156+ case 37:// Left
 157+ case 38:// Up
 158+ case 39:// Right
 159+ case 40: //Down
 160+ return true;
 161+ default:
 162+ // Set the ignroreKeypress variable so we don't allow typing if the key is held
 163+ context.$iframe.data( 'ignoreKeypress', true );
 164+ // Can't type in a template name
 165+ event.preventDefault();
 166+ return false;
 167+ }
 168+ } else if ( $evtElem.hasClass( 'wikiEditor-template-text' ) ) {
 169+ switch ( event.which ) {
 170+ case 13: // Enter
 171+ // Ensure that the user can't break this by holding in the enter key
 172+ context.$iframe.data( 'ignoreKeypress', true );
 173+ // FIXME: May be a more elegant way to do this, but this works too
 174+ context.fn.encapsulateSelection( { 'pre': '\n', 'peri': '', 'post': '' } );
 175+ event.preventDefault();
 176+ return false;
 177+ default: return true;
 178+ }
 179+ }
 180+ },
 181+ keyup: function( context, event ) {
 182+ // Rest our ignoreKeypress variable if it's set to true
 183+ if ( context.$iframe.data( 'ignoreKeypress' ) ) {
 184+ context.$iframe.data( 'ignoreKeypress', false );
 185+ }
 186+ return true;
 187+ },
 188+ keypress: function( context, event ) {
 189+ // If this event is from a keydown event which we want to block, ignore it
 190+ return ( context.$iframe.data( 'ignoreKeypress' ) ? false : true );
 191+ }
 192+},
 193+/**
 194+ * Regular expressions that produce tokens
 195+ */
 196+exp: [
 197+ { 'regex': /{{/, 'label': "TEMPLATE_BEGIN" },
 198+ { 'regex': /}}/, 'label': "TEMPLATE_END", 'markAfter': true }
 199+],
 200+/**
 201+ * Configuration
 202+ */
 203+cfg: {
 204+},
 205+/**
 206+ * Internally used functions
 207+ */
 208+fn: {
 209+ /**
 210+ * Creates template form module within wikieditor
 211+ * @param context Context object of editor to create module in
 212+ * @param config Configuration object to create module from
 213+ */
 214+ create: function( context, config ) {
 215+ // Initialize module within the context
 216+ context.modules.templateEditor = {};
 217+ },
 218+ /**
 219+ * Turns a simple template wrapper (really just a <span>) into a complex one
 220+ * @param $wrapper Wrapping <span>
 221+ */
 222+ wrapTemplate: function( $wrapper ) {
 223+ var model = $wrapper.data( 'model' );
 224+ var context = $wrapper.data( 'marker' ).context;
 225+ var $template = $wrapper
 226+ .wrap( '<span class="wikiEditor-template"></span>' )
 227+ .addClass( 'wikiEditor-template-text wikiEditor-template-text-shrunken' )
 228+ .parent()
 229+ .addClass( 'wikiEditor-template-collapsed' )
 230+ .prepend(
 231+ '<span class="wikiEditor-template-expand wikiEditor-noinclude"></span>' +
 232+ '<span class="wikiEditor-template-name wikiEditor-noinclude">' +
 233+ '<span class="wikiEditor-template-label wikiEditor-noinclude">' +
 234+ $.wikiEditor.modules.templateEditor.fn.getTemplateDisplayName( model ) + '</span>' +
 235+ '<span class="wikiEditor-template-dialog wikiEditor-noinclude"></span>' +
 236+ '</span>'
 237+ );
 238+ },
 239+ /**
 240+ * Turn a complex template wrapper back into a simple one
 241+ * @param $wrapper Wrapping <span>
 242+ */
 243+ unwrapTemplate: function( $wrapper ) {
 244+ $wrapper.parent().replaceWith( $wrapper );
 245+ },
 246+ /**
 247+ * Bind events to a template
 248+ * @param $wrapper Original wrapper for the template to bind events to
 249+ */
 250+ bindTemplateEvents: function( $wrapper ) {
 251+ var $template = $wrapper.parent( '.wikiEditor-template' );
 252+
 253+ if ( typeof ( opera ) == "undefined" ) {
 254+ $template.parent().attr('contentEditable', 'false');
 255+ }
 256+
 257+ $template.click( function(event) {event.preventDefault(); return false;} )
 258+
 259+ $template.find( '.wikiEditor-template-name' )
 260+ .click( function( event ) {
 261+ $.wikiEditor.modules.templateEditor.fn.createDialog( $wrapper );
 262+ event.stopPropagation();
 263+ return false;
 264+ } )
 265+ .mousedown( function( event ) { event.stopPropagation(); return false; } );
 266+ $template.find( '.wikiEditor-template-expand' )
 267+ .click( function( event ) {
 268+ $.wikiEditor.modules.templateEditor.fn.toggleWikiTextEditor( $wrapper );
 269+ event.stopPropagation();
 270+ return false;
 271+ } )
 272+ .mousedown( function( event ) { event.stopPropagation(); return false; } );
 273+ },
 274+ /**
 275+ * Toggle the visisbilty of the wikitext for a given template
 276+ * @param $wrapper The origianl wrapper we want expand/collapse
 277+ */
 278+ toggleWikiTextEditor: function( $wrapper ) {
 279+ var context = $wrapper.data( 'marker' ).context;
 280+ var $template = $wrapper.parent( '.wikiEditor-template' );
 281+ context.fn.purgeOffsets();
 282+ $template
 283+ .toggleClass( 'wikiEditor-template-expanded' )
 284+ .toggleClass( 'wikiEditor-template-collapsed' ) ;
 285+
 286+ var $templateText = $template.find( '.wikiEditor-template-text' );
 287+ $templateText.toggleClass( 'wikiEditor-template-text-shrunken' );
 288+ $templateText.toggleClass( 'wikiEditor-template-text-visible' );
 289+ if( $templateText.hasClass('wikiEditor-template-text-shrunken') ){
 290+ //we just closed the template
 291+
 292+ // Update the model if we need to
 293+ if ( $templateText.html() != $templateText.data( 'oldHTML' ) ) {
 294+ var templateModel = $.wikiEditor.modules.templateEditor.fn.updateModel( $templateText );
 295+
 296+ //this is the only place the template name can be changed; keep the template name in sync
 297+ var $tLabel = $template.find( '.wikiEditor-template-label' );
 298+ $tLabel.text( $.wikiEditor.modules.templateEditor.fn.getTemplateDisplayName( templateModel ) );
 299+ }
 300+
 301+ }
 302+ },
 303+ /**
 304+ * Create a dialog for editing a given template and open it
 305+ * @param $wrapper The origianl wrapper for which to create the dialog
 306+ */
 307+ createDialog: function( $wrapper ) {
 308+ var context = $wrapper.data( 'marker' ).context;
 309+ var $template = $wrapper.parent( '.wikiEditor-template' );
 310+ var dialog = {
 311+ 'titleMsg': 'wikieditor-template-editor-dialog-title',
 312+ 'id': 'wikiEditor-template-dialog',
 313+ 'html': '\
 314+ <fieldset>\
 315+ <div class="wikiEditor-template-dialog-title" />\
 316+ <div class="wikiEditor-template-dialog-fields" />\
 317+ </fieldset>',
 318+ init: function() {
 319+ $(this).find( '[rel]' ).each( function() {
 320+ $(this).text( mw.usability.getMsg( $(this).attr( 'rel' ) ) );
 321+ } );
 322+ },
 323+ dialog: {
 324+ width: 600,
 325+ height: 400,
 326+ dialogClass: 'wikiEditor-toolbar-dialog',
 327+ buttons: {
 328+ 'wikieditor-template-editor-dialog-submit': function() {
 329+ // More user feedback
 330+ var $templateDiv = $( this ).data( 'templateDiv' );
 331+ context.fn.highlightLine( $templateDiv );
 332+
 333+ var $templateText = $templateDiv.children( '.wikiEditor-template-text' );
 334+ var templateModel = $templateText.data( 'model' );
 335+ $( this ).find( '.wikiEditor-template-dialog-field-wrapper textarea' ).each( function() {
 336+ // Update the value
 337+ templateModel.setValue( $( this ).data( 'name' ), $( this ).val() );
 338+ });
 339+ //keep text consistent
 340+ $.wikiEditor.modules.templateEditor.fn.updateModel( $templateText, templateModel );
 341+
 342+ $( this ).dialog( 'close' );
 343+ },
 344+ 'wikieditor-template-editor-dialog-cancel': function() {
 345+ $(this).dialog( 'close' );
 346+ }
 347+ },
 348+ open: function() {
 349+ var $templateDiv = $( this ).data( 'templateDiv' );
 350+ var $templateText = $templateDiv.children( '.wikiEditor-template-text' );
 351+ var templateModel = $templateText.data( 'model' );
 352+ // Update the model if we need to
 353+ if ( $templateText.html() != $templateText.data( 'oldHTML' ) ) {
 354+ templateModel = $.wikiEditor.modules.templateEditor.fn.updateModel( $templateText );
 355+ }
 356+
 357+ // Build the table
 358+ // TODO: Be smart and recycle existing table
 359+ var params = templateModel.getAllInitialParams();
 360+ var $fields = $( this ).find( '.wikiEditor-template-dialog-fields' );
 361+ // Do some bookkeeping so we can recycle existing rows
 362+ var $rows = $fields.find( '.wikiEditor-template-dialog-field-wrapper' );
 363+ for ( var paramIndex in params ) {
 364+ var param = params[paramIndex];
 365+ if ( typeof param.name == 'undefined' ) {
 366+ // param is the template name, skip it
 367+ continue;
 368+ }
 369+ var paramText = typeof param == 'string' ?
 370+ param.name.replace( /[\_\-]/g, ' ' ) :
 371+ param.name;
 372+ var paramVal = templateModel.getValue( param.name );
 373+ if ( $rows.length > 0 ) {
 374+ // We have another row to recycle
 375+ var $row = $rows.eq( 0 );
 376+ $row.children( 'label' ).text( paramText );
 377+ $row.children( 'textarea' )
 378+ .data( 'name', param.name )
 379+ .val( paramVal )
 380+ .each( function() {
 381+ $(this).css( 'height', $(this).val().length > 24 ? '4.5em' : '1.5em' );
 382+ } )
 383+ $rows = $rows.not( $row );
 384+ } else {
 385+ // Create a new row
 386+ var $paramRow = $( '<div />' )
 387+ .addClass( 'wikiEditor-template-dialog-field-wrapper' );
 388+ $( '<label />' )
 389+ .text( paramText )
 390+ .appendTo( $paramRow );
 391+ $( '<textarea />' )
 392+ .data( 'name', param.name )
 393+ .val( paramVal )
 394+ .each( function() {
 395+ $(this).css( 'height', $(this).val().length > 24 ? '4.5em' : '1.5em' );
 396+ } )
 397+ .data( 'expanded', false )
 398+ .bind( 'cut paste keypress click change', function( e ) {
 399+ // If this was fired by a tab keypress, let it go
 400+ if ( e.keyCode == '9' ) return true;
 401+ var $this = $( this );
 402+ setTimeout( function() {
 403+ var expanded = $this.data( 'expanded' );
 404+ if ( $this.val().indexOf( '\n' ) != -1 || $this.val().length > 24 ) {
 405+ if ( !expanded ) {
 406+ $this.animate( { 'height': '4.5em' }, 'fast' );
 407+ $this.data( 'expanded', true );
 408+ }
 409+ } else {
 410+ if ( expanded ) {
 411+ $this.animate( { 'height': '1.5em' }, 'fast' );
 412+ $this.data( 'expanded', false );
 413+ }
 414+ }
 415+ }, 0 );
 416+ } )
 417+ .appendTo( $paramRow );
 418+ $paramRow
 419+ .append( '<div style="clear:both"></div>' )
 420+ .appendTo( $fields );
 421+ }
 422+ }
 423+
 424+ // Remove any leftover rows
 425+ $rows.remove();
 426+ $fields.find( 'label' ).autoEllipsis();
 427+ // Ensure our close button doesn't recieve the ui-state-focus class
 428+ $( this ).parent( '.ui-dialog' ).find( '.ui-dialog-titlebar-close' )
 429+ .removeClass( 'ui-state-focus' );
 430+
 431+ // Set tabindexes on form fields if needed
 432+ // First unset the tabindexes on the buttons and existing form fields
 433+ // so the order doesn't get messed up
 434+ var $needTabindex = $( this ).closest( '.ui-dialog' ).find( 'button, textarea' );
 435+ if ( $needTabindex.not( '[tabindex]' ).length ) {
 436+ // Only do this if there actually are elements missing a tabindex
 437+ $needTabindex.removeAttr( 'tabindex' );
 438+ $.wikiEditor.modules.dialogs.fn.setTabindexes( $needTabindex );
 439+ }
 440+ }
 441+ }
 442+ };
 443+ // Lazy-create the dialog at this time
 444+ context.$textarea.wikiEditor( 'addDialog', { 'templateEditor': dialog } );
 445+ $( '#' + dialog.id )
 446+ .data( 'templateDiv', $template )
 447+ .dialog( 'open' );
 448+ },
 449+ /**
 450+ * Update a template's model and HTML
 451+ * @param $templateText Wrapper <span> containing the template text
 452+ * @param model Template model to use, will be generated if not set
 453+ * @return model object
 454+ */
 455+ updateModel: function( $templateText, model ) {
 456+ var context = $templateText.data( 'marker' ).context;
 457+ var text;
 458+ if ( typeof model == 'undefined' ) {
 459+ text = context.fn.htmlToText( $templateText.html() );
 460+ } else {
 461+ text = model.getText();
 462+ }
 463+ // To keep stuff simple but not break it, we need to do encode newlines as <br>s
 464+ $templateText.text( text );
 465+ $templateText.html( $templateText.html().replace( /\n/g, '<br />' ) );
 466+ $templateText.data( 'oldHTML', $templateText.html() );
 467+ if ( typeof model == 'undefined' ) {
 468+ model = new $.wikiEditor.modules.templateEditor.fn.model( text );
 469+ $templateText.data( 'model', model );
 470+ }
 471+ return model;
 472+ },
 473+
 474+ /**
 475+ * Gets template display name
 476+ */
 477+ getTemplateDisplayName: function ( model ) {
 478+ var tName = model.getName();
 479+ if( model.getValue( 'name' ) != '' ) {
 480+ return tName + ': ' + model.getValue( 'name' );
 481+ } else if( model.getValue( 'Name' ) != '' ) {
 482+ return tName + ': ' + model.getValue( 'Name' );
 483+ } else if( tName.toLowerCase() in $.wikiEditor.modules.templateEditor.nameMappings ) {
 484+ return tName + ': ' + model.getValue( $.wikiEditor.modules.templateEditor.nameMappings[tName.toLowerCase()] );
 485+ }
 486+ return tName;
 487+ },
 488+
 489+ /**
 490+ * Builds a template model from given wikitext representation, allowing object-oriented manipulation of the contents
 491+ * of the template while preserving whitespace and formatting.
 492+ *
 493+ * @param wikitext String of wikitext content
 494+ */
 495+ model: function( wikitext ) {
 496+
 497+ /* Private members */
 498+
 499+ var collapsible = true;
 500+
 501+ /* Private Functions */
 502+
 503+ /**
 504+ * Builds a Param object.
 505+ *
 506+ * @param name
 507+ * @param value
 508+ * @param number
 509+ * @param nameIndex
 510+ * @param equalsIndex
 511+ * @param valueIndex
 512+ */
 513+ function Param( name, value, number, nameIndex, equalsIndex, valueIndex ) {
 514+ this.name = name;
 515+ this.value = value;
 516+ this.number = number;
 517+ this.nameIndex = nameIndex;
 518+ this.equalsIndex = equalsIndex;
 519+ this.valueIndex = valueIndex;
 520+ }
 521+ /**
 522+ * Builds a Range object.
 523+ *
 524+ * @param begin
 525+ * @param end
 526+ */
 527+ function Range( begin, end ) {
 528+ this.begin = begin;
 529+ this.end = end;
 530+ }
 531+ /**
 532+ * Set 'original' to true if you want the original value irrespective of whether the model's been changed
 533+ *
 534+ * @param name
 535+ * @param value
 536+ * @param original
 537+ */
 538+ function getSetValue( name, value, original ) {
 539+ var valueRange;
 540+ var rangeIndex;
 541+ var retVal;
 542+ if ( isNaN( name ) ) {
 543+ // It's a string!
 544+ if ( typeof paramsByName[name] == 'undefined' ) {
 545+ // Does not exist
 546+ return "";
 547+ }
 548+ rangeIndex = paramsByName[name];
 549+ } else {
 550+ // It's a number!
 551+ rangeIndex = parseInt( name );
 552+ }
 553+ if ( typeof params[rangeIndex] == 'undefined' ) {
 554+ // Does not exist
 555+ return "";
 556+ }
 557+ valueRange = ranges[params[rangeIndex].valueIndex];
 558+ if ( typeof valueRange.newVal == 'undefined' || original ) {
 559+ // Value unchanged, return original wikitext
 560+ retVal = wikitext.substring( valueRange.begin, valueRange.end );
 561+ } else {
 562+ // New value exists, return new value
 563+ retVal = valueRange.newVal;
 564+ }
 565+ if ( value != null ) {
 566+ ranges[params[rangeIndex].valueIndex].newVal = value;
 567+ }
 568+ return retVal;
 569+ };
 570+
 571+ /* Public Functions */
 572+
 573+ /**
 574+ * Get template name
 575+ */
 576+ this.getName = function() {
 577+ if( typeof ranges[templateNameIndex].newVal == 'undefined' ) {
 578+ return wikitext.substring( ranges[templateNameIndex].begin, ranges[templateNameIndex].end );
 579+ } else {
 580+ return ranges[templateNameIndex].newVal;
 581+ }
 582+ };
 583+ /**
 584+ * Set template name (if we want to support this)
 585+ *
 586+ * @param name
 587+ */
 588+ this.setName = function( name ) {
 589+ ranges[templateNameIndex].newVal = name;
 590+ };
 591+ /**
 592+ * Set value for a given param name / number
 593+ *
 594+ * @param name
 595+ * @param value
 596+ */
 597+ this.setValue = function( name, value ) {
 598+ return getSetValue( name, value, false );
 599+ };
 600+ /**
 601+ * Get value for a given param name / number
 602+ *
 603+ * @param name
 604+ */
 605+ this.getValue = function( name ) {
 606+ return getSetValue( name, null, false );
 607+ };
 608+ /**
 609+ * Get original value of a param
 610+ *
 611+ * @param name
 612+ */
 613+ this.getOriginalValue = function( name ) {
 614+ return getSetValue( name, null, true );
 615+ };
 616+ /**
 617+ * Get a list of all param names (numbers for the anonymous ones)
 618+ */
 619+ this.getAllParamNames = function() {
 620+ return paramsByName;
 621+ };
 622+ /**
 623+ * Get the initial params
 624+ */
 625+ this.getAllInitialParams = function(){
 626+ return params;
 627+ }
 628+ /**
 629+ * Get original template text
 630+ */
 631+ this.getOriginalText = function() {
 632+ return wikitext;
 633+ };
 634+ /**
 635+ * Get modified template text
 636+ */
 637+ this.getText = function() {
 638+ newText = "";
 639+ for ( i = 0 ; i < ranges.length; i++ ) {
 640+ if( typeof ranges[i].newVal == 'undefined' ) {
 641+ newText += wikitext.substring( ranges[i].begin, ranges[i].end );
 642+ } else {
 643+ newText += ranges[i].newVal;
 644+ }
 645+ }
 646+ return newText;
 647+ };
 648+
 649+ this.isCollapsible = function() {
 650+ return collapsible;
 651+ }
 652+
 653+ /**
 654+ * Update ranges if there's been a change in one or more 'segments' of the template.
 655+ * Removes adjustment function so adjustment is only made once ever.
 656+ */
 657+
 658+ this.updateRanges = function() {
 659+ var adjustment = 0;
 660+ for (var i = 0 ; i < ranges.length; i++ ) {
 661+ ranges[i].begin += adjustment;
 662+ if( typeof ranges[i].adjust != 'undefined' ) {
 663+ adjustment += ranges[i].adjust();
 664+ // NOTE: adjust should be a function that has the information necessary to calculate the length of
 665+ // this 'segment'
 666+ delete ranges[i].adjust;
 667+ }
 668+ ranges[i].end += adjustment;
 669+ }
 670+ };
 671+
 672+ // Whitespace* {{ whitespace* nonwhitespace:
 673+ if ( wikitext.match( /\s*{{\s*[^\s|]*:/ ) ) {
 674+ collapsible = false; // is a parser function
 675+ }
 676+ /*
 677+ * Take all template-specific characters that are not particular to the template we're looking at, namely {|=},
 678+ * and convert them into something harmless, in this case 'X'
 679+ */
 680+ // Get rid of first {{ with whitespace
 681+ var sanatizedStr = wikitext.replace( /{{/, " " );
 682+ // Replace end
 683+ endBraces = sanatizedStr.match( /}}\s*$/ );
 684+ if ( endBraces ) {
 685+ sanatizedStr = sanatizedStr.substring( 0, endBraces.index ) + " " +
 686+ sanatizedStr.substring( endBraces.index + 2 );
 687+ }
 688+
 689+
 690+ //treat HTML comments like whitespace
 691+ while ( sanatizedStr.indexOf( '<!' ) != -1 ) {
 692+ startIndex = sanatizedStr.indexOf( '<!' );
 693+ endIndex = sanatizedStr.indexOf('-->') + 3;
 694+ if( endIndex < 3 ){
 695+ break;
 696+ }
 697+ sanatizedSegment = sanatizedStr.substring( startIndex,endIndex ).replace( /\S/g , ' ' );
 698+ sanatizedStr =
 699+ sanatizedStr.substring( 0, startIndex ) + sanatizedSegment + sanatizedStr.substring( endIndex );
 700+ }
 701+
 702+ // Match the open braces we just found with equivalent closing braces note, works for any level of braces
 703+ while ( sanatizedStr.indexOf( '{{' ) != -1 ) {
 704+ startIndex = sanatizedStr.indexOf( '{{' ) + 1;
 705+ openBraces = 2;
 706+ endIndex = startIndex;
 707+ while ( (openBraces > 0) && (endIndex < sanatizedStr.length) ) {
 708+ var brace = sanatizedStr[++endIndex];
 709+ openBraces += brace == '}' ? -1 : brace == '{' ? 1 : 0;
 710+ }
 711+ sanatizedSegment = sanatizedStr.substring( startIndex,endIndex ).replace( /[{}|=]/g , 'X' );
 712+ sanatizedStr =
 713+ sanatizedStr.substring( 0, startIndex ) + sanatizedSegment + sanatizedStr.substring( endIndex );
 714+ }
 715+ //links, images, etc, which also can nest
 716+ while ( sanatizedStr.indexOf( '[[' ) != -1 ) {
 717+ startIndex = sanatizedStr.indexOf( '[[' ) + 1;
 718+ openBraces = 2;
 719+ endIndex = startIndex;
 720+ while ( (openBraces > 0) && (endIndex < sanatizedStr.length) ) {
 721+ var brace = sanatizedStr[++endIndex];
 722+ openBraces += brace == ']' ? -1 : brace == '[' ? 1 : 0;
 723+ }
 724+ sanatizedSegment = sanatizedStr.substring( startIndex,endIndex ).replace( /[\[\]|=]/g , 'X' );
 725+ sanatizedStr =
 726+ sanatizedStr.substring( 0, startIndex ) + sanatizedSegment + sanatizedStr.substring( endIndex );
 727+ }
 728+
 729+ /*
 730+ * Parse 1 param at a time
 731+ */
 732+ var ranges = [];
 733+ var params = [];
 734+ var templateNameIndex = 0;
 735+ var doneParsing = false;
 736+ oldDivider = 0;
 737+ divider = sanatizedStr.indexOf( '|', oldDivider );
 738+ if ( divider == -1 ) {
 739+ divider = sanatizedStr.length;
 740+ doneParsing = true;
 741+ collapsible = false; //zero params
 742+ }
 743+ nameMatch = sanatizedStr.substring( 0, divider ).match( /[^\s]/ );
 744+ if ( nameMatch != null ) {
 745+ ranges.push( new Range( 0 ,nameMatch.index ) ); //whitespace and squiggles upto the name
 746+ nameEndMatch = sanatizedStr.substring( 0 , divider ).match( /[^\s]\s*$/ ); //last nonwhitespace character
 747+ templateNameIndex = ranges.push( new Range( nameMatch.index,
 748+ nameEndMatch.index + 1 ) );
 749+ templateNameIndex--; //push returns 1 less than the array
 750+ ranges[templateNameIndex].old = wikitext.substring( ranges[templateNameIndex].begin,
 751+ ranges[templateNameIndex].end );
 752+ } else {
 753+ ranges.push(new Range(0,0));
 754+ ranges[templateNameIndex].old = "";
 755+ }
 756+ params.push( ranges[templateNameIndex].old ); //put something in params (0)
 757+ /*
 758+ * Start looping over params
 759+ */
 760+ var currentParamNumber = 0;
 761+ var valueEndIndex = ranges[templateNameIndex].end;
 762+ var paramsByName = [];
 763+ while ( !doneParsing ) {
 764+ currentParamNumber++;
 765+ oldDivider = divider;
 766+ divider = sanatizedStr.indexOf( '|', oldDivider + 1 );
 767+ if ( divider == -1 ) {
 768+ divider = sanatizedStr.length;
 769+ doneParsing = true;
 770+ }
 771+ currentField = sanatizedStr.substring( oldDivider+1, divider );
 772+ if ( currentField.indexOf( '=' ) == -1 ) {
 773+ // anonymous field, gets a number
 774+
 775+ //default values, since we'll allow empty values
 776+ valueBeginIndex = oldDivider + 1;
 777+ valueEndIndex = oldDivider + 1;
 778+
 779+ valueBegin = currentField.match( /\S+/ ); //first nonwhitespace character
 780+ if( valueBegin != null ){
 781+ valueBeginIndex = valueBegin.index + oldDivider+1;
 782+ valueEnd = currentField.match( /[^\s]\s*$/ ); //last nonwhitespace character
 783+ if( valueEnd == null ){ //ie
 784+ continue;
 785+ }
 786+ valueEndIndex = valueEnd.index + oldDivider + 2;
 787+ }
 788+ ranges.push( new Range( ranges[ranges.length-1].end,
 789+ valueBeginIndex ) ); //all the chars upto now
 790+ nameIndex = ranges.push( new Range( valueBeginIndex, valueBeginIndex ) ) - 1;
 791+ equalsIndex = ranges.push( new Range( valueBeginIndex, valueBeginIndex ) ) - 1;
 792+ valueIndex = ranges.push( new Range( valueBeginIndex, valueEndIndex ) ) - 1;
 793+ params.push( new Param(
 794+ currentParamNumber,
 795+ wikitext.substring( ranges[valueIndex].begin, ranges[valueIndex].end ),
 796+ currentParamNumber,
 797+ nameIndex,
 798+ equalsIndex,
 799+ valueIndex
 800+ ) );
 801+ paramsByName[currentParamNumber] = currentParamNumber;
 802+ } else {
 803+ // There's an equals, could be comment or a value pair
 804+ currentName = currentField.substring( 0, currentField.indexOf( '=' ) );
 805+ // Still offset by oldDivider - first nonwhitespace character
 806+ nameBegin = currentName.match( /\S+/ );
 807+ if ( nameBegin == null ) {
 808+ // This is a comment inside a template call / parser abuse. let's not encourage it
 809+ currentParamNumber--;
 810+ continue;
 811+ }
 812+ nameBeginIndex = nameBegin.index + oldDivider + 1;
 813+ // Last nonwhitespace and non } character
 814+ nameEnd = currentName.match( /[^\s]\s*$/ );
 815+ if( nameEnd == null ){ //ie
 816+ continue;
 817+ }
 818+ nameEndIndex = nameEnd.index + oldDivider + 2;
 819+ // All the chars upto now
 820+ ranges.push( new Range( ranges[ranges.length-1].end, nameBeginIndex ) );
 821+ nameIndex = ranges.push( new Range( nameBeginIndex, nameEndIndex ) ) - 1;
 822+ currentValue = currentField.substring( currentField.indexOf( '=' ) + 1);
 823+ oldDivider += currentField.indexOf( '=' ) + 1;
 824+
 825+ //default values, since we'll allow empty values
 826+ valueBeginIndex = oldDivider + 1;
 827+ valueEndIndex = oldDivider + 1;
 828+
 829+ // First nonwhitespace character
 830+ valueBegin = currentValue.match( /\S+/ );
 831+ if( valueBegin != null ){
 832+ valueBeginIndex = valueBegin.index + oldDivider + 1;
 833+ // Last nonwhitespace and non } character
 834+ valueEnd = currentValue.match( /[^\s]\s*$/ );
 835+ if( valueEnd == null ){ //ie
 836+ continue;
 837+ }
 838+ valueEndIndex = valueEnd.index + oldDivider + 2;
 839+ }
 840+ // All the chars upto now
 841+ equalsIndex = ranges.push( new Range( ranges[ranges.length-1].end, valueBeginIndex) ) - 1;
 842+ valueIndex = ranges.push( new Range( valueBeginIndex, valueEndIndex ) ) - 1;
 843+ params.push( new Param(
 844+ wikitext.substring( nameBeginIndex, nameEndIndex ),
 845+ wikitext.substring( valueBeginIndex, valueEndIndex ),
 846+ currentParamNumber,
 847+ nameIndex,
 848+ equalsIndex,
 849+ valueIndex
 850+ ) );
 851+ paramsByName[wikitext.substring( nameBeginIndex, nameEndIndex )] = currentParamNumber;
 852+ }
 853+ }
 854+ // The rest of the string
 855+ ranges.push( new Range( valueEndIndex, wikitext.length ) );
 856+
 857+ // Save vars
 858+ this.ranges = ranges;
 859+ this.wikitext = wikitext;
 860+ this.params = params;
 861+ this.paramsByName = paramsByName;
 862+ this.templateNameIndex = templateNameIndex;
 863+ } //model
 864+}
 865+}; } )( jQuery );
Property changes on: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.templateEditor.js
___________________________________________________________________
Added: svn:eol-style
1866 + native
Index: trunk/extensions/WikiEditor/modules/AddMediaWizard.js
@@ -0,0 +1,10 @@
 2+/* JavaScript for AddMediaWizard gadget */
 3+if ( wgWikiEditorEnabledModules.addMediaWizard ) {
 4+ if( typeof mwAddMediaConfig == 'undefined' ) {
 5+ mwAddMediaConfig = {};
 6+ }
 7+ mwAddMediaConfig['enabled_providers'] = [ 'wiki_commons', 'upload' ];
 8+
 9+ // Transclude mwEmbed support
 10+ importScriptURI( 'http://prototype.wikimedia.org/s-2/js/mwEmbed/remotes/mediaWiki.js?&uselang=' + wgUserLanguage );
 11+}
Property changes on: trunk/extensions/WikiEditor/modules/AddMediaWizard.js
___________________________________________________________________
Added: svn:eol-style
112 + native
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.js
@@ -0,0 +1,213 @@
 2+/**
 3+ * Extend the RegExp object with an escaping function
 4+ * From http://simonwillison.net/2006/Jan/20/escape/
 5+ */
 6+RegExp.escape = function( s ) { return s.replace(/([.*+?^${}()|\/\\[\]])/g, '\\$1'); };
 7+
 8+/**
 9+ * Dialog Module for wikiEditor
 10+ */
 11+( function( $ ) { $.wikiEditor.modules.dialogs = {
 12+
 13+/**
 14+ * Compatability map
 15+ */
 16+'browsers': {
 17+ // Left-to-right languages
 18+ 'ltr': {
 19+ 'msie': [['>=', 7]],
 20+ // jQuery UI appears to be broken in FF 2.0 - 2.0.0.4
 21+ 'firefox': [
 22+ ['>=', 2], ['!=', '2.0'], ['!=', '2.0.0.1'], ['!=', '2.0.0.2'], ['!=', '2.0.0.3'], ['!=', '2.0.0.4']
 23+ ],
 24+ 'opera': [['>=', 9.6]],
 25+ 'safari': [['>=', 3]],
 26+ 'chrome': [['>=', 3]]
 27+ },
 28+ // Right-to-left languages
 29+ 'rtl': {
 30+ 'msie': [['>=', 7]],
 31+ // jQuery UI appears to be broken in FF 2.0 - 2.0.0.4
 32+ 'firefox': [
 33+ ['>=', 2], ['!=', '2.0'], ['!=', '2.0.0.1'], ['!=', '2.0.0.2'], ['!=', '2.0.0.3'], ['!=', '2.0.0.4']
 34+ ],
 35+ 'opera': [['>=', 9.6]],
 36+ 'safari': [['>=', 3]],
 37+ 'chrome': [['>=', 3]]
 38+ }
 39+},
 40+/**
 41+ * API accessible functions
 42+ */
 43+api: {
 44+ addDialog: function( context, data ) {
 45+ $.wikiEditor.modules.dialogs.fn.create( context, data )
 46+ },
 47+ openDialog: function( context, module ) {
 48+ mw.usability.load( [ '$j.ui', '$j.ui.dialog', '$j.ui.draggable', '$j.ui.resizable' ], function() {
 49+ if ( module in $.wikiEditor.modules.dialogs.modules ) {
 50+ var mod = $.wikiEditor.modules.dialogs.modules[module];
 51+ var $dialog = $( '#' + mod.id );
 52+ if ( $dialog.length == 0 ) {
 53+ $.wikiEditor.modules.dialogs.fn.reallyCreate( context, mod );
 54+ $dialog = $( '#' + mod.id );
 55+ }
 56+
 57+ // Workaround for bug in jQuery UI: close button in top right retains focus
 58+ $dialog.closest( '.ui-dialog' )
 59+ .find( '.ui-dialog-titlebar-close' )
 60+ .removeClass( 'ui-state-focus' );
 61+
 62+ $dialog.dialog( 'open' );
 63+ }
 64+ } );
 65+ },
 66+ closeDialog: function( context, module ) {
 67+ if ( module in $.wikiEditor.modules.dialogs.modules ) {
 68+ $( '#' + $.wikiEditor.modules.dialogs.modules[module].id ).dialog( 'close' );
 69+ }
 70+ }
 71+},
 72+/**
 73+ * Internally used functions
 74+ */
 75+fn: {
 76+ /**
 77+ * Creates a dialog module within a wikiEditor
 78+ *
 79+ * @param {Object} context Context object of editor to create module in
 80+ * @param {Object} config Configuration object to create module from
 81+ */
 82+ create: function( context, config ) {
 83+ // Defer building of modules, but do check whether they need the iframe rightaway
 84+ for ( mod in config ) {
 85+ var module = config[mod];
 86+ // Only create the dialog if it's supported, isn't filtered and doesn't exist yet
 87+ var filtered = false;
 88+ if ( typeof module.filters != 'undefined' ) {
 89+ for ( var i = 0; i < module.filters.length; i++ ) {
 90+ if ( $( module.filters[i] ).length == 0 ) {
 91+ filtered = true;
 92+ break;
 93+ }
 94+ }
 95+ }
 96+ if ( !filtered && $.wikiEditor.isSupported( module ) && $( '#' + module.id ).size() == 0 ) {
 97+ $.wikiEditor.modules.dialogs.modules[mod] = module;
 98+ // If this dialog requires the iframe, set it up
 99+ if ( typeof context.$iframe == 'undefined' && $.wikiEditor.isRequired( module, 'iframe' ) ) {
 100+ context.fn.setupIframe();
 101+ }
 102+ context.$textarea.trigger( 'wikiEditor-dialogs-setup-' + mod );
 103+ }
 104+ }
 105+ },
 106+ /**
 107+ * Build the actual dialog. This done on-demand rather than in create()
 108+ * @param {Object} context Context object of editor dialog belongs to
 109+ * @param {Object} module Dialog module object
 110+ */
 111+ reallyCreate: function( context, module ) {
 112+ var configuration = module.dialog;
 113+ // Add some stuff to configuration
 114+ configuration.bgiframe = true;
 115+ configuration.autoOpen = false;
 116+ configuration.modal = true;
 117+ configuration.title = $.wikiEditor.autoMsg( module, 'title' );
 118+ // Transform messages in keys
 119+ // Stupid JS won't let us do stuff like
 120+ // foo = { mw.usability.getMsg( 'bar' ): baz }
 121+ configuration.newButtons = {};
 122+ for ( msg in configuration.buttons )
 123+ configuration.newButtons[mw.usability.getMsg( msg )] = configuration.buttons[msg];
 124+ configuration.buttons = configuration.newButtons;
 125+ // Create the dialog <div>
 126+ var dialogDiv = $( '<div />' )
 127+ .attr( 'id', module.id )
 128+ .html( module.html )
 129+ .data( 'context', context )
 130+ .appendTo( $( 'body' ) )
 131+ .each( module.init )
 132+ .dialog( configuration );
 133+ // Set tabindexes on buttons added by .dialog()
 134+ $.wikiEditor.modules.dialogs.fn.setTabindexes( dialogDiv.closest( '.ui-dialog' )
 135+ .find( 'button' ).not( '[tabindex]' ) );
 136+ if ( !( 'resizeme' in module ) || module.resizeme ) {
 137+ dialogDiv
 138+ .bind( 'dialogopen', $.wikiEditor.modules.dialogs.fn.resize )
 139+ .find( '.ui-tabs' ).bind( 'tabsshow', function() {
 140+ $(this).closest( '.ui-dialog-content' ).each(
 141+ $.wikiEditor.modules.dialogs.fn.resize );
 142+ });
 143+ }
 144+ dialogDiv.bind( 'dialogclose', function() {
 145+ context.fn.restoreSelection();
 146+ } );
 147+
 148+ // Let the outside world know we set up this dialog
 149+ context.$textarea.trigger( 'wikiEditor-dialogs-loaded-' + mod );
 150+ },
 151+ /**
 152+ * Resize a dialog so its contents fit
 153+ *
 154+ * Usage: dialog.each( resize ); or dialog.bind( 'blah', resize );
 155+ * NOTE: This function assumes $j.ui.dialog has already been loaded
 156+ */
 157+ resize: function() {
 158+ var wrapper = $(this).closest( '.ui-dialog' );
 159+ var oldWidth = wrapper.width();
 160+ // Make sure elements don't wrapped so we get an accurate idea of whether they really fit. Also temporarily show
 161+ // hidden elements. Work around jQuery bug where <div style="display:inline;" /> inside a dialog is both
 162+ // :visible and :hidden
 163+ var oldHidden = $(this).find( '*' ).not( ':visible' );
 164+ // Save the style attributes of the hidden elements to restore them later. Calling hide() after show() messes up
 165+ // for elements hidden with a class
 166+ oldHidden.each( function() {
 167+ $(this).data( 'oldstyle', $(this).attr( 'style' ) );
 168+ });
 169+ oldHidden.show();
 170+ var oldWS = $(this).css( 'white-space' );
 171+ $(this).css( 'white-space', 'nowrap' );
 172+ if ( wrapper.width() <= $(this).get(0).scrollWidth ) {
 173+ var thisWidth = $(this).data( 'thisWidth' ) ? $(this).data( 'thisWidth' ) : 0;
 174+ thisWidth = Math.max( $(this).get(0).scrollWidth, thisWidth );
 175+ $(this).width( thisWidth );
 176+ $(this).data( 'thisWidth', thisWidth );
 177+ var wrapperWidth = $(this).data( 'wrapperWidth' ) ? $(this).data( 'wrapperWidth' ) : 0;
 178+ wrapperWidth = Math.max( wrapper.get(0).scrollWidth, wrapperWidth );
 179+ wrapper.width( wrapperWidth );
 180+ $(this).data( 'wrapperWidth', wrapperWidth );
 181+ $(this).dialog( { 'width': wrapper.width() } );
 182+ wrapper.css( 'left', parseInt( wrapper.css( 'left' ) ) - ( wrapper.width() - oldWidth ) / 2 );
 183+ }
 184+ $(this).css( 'white-space', oldWS );
 185+ oldHidden.each( function() {
 186+ $(this).attr( 'style', $(this).data( 'oldstyle' ) );
 187+ });
 188+ },
 189+ /**
 190+ * Set the right tabindexes on elements in a dialog
 191+ * @param $elements Elements to set tabindexes on. If they already have tabindexes, this function can behave a bit weird
 192+ */
 193+ setTabindexes: function( $elements ) {
 194+ // Get the highest tab index
 195+ var tabIndex = mw.usability.getMaxTabIndex() + 1;
 196+ $elements.each( function() {
 197+ $j(this).attr( 'tabindex', tabIndex++ );
 198+ } );
 199+ }
 200+},
 201+// This stuff is just hanging here, perhaps we could come up with a better home for this stuff
 202+modules: {},
 203+quickDialog: function( body, settings ) {
 204+ $( '<div />' )
 205+ .text( body )
 206+ .appendTo( $( 'body' ) )
 207+ .dialog( $.extend( {
 208+ bgiframe: true,
 209+ modal: true
 210+ }, settings ) )
 211+ .dialog( 'open' );
 212+}
 213+
 214+}; } ) ( jQuery );
Property changes on: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.js
___________________________________________________________________
Added: svn:eol-style
1215 + native
Index: trunk/extensions/WikiEditor/modules/wikiEditor.toc.css
@@ -0,0 +1,217 @@
 2+/* wikiEditor toc module */
 3+.wikiEditor-ui-toc {
 4+ /* height and width are set dynamically */
 5+ /*float: right;*/
 6+ padding: 0;
 7+ overflow: auto;
 8+ overflow-x: hidden;
 9+}
 10+.wikiEditor-ui-toc {
 11+ border-left: solid silver 1px;
 12+}
 13+body.rtl .wikiEditor-ui-toc {
 14+ border-right: solid silver 1px;
 15+ border-left: none;
 16+}
 17+.wikiEditor-ui-toc ul {
 18+ padding: 0;
 19+ margin: 0;
 20+ list-style: none;
 21+ /* IE needs to be told in great detail how to act, or it misbehaves */
 22+ list-style-image: none;
 23+ list-style-position: outside;
 24+ list-style-type: none;
 25+ width: 100%;
 26+}
 27+.tab-toc {
 28+ /* Should match the toolbar */
 29+ background-image: url(../images/wikiEditor/toolbar/base.png?1);
 30+ background-position: left top;
 31+ background-repeat: repeat-x;
 32+ height: 26px;
 33+ padding: 3px 0;
 34+ line-height: 26px;
 35+ padding-left: 1em;
 36+ border-bottom: solid 1px silver;
 37+ white-space: nowrap;
 38+ overflow: hidden;
 39+}
 40+body.rtl .tab-toc {
 41+ background-position: right top;
 42+ padding-right: 2em;
 43+}
 44+.tab-toc a {
 45+ outline: none;
 46+}
 47+.wikiEditor-ui-toc li {
 48+ padding: 0;
 49+ margin: 0;
 50+}
 51+.wikiEditor-ui-toc ul ul {
 52+ padding: 0;
 53+ margin: 0;
 54+ margin-bottom: 0 !important;
 55+ margin-top: 0 !important;
 56+ list-style: none;
 57+ background-image: none;
 58+}
 59+.wikiEditor-ui-toc ul li div {
 60+ display: block;
 61+ font-size: 0.9em;
 62+ cursor: pointer;
 63+ color: #0645ad;
 64+}
 65+.wikiEditor-ui-toc ul li div {
 66+ padding: 0.125em;
 67+ padding-left: 1em;
 68+}
 69+body.rtl .wikiEditor-ui-toc ul li div {
 70+ padding-right: 1em;
 71+ padding-left: 0;
 72+}
 73+.wikiEditor-ui-toc ul ul li div {
 74+ padding-left: 2em;
 75+}
 76+body.rtl .wikiEditor-ui-toc ul ul li div {
 77+ padding-right: 2em;
 78+ padding-left: 0;
 79+}
 80+.wikiEditor-ui-toc ul ul ul li div {
 81+ padding-left: 3em;
 82+}
 83+body.rtl .wikiEditor-ui-toc ul ul ul li div {
 84+ padding-right: 3em;
 85+ padding-left: 0;
 86+}
 87+.wikiEditor-ui-toc ul ul ul ul li div {
 88+ padding-left: 4em;
 89+}
 90+body.rtl .wikiEditor-ui-toc ul ul ul ul li div {
 91+ padding-right: 4em;
 92+ padding-left: 0;
 93+}
 94+.wikiEditor-ui-toc ul ul ul ul ul li div {
 95+ padding-left: 5em;
 96+}
 97+body.rtl .wikiEditor-ui-toc ul ul ul ul ul li div {
 98+ padding-right: 5em;
 99+ padding-left: 0;
 100+}
 101+.wikiEditor-ui-toc ul ul ul ul ul ul li div {
 102+ padding-left: 6em;
 103+}
 104+body.rtl wikiEditor-ui-toc ul ul ul ul ul ul li div {
 105+ padding-right: 6em;
 106+ padding-left: 0;
 107+}
 108+.wikiEditor-ui-toc ul li div.current {
 109+ background-color: #FAFAFA;
 110+ color: #333333;
 111+}
 112+.wikiEditor-ui-toc ul li div.section-0 {
 113+ font-size: 1em;
 114+ padding-top: 0.5em;
 115+ padding-bottom: 0.5em;
 116+ border-bottom: solid 1px #DDDDDD;
 117+}
 118+
 119+/* Collapsing changes */
 120+
 121+.wikiEditor-ui-toc {
 122+ overflow-y: hidden;
 123+ position: relative;
 124+}
 125+.wikiEditor-ui-toc ul {
 126+ overflow-y: auto;
 127+ overflow-x: hidden;
 128+ height: 100%;
 129+ margin-bottom: 0 !important;
 130+
 131+}
 132+
 133+.wikiEditor-ui-toc ul ul {
 134+ float: none;
 135+ height: auto;
 136+}
 137+#wikiEditor-ui-toc-collapse {
 138+ height: 100%;
 139+ width: 18px;
 140+ position: absolute;
 141+ top: 0;
 142+ left: 0;
 143+}
 144+.wikiEditor-ui-toc-collapse-open {
 145+ background: #f3f3f3 url(../images/wikiEditor/toc/close.png?1) 4px 50% no-repeat;
 146+ border-left: 1px solid #DDDDDD;
 147+}
 148+.wikiEditor-ui-toc-collapse-closed {
 149+ background: #f3f3f3 url(../images/wikiEditor/toc/open.png?1) 4px 50% no-repeat;
 150+}
 151+
 152+/* Resizing Changes */
 153+.wikiEditor-ui-toc-resize-vertical,
 154+.ui-resizable-w {
 155+ width: 4px;
 156+ position: absolute;
 157+ top: 0;
 158+ left: 0;
 159+ height: 100%;
 160+ cursor: ew-resize;
 161+}
 162+.wikiEditor-ui .wikiEditor-ui-right {
 163+ overflow: visible;
 164+}
 165+.wikiEditor-ui-right .ui-resizable-w {
 166+ left: 0px !important;
 167+ z-index: 0;
 168+}
 169+
 170+.wikiEditor-ui-right .wikiEditor-ui-toc-resize-grip {
 171+ width: 5px;
 172+ height: 12px;
 173+ padding: 3px;
 174+ position: absolute;
 175+ top: 7px;
 176+ left: -12px !important;
 177+ cursor: ew-resize;
 178+ background: url(../images/wikiEditor/toc/grip.png?1) 50% 50% no-repeat;
 179+ z-index: 0;
 180+}
 181+body.rtl .wikiEditor-ui-right .wikiEditor-ui-toc-resize-grip {
 182+ right: 2px !important;
 183+}
 184+.wikiEditor-ui-toolbar .tab-toc {
 185+ float: right;
 186+ margin: 3px 16px 3px 3px;
 187+ line-height: 26px;
 188+}
 189+.wikiEditor-ui-toc-expandControl {
 190+ position: absolute;
 191+ z-index: 2;
 192+ top: 0px;
 193+ right: 10px;
 194+ height: 26px;
 195+ padding: 3px 0;
 196+ line-height: 26px;
 197+ padding-right: 1em;
 198+ white-space: nowrap;
 199+ overflow: hidden;
 200+}
 201+body.rtl .wikiEditor-ui-toc-expandControl {
 202+ padding-left: 1em;
 203+ padding-right: 0;
 204+ left: 10px;
 205+ right: auto;
 206+}
 207+.wikiEditor-ui-text textarea {
 208+ resize: none;
 209+}
 210+.wikiEditor-ui-text textarea:focus {
 211+ outline: none;
 212+}
 213+
 214+/* Self Clearing for the wikiText view */
 215+.wikiEditor-ui-view-wikiText {
 216+ overflow: auto;
 217+ width: 100%;
 218+}
\ No newline at end of file
Property changes on: trunk/extensions/WikiEditor/modules/wikiEditor.toc.css
___________________________________________________________________
Added: svn:eol-style
1219 + native
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.toc.js
@@ -0,0 +1,669 @@
 2+/* TOC Module for wikiEditor */
 3+( function( $ ) { $.wikiEditor.modules.toc = {
 4+
 5+/**
 6+ * Compatability map
 7+ */
 8+'browsers': {
 9+ // Left-to-right languages
 10+ 'ltr': {
 11+ 'msie': [['>=', 7]],
 12+ 'firefox': [['>=', 3]],
 13+ 'opera': [['>=', 10]],
 14+ 'safari': [['>=', 4]],
 15+ 'chrome': [['>=', 4]]
 16+ },
 17+ // Right-to-left languages
 18+ 'rtl': {
 19+ 'msie': [['>=', 8]],
 20+ 'firefox': [['>=', 3]],
 21+ 'opera': [['>=', 10]],
 22+ 'safari': [['>=', 4]],
 23+ 'chrome': [['>=', 4]]
 24+ }
 25+},
 26+/**
 27+ * Core Requirements
 28+ */
 29+'req': [ 'iframe' ],
 30+/**
 31+ * Configuration
 32+ */
 33+cfg: {
 34+ // Default width of table of contents
 35+ defaultWidth: '166px',
 36+ // Minimum width to allow resizing to before collapsing the table of contents - used when resizing and collapsing
 37+ minimumWidth: '70px',
 38+ // Minimum width of the wikiText area
 39+ textMinimumWidth: '450px',
 40+ // The style property to be used for positioning the flexible module in regular mode
 41+ flexProperty: 'marginRight',
 42+ // Boolean var indicating text direction
 43+ rtl: false
 44+},
 45+/**
 46+ * API accessible functions
 47+ */
 48+api: {
 49+ //
 50+},
 51+/**
 52+ * Event handlers
 53+ */
 54+evt: {
 55+ change: function( context, event ) {
 56+ $.wikiEditor.modules.toc.fn.update( context );
 57+ },
 58+ ready: function( context, event ) {
 59+ // Add the TOC to the document
 60+ $.wikiEditor.modules.toc.fn.build( context );
 61+ if ( !context.$content ) {
 62+ return;
 63+ }
 64+ context.$content.parent()
 65+ .blur( function() {
 66+ var context = event.data.context;
 67+ $.wikiEditor.modules.toc.fn.unhighlight( context );
 68+ });
 69+ $.wikiEditor.modules.toc.fn.improveUI();
 70+ $.wikiEditor.modules.toc.evt.resize( context );
 71+ },
 72+ resize: function( context, event ) {
 73+ var availableWidth = context.$wikitext.width() - parseFloat( $.wikiEditor.modules.toc.cfg.textMinimumWidth ),
 74+ totalMinWidth = parseFloat( $.wikiEditor.modules.toc.cfg.minimumWidth ) +
 75+ parseFloat( $.wikiEditor.modules.toc.cfg.textMinimumWidth );
 76+ context.$ui.find( '.wikiEditor-ui-right' )
 77+ .resizable( 'option', 'maxWidth', availableWidth );
 78+ if ( context.modules.toc.$toc.data( 'positionMode' ) != 'disabled' &&
 79+ context.$wikitext.width() < totalMinWidth ) {
 80+ $.wikiEditor.modules.toc.fn.disable( context );
 81+ } else if ( context.modules.toc.$toc.data( 'positionMode' ) == 'disabled' &&
 82+ context.$wikitext.width() > totalMinWidth ) {
 83+ $.wikiEditor.modules.toc.fn.enable( context );
 84+ } else if ( context.modules.toc.$toc.data( 'positionMode' ) == 'regular' &&
 85+ context.$ui.find( '.wikiEditor-ui-right' ).width() > availableWidth ) {
 86+ //switch mode
 87+ $.wikiEditor.modules.toc.fn.switchLayout( context );
 88+ } else if ( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' &&
 89+ context.modules.toc.$toc.data( 'previousWidth' ) < context.$wikitext.width() ) {
 90+ //switch mode
 91+ $.wikiEditor.modules.toc.fn.switchLayout( context );
 92+ }
 93+ if ( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
 94+ context.modules.toc.$toc.find( 'div' ).autoEllipsis(
 95+ { 'position': 'right', 'tooltip': true, 'restoreText': true }
 96+ );
 97+ }
 98+ // reset the height of the TOC
 99+ if ( !context.modules.toc.$toc.data( 'collapsed' ) ){
 100+ context.modules.toc.$toc.height(
 101+ context.$ui.find( '.wikiEditor-ui-left' ).height() -
 102+ context.$ui.find( '.tab-toc' ).outerHeight()
 103+ );
 104+ }
 105+
 106+ // store the width of the view for comparison on next resize
 107+ context.modules.toc.$toc.data( 'previousWidth', context.$wikitext.width() );
 108+ },
 109+ mark: function( context, event ) {
 110+ var hash = '';
 111+ var markers = context.modules.highlight.markers;
 112+ var tokenArray = context.modules.highlight.tokenArray;
 113+ var outline = context.data.outline = [];
 114+ var h = 0;
 115+ for ( var i = 0; i < tokenArray.length; i++ ) {
 116+ if ( tokenArray[i].label != 'TOC_HEADER' ) {
 117+ continue;
 118+ }
 119+ h++;
 120+ markers.push( {
 121+ index: h,
 122+ start: tokenArray[i].tokenStart,
 123+ end: tokenArray[i].offset,
 124+ type: 'toc',
 125+ anchor: 'tag',
 126+ afterWrap: function( node ) {
 127+ var marker = $( node ).data( 'marker' );
 128+ $( node ).addClass( 'wikiEditor-toc-header' )
 129+ .addClass( 'wikiEditor-toc-section-' + marker.index )
 130+ .data( 'section', marker.index );
 131+ },
 132+ beforeUnwrap: function( node ) {
 133+ $( node ).removeClass( 'wikiEditor-toc-header' )
 134+ .removeClass( 'wikiEditor-toc-section-' + $( node ).data( 'section' ) );
 135+ },
 136+ onSkip: function( node ) {
 137+ var marker = $( node ).data( 'marker' );
 138+ if ( $( node ).data( 'section' ) != marker.index ) {
 139+ $( node )
 140+ .removeClass( 'wikiEditor-toc-section-' + $( node ).data( 'section' ) )
 141+ .addClass( 'wikiEditor-toc-section-' + marker.index )
 142+ .data( 'section', marker.index );
 143+ }
 144+ },
 145+ getAnchor: function( ca1, ca2 ) {
 146+ return $( ca1.parentNode ).is( '.wikiEditor-toc-header' ) ?
 147+ ca1.parentNode : null;
 148+ }
 149+ } );
 150+ hash += tokenArray[i].match[2] + '\n';
 151+ outline.push ( {
 152+ 'text': tokenArray[i].match[2],
 153+ 'level': tokenArray[i].match[1].length,
 154+ 'index': h
 155+ } );
 156+ }
 157+ // Only update the TOC if it's been changed - we do this by comparing a hash of the headings this time to last
 158+ if ( typeof context.modules.toc.lastHash == 'undefined' || context.modules.toc.lastHash !== hash ) {
 159+ $.wikiEditor.modules.toc.fn.build( context );
 160+ $.wikiEditor.modules.toc.fn.update( context );
 161+ // Remember the changed version
 162+ context.modules.toc.lastHash = hash;
 163+ }
 164+ }
 165+},
 166+exp: [
 167+ { 'regex': /^(={1,6})([^\r\n]+?)\1\s*$/m, 'label': 'TOC_HEADER', 'markAfter': true }
 168+],
 169+/**
 170+ * Internally used functions
 171+ */
 172+fn: {
 173+ /**
 174+ * Creates a table of contents module within a wikiEditor
 175+ *
 176+ * @param {Object} context Context object of editor to create module in
 177+ * @param {Object} config Configuration object to create module from
 178+ */
 179+ create: function( context, config ) {
 180+ if ( '$toc' in context.modules.toc ) {
 181+ return;
 182+ }
 183+ $.wikiEditor.modules.toc.cfg.rtl = config.rtl;
 184+ $.wikiEditor.modules.toc.cfg.flexProperty = config.rtl ? 'marginLeft' : 'marginRight';
 185+ var height = context.$ui.find( '.wikiEditor-ui-left' ).height();
 186+ context.modules.toc.$toc = $( '<div />' )
 187+ .addClass( 'wikiEditor-ui-toc' )
 188+ .data( 'context', context )
 189+ .data( 'positionMode', 'regular' )
 190+ .data( 'collapsed', false );
 191+ context.$ui.find( '.wikiEditor-ui-right' )
 192+ .append( context.modules.toc.$toc );
 193+ context.modules.toc.$toc.height(
 194+ context.$ui.find( '.wikiEditor-ui-left' ).height()
 195+ );
 196+ $.wikiEditor.modules.toc.fn.redraw( context, $.wikiEditor.modules.toc.cfg.defaultWidth );
 197+ },
 198+
 199+
 200+ redraw: function( context, fixedWidth ) {
 201+ var fixedWidth = parseFloat( fixedWidth );
 202+ if( context.modules.toc.$toc.data( 'positionMode' ) == 'regular' ) {
 203+ context.$ui.find( '.wikiEditor-ui-right' )
 204+ .css( 'width', fixedWidth + 'px' );
 205+ context.$ui.find( '.wikiEditor-ui-left' )
 206+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, ( -1 * fixedWidth ) + 'px' )
 207+ .children()
 208+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, fixedWidth + 'px' );
 209+ } else if( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
 210+ context.$ui.find( '.wikiEditor-ui-left' )
 211+ .css( 'width', fixedWidth );
 212+ context.$ui.find( '.wikiEditor-ui-right' )
 213+ .css( $.wikiEditor.modules.toc.cfg.rtl ? 'right': 'left', fixedWidth );
 214+ context.$wikitext.css( 'height', context.$ui.find( '.wikiEditor-ui-right' ).height() );
 215+ }
 216+ },
 217+ switchLayout: function( context ) {
 218+ var width,
 219+ height = context.$ui.find( '.wikiEditor-ui-right' ).height();
 220+ if( context.modules.toc.$toc.data( 'positionMode' ) == 'regular'
 221+ && !context.modules.toc.$toc.data( 'collapsed' ) ) {
 222+ // store position mode
 223+ context.modules.toc.$toc.data( 'positionMode', 'goofy' );
 224+ // store the width of the TOC, to ensure we dont allow it to be larger than this when switching back
 225+ context.modules.toc.$toc.data( 'positionModeChangeAt',
 226+ context.$ui.find( '.wikiEditor-ui-right' ).width() );
 227+ width = $.wikiEditor.modules.toc.cfg.textMinimumWidth;
 228+ // set our styles for goofy mode
 229+ context.$ui.find( '.wikiEditor-ui-left' )
 230+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, '')
 231+ .css( { 'position': 'absolute', 'float': 'none',
 232+ 'left': $.wikiEditor.modules.toc.cfg.rtl ? 'auto': 0,
 233+ 'right' : $.wikiEditor.modules.toc.cfg.rtl ? 0 : 'auto' } )
 234+ .children()
 235+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, '' );
 236+ context.$ui.find( '.wikiEditor-ui-right' )
 237+ .css( { 'width': 'auto', 'position': 'absolute', 'float': 'none',
 238+ 'right': $.wikiEditor.modules.toc.cfg.rtl ? 'auto': 0,
 239+ 'left' : $.wikiEditor.modules.toc.cfg.rtl ? 0 : 'auto' } );
 240+ context.$wikitext
 241+ .css( 'position', 'relative' );
 242+ } else if ( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
 243+ // store position mode
 244+ context.modules.toc.$toc.data( 'positionMode', 'regular' );
 245+ // set width
 246+ width = context.$wikitext.width() - context.$ui.find( '.wikiEditor-ui-left' ).width();
 247+ if ( width > context.modules.toc.$toc.data( 'positionModeChangeAt' ) ) {
 248+ width = context.modules.toc.$toc.data( 'positionModeChangeAt' );
 249+ }
 250+ // set our styles for regular mode
 251+ context.$wikitext
 252+ .css( { 'position': '', 'height': '' } );
 253+ context.$ui.find( '.wikiEditor-ui-right' )
 254+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, '' )
 255+ .css( { 'position': '', 'left': '', 'right': '', 'float': '', 'top': '', 'height': '' } );
 256+ context.$ui.find( '.wikiEditor-ui-left' )
 257+ .css( { 'width': '', 'position': '', 'left': '', 'float': '', 'right': '' } );
 258+ }
 259+ $.wikiEditor.modules.toc.fn.redraw( context, width );
 260+ },
 261+ disable: function( context ) {
 262+ if ( context.modules.toc.$toc.data( 'collapsed' ) ) {
 263+ context.$ui.find( '.wikiEditor-ui-toc-expandControl' ).hide();
 264+ } else {
 265+ if( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
 266+ $.wikiEditor.modules.toc.fn.switchLayout( context );
 267+ }
 268+ context.$ui.find( '.wikiEditor-ui-right' ).hide();
 269+ context.$ui.find( '.wikiEditor-ui-left' )
 270+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, '' )
 271+ .children()
 272+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, '' );
 273+ }
 274+ context.modules.toc.$toc.data( 'positionMode', 'disabled' );
 275+ },
 276+ enable: function( context ) {
 277+ context.modules.toc.$toc.data( 'positionMode', 'regular' );
 278+ if ( context.modules.toc.$toc.data( 'collapsed' ) ) {
 279+ context.$ui.find( '.wikiEditor-ui-toc-expandControl' ).show();
 280+ } else {
 281+ context.$ui.find( '.wikiEditor-ui-right' ).show();
 282+ $.wikiEditor.modules.toc.fn.redraw( context, $.wikiEditor.modules.toc.cfg.minimumWidth );
 283+ context.modules.toc.$toc.find( 'div' ).autoEllipsis(
 284+ { 'position': 'right', 'tooltip': true, 'restoreText': true }
 285+ );
 286+ }
 287+ },
 288+ unhighlight: function( context ) {
 289+ // FIXME: For some reason, IE calls this function twice, the first time with context undefined
 290+ // Investigate this when you have time please! In the meantime, the user interaction is working just
 291+ // fine because the second call is valid
 292+ if ( context ) {
 293+ context.modules.toc.$toc.find( 'div' ).removeClass( 'current' );
 294+ }
 295+ },
 296+ /**
 297+ * Highlight the section the cursor is currently within
 298+ *
 299+ * @param {Object} context
 300+ */
 301+ update: function( context ) {
 302+ //temporarily commenting this out because it is causing all kinds of cursor
 303+ //and text jumping issues in IE. WIll get back to this --pdhanda
 304+ /*
 305+ var div = context.fn.beforeSelection( 'wikiEditor-toc-header' );
 306+ if ( div === null ) {
 307+ // beforeSelection couldn't figure it out, keep the old highlight state
 308+ return;
 309+ }
 310+
 311+ $.wikiEditor.modules.toc.fn.unhighlight( context );
 312+ var section = div.data( 'section' ) || 0;
 313+ if ( context.data.outline.length > 0 ) {
 314+ var sectionLink = context.modules.toc.$toc.find( 'div.section-' + section );
 315+ sectionLink.addClass( 'current' );
 316+
 317+ // Scroll the highlighted link into view if necessary
 318+ var relTop = sectionLink.offset().top - context.modules.toc.$toc.offset().top;
 319+
 320+ var scrollTop = context.modules.toc.$toc.scrollTop();
 321+ var divHeight = context.modules.toc.$toc.height();
 322+ var sectionHeight = sectionLink.height();
 323+ if ( relTop < 0 )
 324+ // Scroll up
 325+ context.modules.toc.$toc.scrollTop( scrollTop + relTop );
 326+ else if ( relTop + sectionHeight > divHeight )
 327+ // Scroll down
 328+ context.modules.toc.$toc.scrollTop( scrollTop + relTop + sectionHeight - divHeight );
 329+ }
 330+ */
 331+ },
 332+
 333+ /**
 334+ * Collapse the contents module
 335+ *
 336+ * @param {Object} event Event object with context as data
 337+ */
 338+ collapse: function( event ) {
 339+ var $this = $( this ),
 340+ context = $this.data( 'context' );
 341+ if( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
 342+ $.wikiEditor.modules.toc.fn.switchLayout( context );
 343+ }
 344+ var pT = $this.parent().position().top - 1;
 345+ context.modules.toc.$toc.data( 'collapsed', true );
 346+ var leftParam = {}, leftChildParam = {};
 347+ leftParam[ $.wikiEditor.modules.toc.cfg.flexProperty ] = '-1px';
 348+ leftChildParam[ $.wikiEditor.modules.toc.cfg.flexProperty ] = '1px';
 349+ context.$ui.find( '.wikiEditor-ui-left' )
 350+ .animate( leftParam, 'fast', function() {
 351+ $( this ).css( $.wikiEditor.modules.toc.cfg.flexProperty, 0 );
 352+ } )
 353+ .children()
 354+ .animate( leftChildParam, 'fast', function() {
 355+ $( this ).css( $.wikiEditor.modules.toc.cfg.flexProperty, 0 );
 356+ } );
 357+ context.$ui.find( '.wikiEditor-ui-right' )
 358+ .css( {
 359+ 'marginTop' : '1px',
 360+ 'position' : 'absolute',
 361+ 'left' : $.wikiEditor.modules.toc.cfg.rtl ? 0 : 'auto',
 362+ 'right' : $.wikiEditor.modules.toc.cfg.rtl ? 'auto' : 0,
 363+ 'top' : pT } )
 364+ .fadeOut( 'fast', function() {
 365+ $( this ).hide()
 366+ .css( { 'marginTop': '0', 'width': '1px' } );
 367+ context.$ui.find( '.wikiEditor-ui-toc-expandControl' ).fadeIn( 'fast' );
 368+ // Let the UI know things have moved around
 369+ context.fn.trigger( 'tocCollapse' );
 370+ context.fn.trigger( 'resize' );
 371+ } );
 372+
 373+ $.cookie( 'wikiEditor-' + context.instance + '-toc-width', 0 );
 374+ return false;
 375+ },
 376+
 377+ /**
 378+ * Expand the contents module
 379+ *
 380+ * @param {Object} event Event object with context as data
 381+ */
 382+ expand: function( event ) {
 383+ var $this = $( this ),
 384+ context = $this.data( 'context' ),
 385+ openWidth = parseFloat( context.modules.toc.$toc.data( 'openWidth' ) ),
 386+ availableSpace = context.$wikitext.width() - parseFloat( $.wikiEditor.modules.toc.cfg.textMinimumWidth );
 387+ if ( availableSpace < $.wikiEditor.modules.toc.cfg.textMinmumWidth ) return false;
 388+ context.modules.toc.$toc.data( 'collapsed', false );
 389+ // check if we've got enough room to open to our stored width
 390+ if ( availableSpace < openWidth ) openWidth = availableSpace;
 391+ context.$ui.find( '.wikiEditor-ui-toc-expandControl' ).hide();
 392+ var leftParam = {}, leftChildParam = {};
 393+ leftParam[ $.wikiEditor.modules.toc.cfg.flexProperty ] = parseFloat( openWidth ) * -1;
 394+ leftChildParam[ $.wikiEditor.modules.toc.cfg.flexProperty ] = openWidth;
 395+ context.$ui.find( '.wikiEditor-ui-left' )
 396+ .animate( leftParam, 'fast' )
 397+ .children()
 398+ .animate( leftChildParam, 'fast' );
 399+ context.$ui.find( '.wikiEditor-ui-right' )
 400+ .show()
 401+ .css( 'marginTop', '1px' )
 402+ .animate( { 'width' : openWidth }, 'fast', function() {
 403+ context.$content.trigger( 'mouseup' );
 404+ $( this ).css( {
 405+ 'marginTop' : '0',
 406+ 'position' : 'relative',
 407+ 'right' : 'auto',
 408+ 'left' : 'auto',
 409+ 'top': 'auto' } );
 410+ context.fn.trigger( 'tocExpand' );
 411+ context.fn.trigger( 'resize' );
 412+ } );
 413+ $.cookie( 'wikiEditor-' + context.instance + '-toc-width',
 414+ context.modules.toc.$toc.data( 'openWidth' ) );
 415+ return false;
 416+ },
 417+ /**
 418+ * Builds table of contents
 419+ *
 420+ * @param {Object} context
 421+ */
 422+ build: function( context ) {
 423+ /**
 424+ * Builds a structured outline from flat outline
 425+ *
 426+ * @param {Object} outline Array of objects with level fields
 427+ */
 428+ function buildStructure( outline, offset, level ) {
 429+ if ( offset == undefined ) offset = 0;
 430+ if ( level == undefined ) level = 1;
 431+ var sections = [];
 432+ for ( var i = offset; i < outline.length; i++ ) {
 433+ if ( outline[i].nLevel == level ) {
 434+ var sub = buildStructure( outline, i + 1, level + 1 );
 435+ if ( sub.length ) {
 436+ outline[i].sections = sub;
 437+ }
 438+ sections[sections.length] = outline[i];
 439+ } else if ( outline[i].nLevel < level ) {
 440+ break;
 441+ }
 442+ }
 443+ return sections;
 444+ }
 445+ /**
 446+ * Builds unordered list HTML object from structured outline
 447+ *
 448+ * @param {Object} structure Structured outline
 449+ */
 450+ function buildList( structure ) {
 451+ var list = $( '<ul />' );
 452+ for ( i in structure ) {
 453+ var div = $( '<div />' )
 454+ .addClass( 'section-' + structure[i].index )
 455+ .data( 'index', structure[i].index )
 456+ .mousedown( function() {
 457+ // No dragging!
 458+ return false;
 459+ } )
 460+ .click( function( event ) {
 461+ var wrapper = context.$content.find(
 462+ '.wikiEditor-toc-section-' + $( this ).data( 'index' ) );
 463+ if ( wrapper.size() == 0 )
 464+ wrapper = context.$content;
 465+ context.fn.scrollToTop( wrapper, true );
 466+ context.$textarea.textSelection( 'setSelection', {
 467+ 'start': 0,
 468+ 'startContainer': wrapper
 469+ } );
 470+ // Bring user's eyes to the point we've now jumped to
 471+ context.fn.highlightLine( $( wrapper ) );
 472+ // Highlight the clicked link
 473+ //remove highlighting of toc after a second. Temporary hack till the highlight works --pdhanda
 474+ //$.wikiEditor.modules.toc.fn.unhighlight( context );
 475+ $( this ).addClass( 'current' );
 476+ //$( this ).removeClass( 'current' );
 477+ setTimeout( function() { $.wikiEditor.modules.toc.fn.unhighlight( context ) }, 1000 );
 478+
 479+ if ( typeof $.trackAction != 'undefined' )
 480+ $.trackAction( 'ntoc.heading' );
 481+ event.preventDefault();
 482+ } )
 483+ .text( structure[i].text );
 484+ if ( structure[i].text == '' )
 485+ div.html( '&nbsp;' );
 486+ var item = $( '<li />' ).append( div );
 487+ if ( structure[i].sections !== undefined ) {
 488+ item.append( buildList( structure[i].sections ) );
 489+ }
 490+ list.append( item );
 491+ }
 492+ return list;
 493+ }
 494+ /**
 495+ * Builds controls for collapsing and expanding the TOC
 496+ *
 497+ */
 498+ function buildCollapseControls( ) {
 499+ var $collapseControl = $( '<div />' ), $expandControl = $( '<div />' );
 500+ $collapseControl
 501+ .addClass( 'tab' )
 502+ .addClass( 'tab-toc' )
 503+ .append( '<a href="#" />' )
 504+ .mousedown( function( e ) {
 505+ // No dragging!
 506+ e.preventDefault();
 507+ return false;
 508+ } )
 509+ .bind( 'click.wikiEditor-toc', function( e ) {
 510+ context.modules.toc.$toc.trigger( 'collapse.wikiEditor-toc' );
 511+ // No dragging!
 512+ e.preventDefault();
 513+ return false;
 514+ } )
 515+ .find( 'a' )
 516+ .text( mw.usability.getMsg( 'wikieditor-toc-hide' ) );
 517+ $expandControl
 518+ .addClass( 'wikiEditor-ui-toc-expandControl' )
 519+ .append( '<a href="#" />' )
 520+ .mousedown( function( e ) {
 521+ // No dragging!
 522+ e.preventDefault();
 523+ return false;
 524+ } )
 525+ .bind( 'click.wikiEditor-toc', function( e ) {
 526+ context.modules.toc.$toc.trigger( 'expand.wikiEditor-toc' );
 527+ // No dragging!
 528+ e.preventDefault();
 529+ return false;
 530+ } )
 531+ .hide()
 532+ .find( 'a' )
 533+ .text( mw.usability.getMsg( 'wikieditor-toc-show' ) );
 534+ $collapseControl.insertBefore( context.modules.toc.$toc );
 535+ context.$ui.find( '.wikiEditor-ui-left .wikiEditor-ui-top' ).append( $expandControl );
 536+ }
 537+ /**
 538+ * Initializes resizing controls on the TOC and sets the width of
 539+ * the TOC based on it's previous state
 540+ *
 541+ */
 542+ function buildResizeControls( ) {
 543+ context.$ui
 544+ .data( 'resizableDone', true )
 545+ .find( '.wikiEditor-ui-right' )
 546+ .data( 'wikiEditor-ui-left', context.$ui.find( '.wikiEditor-ui-left' ) )
 547+ .resizable( { handles: 'w,e', preventPositionLeftChange: true,
 548+ minWidth: parseFloat( $.wikiEditor.modules.toc.cfg.minimumWidth ),
 549+ start: function( e, ui ) {
 550+ var $this = $( this );
 551+ // Toss a transparent cover over our iframe
 552+ $( '<div />' )
 553+ .addClass( 'wikiEditor-ui-resize-mask' )
 554+ .css( {
 555+ 'position': 'absolute',
 556+ 'z-index': 2,
 557+ 'left': 0,
 558+ 'top': 0,
 559+ 'bottom': 0,
 560+ 'right': 0
 561+ } )
 562+ .appendTo( context.$ui.find( '.wikiEditor-ui-left' ) );
 563+ $this.resizable( 'option', 'maxWidth', $this.parent().width() -
 564+ parseFloat( $.wikiEditor.modules.toc.cfg.textMinimumWidth ) );
 565+ if(context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
 566+ $.wikiEditor.modules.toc.fn.switchLayout( context );
 567+ }
 568+ },
 569+ resize: function( e, ui ) {
 570+ // for some odd reason, ui.size.width seems a step ahead of what the *actual* width of
 571+ // the resizable is
 572+ $( this ).css( { 'width': ui.size.width, 'top': 'auto', 'height': 'auto' } )
 573+ .data( 'wikiEditor-ui-left' )
 574+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, ( -1 * ui.size.width ) )
 575+ .children().css( $.wikiEditor.modules.toc.cfg.flexProperty, ui.size.width );
 576+ // Let the UI know things have moved around
 577+ context.fn.trigger( 'resize' );
 578+ },
 579+ stop: function ( e, ui ) {
 580+ context.$ui.find( '.wikiEditor-ui-resize-mask' ).remove();
 581+ context.$content.trigger( 'mouseup' );
 582+ if( ui.size.width <= parseFloat( $.wikiEditor.modules.toc.cfg.minimumWidth ) ) {
 583+ context.modules.toc.$toc.trigger( 'collapse.wikiEditor-toc' );
 584+ } else {
 585+ context.modules.toc.$toc.find( 'div' ).autoEllipsis(
 586+ { 'position': 'right', 'tooltip': true, 'restoreText': true }
 587+ );
 588+ context.modules.toc.$toc.data( 'openWidth', ui.size.width );
 589+ $.cookie( 'wikiEditor-' + context.instance + '-toc-width', ui.size.width );
 590+ }
 591+ // Let the UI know things have moved around
 592+ context.fn.trigger( 'resize' );
 593+ }
 594+ });
 595+ // Convert our east resize handle into a secondary west resize handle
 596+ var handle = $.wikiEditor.modules.toc.cfg.rtl ? 'w' : 'e';
 597+ context.$ui.find( '.ui-resizable-' + handle )
 598+ .removeClass( 'ui-resizable-' + handle )
 599+ .addClass( 'ui-resizable-' + ( handle == 'w' ? 'e' : 'w' ) )
 600+ .addClass( 'wikiEditor-ui-toc-resize-grip' );
 601+ // Bind collapse and expand event handlers to the TOC
 602+ context.modules.toc.$toc
 603+ .bind( 'collapse.wikiEditor-toc', $.wikiEditor.modules.toc.fn.collapse )
 604+ .bind( 'expand.wikiEditor-toc', $.wikiEditor.modules.toc.fn.expand );
 605+ context.modules.toc.$toc.data( 'openWidth', $.wikiEditor.modules.toc.cfg.defaultWidth );
 606+ // If the toc-width cookie is set, reset the widths based upon that
 607+ if ( $.cookie( 'wikiEditor-' + context.instance + '-toc-width' ) == 0 ) {
 608+ context.modules.toc.$toc.trigger( 'collapse.wikiEditor-toc', { data: context } );
 609+ } else if ( $.cookie( 'wikiEditor-' + context.instance + '-toc-width' ) > 0 ) {
 610+ var initialWidth = $.cookie( 'wikiEditor-' + context.instance + '-toc-width' );
 611+ if( initialWidth < parseFloat( $.wikiEditor.modules.toc.cfg.minimumWidth ) )
 612+ initialWidth = parseFloat( $.wikiEditor.modules.toc.cfg.minimumWidth ) + 1;
 613+ context.modules.toc.$toc.data( 'openWidth', initialWidth + 'px' );
 614+ $.wikiEditor.modules.toc.fn.redraw( context, initialWidth );
 615+ }
 616+ }
 617+
 618+ // Normalize heading levels for list creation
 619+ // This is based on Linker::generateTOC(), so it should behave like the
 620+ // TOC on rendered articles does - which is considdered to be correct
 621+ // at this point in time.
 622+ if ( context.data.outline ) {
 623+ var outline = context.data.outline;
 624+ var lastLevel = 0;
 625+ var nLevel = 0;
 626+ for ( var i = 0; i < outline.length; i++ ) {
 627+ if ( outline[i].level > lastLevel ) {
 628+ nLevel++;
 629+ }
 630+ else if ( outline[i].level < lastLevel ) {
 631+ nLevel -= Math.max( 1, lastLevel - outline[i].level );
 632+ }
 633+ if ( nLevel <= 0 ) {
 634+ nLevel = 1;
 635+ }
 636+ outline[i].nLevel = nLevel;
 637+ lastLevel = outline[i].level;
 638+ }
 639+ // Recursively build the structure and add special item for
 640+ // section 0, if needed
 641+ var structure = buildStructure( outline );
 642+ if ( $( 'input[name=wpSection]' ).val() == '' ) {
 643+ structure.unshift( { 'text': wgPageName.replace( /_/g, ' ' ), 'level': 1, 'index': 0 } );
 644+ }
 645+ context.modules.toc.$toc.html( buildList( structure ) );
 646+
 647+ if ( wgNavigableTOCResizable && !context.$ui.data( 'resizableDone' ) ) {
 648+ buildResizeControls();
 649+ buildCollapseControls();
 650+ }
 651+ context.modules.toc.$toc.find( 'div' ).autoEllipsis(
 652+ { 'position': 'right', 'tooltip': true, 'restoreText': true }
 653+ );
 654+ }
 655+ },
 656+ improveUI: function() {
 657+ /*
 658+ * Extending resizable to allow west resizing without altering the left position attribute
 659+ */
 660+ $.ui.plugin.add( "resizable", "preventPositionLeftChange", {
 661+ resize: function( event, ui ) {
 662+ $( this ).data( "resizable" ).position.left = 0;
 663+ }
 664+ } );
 665+ }
 666+}
 667+
 668+};
 669+
 670+} ) ( jQuery );
Property changes on: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.toc.js
___________________________________________________________________
Added: svn:eol-style
1671 + native
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.html
@@ -0,0 +1,135 @@
 2+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 3+<html>
 4+<head>
 5+ <title>WikiEditor</title>
 6+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 7+ <!--[if gte IE 8]>
 8+ <style>
 9+ /* IE8 ONLY - This is how we are fixing the double-height of BR tags when they are alone in a P tag */
 10+ p > br {
 11+ display: none;
 12+ }
 13+ p > br + br {
 14+ display: block;
 15+ }
 16+ </style>
 17+ <![endif]-->
 18+ <style>
 19+ body {
 20+ margin: 0;
 21+ padding: 0;
 22+ width: 100%;
 23+ height: 100%;
 24+ font-family: monospace;
 25+ font-size: 9.5pt;
 26+ line-height: 1.5em;
 27+ overflow-x: auto; /* Use horizontal scroller if needed; for Firefox 2, not needed in Firefox 3 */
 28+ white-space: pre-wrap; /* css-3 */
 29+ white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
 30+ white-space: -pre-wrap; /* Opera 4-6 */
 31+ white-space: -o-pre-wrap; /* Opera 7 */
 32+ word-wrap: break-word; /* Internet Explorer 5.5+ */
 33+ }
 34+ body.pasting {
 35+ white-space: normal;
 36+ }
 37+ p {
 38+ margin: 0;
 39+ padding: 0;
 40+ }
 41+ /* General WikiEditor stuff */
 42+ .wikiEditor-nodisplay {
 43+ display: none !important;
 44+ }
 45+ .wikiEditor-tab {
 46+ padding-left: 4em;
 47+ }
 48+ /* WikiEditor Templates */
 49+ .wikiEditor-templates .wikiEditor-template {
 50+ color: silver;
 51+ }
 52+ /* WikiEditor TemplateEditor */
 53+ .wikiEditor-templateEditor .wikiEditor-template-text-shrunken{
 54+ height: 1px !important;
 55+ width: 1px !important;
 56+ overflow: hidden;
 57+ float: right;
 58+ }
 59+ .wikiEditor-templateEditor .wikiEditor-template-text-visible{
 60+ padding: 0.5em 4px;
 61+ padding-top: 1.25em;
 62+ margin-top: -0.95em;
 63+ background: #F3F3F3 url(../../images/wikiEditor/templateEditor/text-base.png) repeat-x scroll center top;
 64+ display: block;
 65+ width: 100%;
 66+ border-bottom: solid 1px #cccccc;
 67+ }
 68+ .wikiEditor-templateEditor .wikiEditor-template {
 69+ display: inline-block;
 70+ font-size: 12px;
 71+ }
 72+ .wikiEditor-templateEditor .wikiEditor-template-name {
 73+ cursor: pointer;
 74+ vertical-align: -2px;
 75+ display: inline-block;
 76+ height: 16px;
 77+ margin-bottom: -1px;
 78+ margin-right: 2px;
 79+ overflow: hidden;
 80+ background: url( '../../images/wikiEditor/templateEditor/name-base.png' ) 0 0 repeat-x #e8e8e8;
 81+ color: #000000;
 82+ font-family: monospace;
 83+ text-decoration: none;
 84+ padding-left: 0.33em;
 85+ line-height: 16px;
 86+ }
 87+ .wikiEditor-templateEditor .wikiEditor-template-expand {
 88+ cursor: pointer;
 89+ vertical-align: -2px;
 90+ display: inline-block;
 91+ margin-left: 2px;
 92+ height: 16px;
 93+ margin-bottom: -1px;
 94+ line-height: 16px;
 95+ overflow: hidden;
 96+ width: 13px;
 97+ background-position: 50%;
 98+ }
 99+ .wikiEditor-templateEditor .wikiEditor-template-dialog {
 100+ cursor: pointer;
 101+ vertical-align: -18%;
 102+ display: inline-block;
 103+ height: 16px;
 104+ overflow: hidden;
 105+ width: 22px;
 106+ background-position: 50%;
 107+ }
 108+ .wikiEditor-templateEditor .wikiEditor-template-name:hover {
 109+ text-decoration: underline;
 110+ }
 111+ .wikiEditor-templateEditor .wikiEditor-template-expanded .wikiEditor-template-expand {
 112+ background-image: url(../../images/wikiEditor/templateEditor/collapse.png);
 113+ }
 114+ .wikiEditor-templateEditor .wikiEditor-template-expanded .wikiEditor-template-dialog {
 115+ background-image: url(../../images/wikiEditor/templateEditor/dialog-expanded.png);
 116+ }
 117+ .wikiEditor-templateEditor .wikiEditor-template-collapsed .wikiEditor-template-expand {
 118+ background-image: url(../../images/wikiEditor/templateEditor/expand.png);
 119+ }
 120+ .wikiEditor-templateEditor .wikiEditor-template-collapsed .wikiEditor-template-dialog {
 121+ background-image: url(../../images/wikiEditor/templateEditor/dialog-collapsed.png);
 122+ }
 123+ .wikiEditor-templateEditor .wikiEditor-template-expanded {
 124+ display: block;
 125+ }
 126+ .wikiEditor-templateEditor .wikiEditor-template .wikiEditor-template-text {
 127+
 128+ }
 129+ .wikiEditor-templateEditor .wikiEditor-template-end, .wikiEditor-template-start {
 130+ color: blue;
 131+ cursor: pointer;
 132+ }
 133+ </style>
 134+</head>
 135+<body></body>
 136+</html>
Property changes on: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.html
___________________________________________________________________
Added: svn:eol-style
1137 + native
Index: trunk/extensions/WikiEditor/modules/Preview.js
@@ -0,0 +1,12 @@
 2+/* JavaScript for WikiEditor Preview module */
 3+
 4+$j(document).ready( function() {
 5+ // Check preferences for preview
 6+ if ( !wgWikiEditorEnabledModules.preview ) {
 7+ return true;
 8+ }
 9+ // Add the preview module
 10+ if ( $j.fn.wikiEditor ) {
 11+ $j( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'preview' );
 12+ }
 13+});
Property changes on: trunk/extensions/WikiEditor/modules/Preview.js
___________________________________________________________________
Added: svn:eol-style
114 + native
Index: trunk/extensions/WikiEditor/modules/TemplateEditor.js
@@ -0,0 +1,16 @@
 2+/* JavaScript for WikiEditor Template Editor module */
 3+
 4+$j(document).ready( function() {
 5+ // Check preferences for templateEditor
 6+ if ( !wgWikiEditorEnabledModules.templateEditor ) {
 7+ return true;
 8+ }
 9+ //disable if in template namespace
 10+ if ( wgNamespaceNumber == 10 ) {
 11+ return true;
 12+ }
 13+ // Add the templateEditor module
 14+ if ( $j.fn.wikiEditor ) {
 15+ $j( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'templateEditor' );
 16+ }
 17+});
Property changes on: trunk/extensions/WikiEditor/modules/TemplateEditor.js
___________________________________________________________________
Added: svn:eol-style
118 + native
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.templates.js
@@ -0,0 +1,69 @@
 2+/* Templates Module for wikiEditor */
 3+( function( $ ) { $.wikiEditor.modules.templates = {
 4+
 5+/**
 6+ * Core Requirements
 7+ */
 8+'req': [ 'iframe' ],
 9+/**
 10+ * Object Templates
 11+ */
 12+'tpl': {
 13+ 'marker': {
 14+ 'type': 'template',
 15+ 'anchor': 'wrap',
 16+ 'skipDivision': 'realchange',
 17+ 'afterWrap': function( node ) {
 18+ $( node ).addClass( 'wikiEditor-template' );
 19+ },
 20+ 'getAnchor': function( ca1, ca2 ) {
 21+ return $( ca1.parentNode ).is( '.wikiEditor-template' ) ? ca1.parentNode : null;
 22+ }
 23+ }
 24+},
 25+/**
 26+ * Event handlers
 27+ */
 28+'evt': {
 29+ 'mark': function( context, event ) {
 30+ // The markers returned by this function are skipped on realchange, so don't regenerate them in that case
 31+ if ( context.modules.highlight.currentScope == 'realchange' ) {
 32+ return;
 33+ }
 34+ // Get references to the markers and tokens from the current context
 35+ var markers = context.modules.highlight.markers;
 36+ var tokens = context.modules.highlight.tokenArray;
 37+ // Use depth-tracking to extract top-level templates from tokens
 38+ var depth = 0, bias, start;
 39+ for ( var i in tokens ) {
 40+ depth += ( bias = tokens[i].label == 'TEMPLATE_BEGIN' ? 1 : ( tokens[i].label == 'TEMPLATE_END' ? -1 : 0 ) );
 41+ if ( bias > 0 && depth == 1 ) {
 42+ // Top-level opening - use offset as start
 43+ start = tokens[i].offset;
 44+ } else if ( bias < 0 && depth == 0 ) {
 45+ // Top-level closing - use offset as end
 46+ markers[markers.length] = $.extend(
 47+ { 'context': context, 'start': start, 'end': tokens[i].offset },
 48+ $.wikiEditor.modules.templates.tpl.marker
 49+ );
 50+ }
 51+ if ( depth < 0 ) {
 52+ depth = 0;
 53+ }
 54+ }
 55+ }
 56+},
 57+'exp': [
 58+ { 'regex': /{{/, 'label': "TEMPLATE_BEGIN" },
 59+ { 'regex': /}}/, 'label': "TEMPLATE_END", 'markAfter': true }
 60+],
 61+/**
 62+ * Internally used functions
 63+ */
 64+'fn': {
 65+ 'create': function( context, config ) {
 66+ // Do some stuff here...
 67+ }
 68+}
 69+
 70+}; } ) ( jQuery );
Property changes on: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.templates.js
___________________________________________________________________
Added: svn:eol-style
171 + native
Index: trunk/extensions/WikiEditor/modules/Toc.js
@@ -0,0 +1,18 @@
 2+/* JavaScript for WikiEditor Toc module */
 3+
 4+$j(document).ready( function() {
 5+ // Check preferences for toolbar
 6+ if ( !wgWikiEditorPreferences || !( wgWikiEditorPreferences.toc && wgWikiEditorPreferences.toc.enable ) ) {
 7+ return true;
 8+ }
 9+ // Add the toc module
 10+ if ( $j.fn.wikiEditor ) {
 11+ mw.usability.load( [ '$j.ui','$j.ui.draggable', '$j.ui.resizable' ], function() {
 12+ // load the module and let it know were ready to go
 13+ $j( '#wpTextbox1' )
 14+ .wikiEditor( 'addModule', { 'toc' : { 'rtl' : ( $j( 'body' ).is( '.rtl' ) ) } } )
 15+ //FIXME - should move the ready handler code to the create function so this isn't necissary
 16+ .data( 'wikiEditor-context' ).fn.trigger( 'ready' );
 17+ } );
 18+ }
 19+});
Property changes on: trunk/extensions/WikiEditor/modules/Toc.js
___________________________________________________________________
Added: svn:eol-style
120 + native
Index: trunk/extensions/WikiEditor/modules/wikiEditor.css
@@ -0,0 +1,134 @@
 2+/* wikiEditor plugin */
 3+
 4+/* This ID (#editform) could change in MediaWiki */
 5+form#editform {
 6+ margin: 0;
 7+ padding: 0;
 8+}
 9+/* These IDs (#wpSummaryLabel and #wpSummary) could change in MediaWiki */
 10+#wpSummary, #wpSummaryLabel {
 11+ margin-bottom: 1em;
 12+}
 13+/* This ID (#wpTextbox1) could change in MediaWiki */
 14+.wikiEditor-ui textarea#wpTextbox1 {
 15+ border: none;
 16+ padding: 0;
 17+ margin: -1px;
 18+ line-height: 1.5em;
 19+}
 20+.wikiEditor-ui .wikiEditor-ui-text > textarea#wpTextbox1 {
 21+ margin: 0;
 22+}
 23+.wikiEditor-ui {
 24+ float: left;
 25+ position: relative;
 26+ clear: both;
 27+ width: 100%;
 28+ background-color: #E0EEf7;
 29+ border: solid silver 1px;
 30+}
 31+body.rtl .wikiEditor-ui {
 32+ float: right;
 33+}
 34+.wikiEditor-ui .wikiEditor-ui-bottom {
 35+
 36+}
 37+.wikiEditor-ui .wikiEditor-ui-text {
 38+ line-height: 0;
 39+}
 40+.wikiEditor-ui .wikiEditor-ui-top {
 41+ position: relative;
 42+ border-bottom: solid silver 1px;
 43+}
 44+.wikiEditor-ui .wikiEditor-ui-left {
 45+ float: left;
 46+ width: 100%;
 47+}
 48+body.rtl .wikiEditor-ui .wikiEditor-ui-left {
 49+ float: right;
 50+}
 51+.wikiEditor-ui .wikiEditor-ui-right {
 52+ float: right;
 53+ background: #F3F3F3;
 54+ overflow: hidden;
 55+}
 56+body.rtl .wikiEditor-ui .wikiEditor-ui-right {
 57+ float: left;
 58+}
 59+.wikiEditor-wikitext {
 60+ float: left;
 61+ width: 100%;
 62+}
 63+.wikiEditor-ui-controls {
 64+ float: left;
 65+ width: 100%;
 66+ background-color: white;
 67+ margin-top: -1px;
 68+ border-bottom: solid 1px silver;
 69+}
 70+.wikiEditor-ui-tabs {
 71+ float: left;
 72+ height: 2.5em;
 73+ margin-left: -1px;
 74+ background-color: white;
 75+ border-left: solid 1px silver;
 76+ border-top: solid 1px silver;
 77+}
 78+.wikiEditor-ui-buttons {
 79+ float: right;
 80+ height: 2.5em;
 81+ margin-right: -1px;
 82+ background-color: white;
 83+ padding-left: 1em;
 84+ border-top: solid 1px white;
 85+}
 86+.wikiEditor-ui-buttons button {
 87+ margin-left: 0.5em;
 88+}
 89+.wikiEditor-ui-tabs div {
 90+ float: left;
 91+ height: 2.5em;
 92+ background-color: #f3f3f3;
 93+ border-right: solid 1px silver;
 94+ border-bottom: solid 1px silver;
 95+}
 96+.wikiEditor-ui-tabs div.current {
 97+ border-bottom: solid 1px white;
 98+ background-color: white;
 99+}
 100+.wikiEditor-ui-tabs div a {
 101+ display: inline-block;
 102+ padding: 0 0.75em;
 103+ line-height: 2.5em;
 104+ color: #0645AD;
 105+}
 106+.wikiEditor-ui-tabs div.current a {
 107+ color: #333333;
 108+}
 109+.wikiEditor-ui-tabs div.current a:hover {
 110+ text-decoration: none;
 111+}
 112+
 113+.wikiEditor-view-wikitext {
 114+ line-height: 1em;
 115+}
 116+.wikiEditor-ui-loading {
 117+ background: #f3f3f3;
 118+ z-index: 10;
 119+ position: absolute;
 120+ top: 0;
 121+ left: 0;
 122+ text-align: center;
 123+ height: 100%;
 124+ width: 100%;
 125+ border: 1px solid silver;
 126+ margin: -1px;
 127+}
 128+.wikiEditor-ui-loading span {
 129+ display: block;
 130+ height: 24px;
 131+ width: 24px;
 132+ background: url( ../images/wikiEditor/toolbar/loading.gif ) 0 0 no-repeat;
 133+ text-indent: -9999px;
 134+ margin: 0 auto;
 135+}
\ No newline at end of file
Property changes on: trunk/extensions/WikiEditor/modules/wikiEditor.css
___________________________________________________________________
Added: svn:eol-style
1136 + native
Index: trunk/extensions/WikiEditor/modules/wikiEditor.previewDialog.css
@@ -0,0 +1,28 @@
 2+#wikiEditor-0-preview-dialog .wikiEditor-ui-loading {
 3+ background: #f3f3f3;
 4+ z-index: 10;
 5+ position: absolute;
 6+ left: 0;
 7+ text-align: center;
 8+ height: 100%;
 9+ width: 100%;
 10+ overflow: hidden;
 11+ border: none;
 12+}
 13+#wikiEditor-0-preview-dialog .wikiEditor-ui-loading span {
 14+ display: block;
 15+ height: 24px;
 16+ width: 24px;
 17+ background: url( ../images/wikiEditor/toolbar/loading.gif ) 0 0 no-repeat;
 18+ text-indent: -9999px;
 19+ margin: 50px auto;
 20+}
 21+.ui-dialog .ui-dialog-buttonpane {
 22+ margin: 0 !important;
 23+}
 24+.wikiEditor-preview-dialog-contents {
 25+ font-size: 0.9em !important;
 26+}
 27+.wikiEditor-preview-dialog-contents #firstHeading {
 28+ font-size: 2.1em;
 29+}
\ No newline at end of file
Property changes on: trunk/extensions/WikiEditor/modules/wikiEditor.previewDialog.css
___________________________________________________________________
Added: svn:eol-style
130 + native
Index: trunk/extensions/WikiEditor/modules/wikiEditor.toolbar.css
@@ -0,0 +1,408 @@
 2+/* wikiEditor toolbar module */
 3+
 4+.wikiEditor-ui-toolbar {
 5+ position: relative;
 6+ width: 100%;
 7+}
 8+
 9+/* Expandable Sections */
 10+.wikiEditor-ui-toolbar .sections {
 11+ float: left;
 12+ width: 100%;
 13+ clear: both;
 14+ height: 0;
 15+}
 16+body.rtl .wikiEditor-ui-toolbar .sections {
 17+ float: right;
 18+}
 19+.wikiEditor-ui-toolbar .sections .section {
 20+ display: none;
 21+ float: left;
 22+ width: 100%;
 23+ border-top: solid 1px #DDDDDD;
 24+ background-color: #E0EEf7;
 25+}
 26+.wikiEditor-ui-toolbar {
 27+ background-image: url(../images/wikiEditor/toolbar/base.png?1);
 28+ background-position: left top;
 29+ background-repeat: repeat-x;
 30+}
 31+/*
 32+.wikiEditor-ui-toolbar .toolbar {
 33+ background-image: url(../images/wikiEditor/toolbar/base.png?1);
 34+ background-position: left top;
 35+}
 36+*/
 37+body.rtl .wikiEditor-ui-toolbar .sections .section {
 38+ float: right;
 39+}
 40+/* Gets overridden when the section div is in class loading - see below */
 41+.wikiEditor-ui-toolbar .sections div .spinner {
 42+ display: none;
 43+}
 44+.wikiEditor-ui-toolbar .sections .loading .spinner {
 45+ display: block;
 46+ background-image: url(../images/wikiEditor/toolbar/loading.gif?2);
 47+ background-position: left center;
 48+ background-repeat: no-repeat;
 49+ padding-left: 32px;
 50+ margin-left: 0.5em;
 51+ height: 32px;
 52+ float: left;
 53+ color: #666666;
 54+}
 55+body.rtl .wikiEditor-ui-toolbar .sections .loading .spinner {
 56+ background-position: right center;
 57+ padding-left: 0;
 58+ padding-right: 32px;
 59+ margin-left: 0;
 60+ margin-right: 0.5em;
 61+ float: right;
 62+}
 63+/* Top Level Containers */
 64+.wikiEditor-ui-toolbar .tabs,
 65+.wikiEditor-ui-toolbar .section-main {
 66+ position: relative;
 67+ float: left;
 68+ height: 26px;
 69+}
 70+body.rtl .wikiEditor-ui-toolbar .tabs,
 71+body.rtl .wikiEditor-ui-toolbar .section-main {
 72+ float: right;
 73+}
 74+/* Groups */
 75+.wikiEditor-ui-toolbar .group {
 76+ float: left;
 77+ height: 26px;
 78+ padding-right: 6px;
 79+ border-right: solid 1px #DDDDDD;
 80+ margin: 3px;
 81+}
 82+body.rtl .wikiEditor-ui-toolbar .group {
 83+ float: right;
 84+ padding-right: 0;
 85+ padding-left: 6px;
 86+ border-right: none;
 87+ border-left: solid 1px #DDDDDD;
 88+}
 89+.wikiEditor-ui-toolbar .group-search {
 90+ float: right;
 91+ padding: 0 0 0 6px;
 92+ border-right: none;
 93+ border-left: 1px solid #DDDDDD;
 94+}
 95+.wikiEditor-ui-toolbar .group-insert {
 96+ border-right: none;
 97+}
 98+body.rtl .wikiEditor-ui-toolbar .group-search {
 99+ float: left;
 100+ padding: 0 6px 0 0;
 101+ border-left: none;
 102+ border-right: 1px solid #DDDDDD;
 103+}
 104+body.rtl .wikiEditor-ui-toolbar .group-insert {
 105+ border-left: none;
 106+}
 107+/* Sprited Buttons */
 108+.wikiEditor-toolbar-spritedButton {
 109+ background: url(../images/wikiEditor/toolbar/button-sprite.png?1) 0 0 no-repeat;
 110+ display: block;
 111+ float: left;
 112+ height: 22px;
 113+ text-indent: -9999px;
 114+ width: 22px;
 115+ padding: 2px;
 116+ cursor: pointer;
 117+ overflow: hidden;
 118+}
 119+/* Tabs */
 120+.wikiEditor-ui-toolbar .tabs {
 121+ list-style: none;
 122+ margin: 3px;
 123+}
 124+.wikiEditor-ui-toolbar .tabs span.tab {
 125+ display: inline-block;
 126+ float: left;
 127+ line-height: 26px;
 128+}
 129+/* IGNORED BY IE6 */
 130+.wikiEditor-ui-toolbar .tabs > span.tab {
 131+ display: block;
 132+}
 133+/* IGNORED BY IE6 */
 134+body.rtl .wikiEditor-ui-toolbar .tabs > span.tab {
 135+ float: right;
 136+}
 137+.wikiEditor-ui-toolbar .tabs span.tab a,
 138+.wikiEditor-ui-toolbar .tabs span.tab a:visited {
 139+ display: inline-block;
 140+ float: left;
 141+ padding-left: 18px;
 142+ padding-right: 12px;
 143+ height: 26px;
 144+ cursor: pointer;
 145+ color: #0645ad;
 146+ background-image: url(../images/wikiEditor/toolbar/arrow-right.png?1);
 147+ background-position: left center;
 148+ background-repeat: no-repeat;
 149+}
 150+body.rtl .wikiEditor-ui-toolbar .tabs span.tab a,
 151+body.rtl .wikiEditor-ui-toolbar .tabs span.tab a:visited {
 152+ padding-left: 12px;
 153+ padding-right: 18px;
 154+ background-image: url(../images/wikiEditor/toolbar/arrow-left.png?1);
 155+ background-position: right center;
 156+}
 157+/* IGNORED BY IE6 */
 158+body.rtl .wikiEditor-ui-toolbar .tabs > span.tab > a,
 159+body.rtl .wikiEditor-ui-toolbar .tabs > pan.tab > a:visited {
 160+ float: right;
 161+}
 162+.wikiEditor-ui-toolbar .tabs span.tab a.current,
 163+.wikiEditor-ui-toolbar .tabs span.tab a.current:visited {
 164+ color: #333333;
 165+ background-image: url(../images/wikiEditor/toolbar/arrow-down.png?1);
 166+}
 167+body.rtl .wikiEditor-ui-toolbar .tabs span.tab a.current,
 168+body.rtl .wikiEditor-ui-toolbar .tabs span.tab a.current:visited {
 169+ background-image: url(../images/wikiEditor/toolbar/arrow-down.png?1);
 170+}
 171+.wikiEditor-ui-toolbar .tabs span.tab a.current:hover {
 172+ text-decoration: none;
 173+}
 174+.wikiEditor-ui-toolbar .tabs span.tab a.loading {
 175+ background-image: url(../images/wikiEditor/toolbar/loading-small.gif?1) !important;
 176+}
 177+/* Toolbar */
 178+.wikiEditor-ui-toolbar .group .label {
 179+ float: left;
 180+ border: 0px;
 181+ height: 22px;
 182+ line-height: 22px;
 183+ margin: 2px;
 184+ margin-left: 5px;
 185+ margin-right: 8px;
 186+ color: #777777;
 187+ cursor: default;
 188+}
 189+/* IGNORED BY IE6 */
 190+body.rtl .wikiEditor-ui-toolbar .group > .label {
 191+ float: right;
 192+ margin-left: 8px;
 193+ margin-right: 5px;
 194+}
 195+.wikiEditor-ui-toolbar .group img.tool {
 196+ float: left;
 197+ border: 0px;
 198+ height: 22px;
 199+ width: 22px;
 200+ padding: 2px;
 201+ cursor: pointer;
 202+}
 203+/* IGNORED BY IE6 */
 204+body.rtl .wikiEditor-ui-toolbar .group > img.tool {
 205+ float: right;
 206+}
 207+.wikiEditor-ui-toolbar .group .tool-select {
 208+ float: left;
 209+ margin: 2px;
 210+ height: 22px;
 211+ cursor: pointer;
 212+ border: solid 1px silver;
 213+ padding: 0;
 214+ margin-right: 0;
 215+ cursor: pointer;
 216+ background-color: #ffffff;
 217+}
 218+/* IGNORED BY IE6 */
 219+body.rtl .wikiEditor-ui-toolbar .group > .tool-select {
 220+ float: right;
 221+}
 222+.wikiEditor-ui-toolbar .group .tool-select .label {
 223+ background-image: url(../images/wikiEditor/toolbar/arrow-down.png?1);
 224+ background-position: center right;
 225+ background-repeat: no-repeat;
 226+ padding: 0;
 227+ margin: 0;
 228+ padding-left: 4px;
 229+ padding-right: 22px;
 230+ margin-right: 4px;
 231+ cursor: pointer;
 232+ text-decoration: none;
 233+ color: #333333;
 234+}
 235+body.rtl .wikiEditor-ui-toolbar .group .tool-select .label {
 236+ background-position: center left;
 237+ padding-right: 4px;
 238+ padding-left: 22px;
 239+ margin-left: 4px;
 240+ margin-right: 0;
 241+}
 242+body.rtl .wikiEditor-ui-toolbar .group .tool-select .menu {
 243+ clear: both;
 244+}
 245+.wikiEditor-ui-toolbar .group .tool-select .menu .options {
 246+ position: absolute;
 247+ display: none;
 248+ margin-left: -1px;
 249+ border: solid 1px silver;
 250+ background-color: #ffffff;
 251+}
 252+body.rtl .wikiEditor-ui-toolbar .group .tool-select .menu {
 253+ margin-left: -1px;
 254+ margin-right: -1px;
 255+}
 256+/* IGNORED BY IE6 */
 257+.wikiEditor-ui-toolbar .group .tool-select .options {
 258+ margin-top: 22px;
 259+}
 260+.wikiEditor-ui-toolbar .group .tool-select .options .option {
 261+ display: block;
 262+ padding: 0.5em;
 263+ text-decoration: none;
 264+ color: black;
 265+ white-space: nowrap;
 266+}
 267+.wikiEditor-ui-toolbar .group .tool-select .options .option:hover {
 268+ background-color: #E0EEf7;
 269+}
 270+.wikiEditor-ui-toolbar .group .tool-select .options .option[rel=heading-2] {
 271+ font-size: 150%;
 272+ font-weight: normal;
 273+}
 274+.wikiEditor-ui-toolbar .group .tool-select .options .option[rel=heading-3] {
 275+ font-size: 132%;
 276+ font-weight: normal;
 277+}
 278+.wikiEditor-ui-toolbar .group .tool-select .options .option[rel=heading-4] {
 279+ font-size: 116%;
 280+ font-weight: normal;
 281+}
 282+.wikiEditor-ui-toolbar .group .tool-select .options .option[rel=heading-5] {
 283+ font-size: 100%;
 284+ font-weight: bold;
 285+}
 286+/* Booklet */
 287+.wikiEditor-ui-toolbar .booklet .index {
 288+ float: left;
 289+ width: 20%;
 290+ height: 125px;
 291+ overflow: auto;
 292+}
 293+body.rtl .wikiEditor-ui-toolbar .booklet .index {
 294+ float: right;
 295+}
 296+.wikiEditor-ui-toolbar .booklet .index div {
 297+ padding: 4px;
 298+ padding-left: 6px;
 299+ cursor: pointer;
 300+ color: #0645ad;
 301+}
 302+body.rtl .wikiEditor-ui-toolbar .booklet .index div {
 303+ padding-left: 4px;
 304+ padding-right: 6px;
 305+}
 306+.wikiEditor-ui-toolbar .booklet .index .current {
 307+ background-color: #FAFAFA;
 308+ color: #333333;
 309+ cursor: default;
 310+}
 311+.wikiEditor-ui-toolbar .booklet .pages {
 312+ float: right;
 313+ width: 80%;
 314+ height: 125px;
 315+ overflow: auto;
 316+ background-color: #FAFAFA;
 317+}
 318+body.rtl .wikiEditor-ui-toolbar .booklet .pages {
 319+ float: left;
 320+}
 321+/* Help Pages */
 322+.wikiEditor-ui-toolbar .page-table table {
 323+ padding-left: 5px;
 324+ padding-right: 5px;
 325+ background: none;
 326+}
 327+.wikiEditor-ui-toolbar .page-table th {
 328+ color: #999999;
 329+}
 330+.wikiEditor-ui-toolbar .page-table td {
 331+ color: black;
 332+ border-top: solid 1px #EEEEEE;
 333+}
 334+.wikiEditor-ui-toolbar .page-table th,
 335+.wikiEditor-ui-toolbar .page-table td {
 336+ text-align: left;
 337+ padding: 5px;
 338+ margin: 0px;
 339+}
 340+body.rtl .wikiEditor-ui-toolbar .page-table th,
 341+body.rtl .wikiEditor-ui-toolbar .page-table td {
 342+ text-align: right;
 343+}
 344+.wikiEditor-ui-toolbar .section-help .page-table td.cell-syntax,
 345+.wikiEditor-ui-toolbar .section-help .page-table td.syntax {
 346+ font-family: monospace;
 347+}
 348+.wikiEditor-ui-toolbar .section-help .page-table td.syntax,
 349+.wikiEditor-ui-toolbar .section-help .page-table td.cell-syntax,
 350+.wikiEditor-ui-toolbar .section-help .page-table td.cell-result,
 351+.wikiEditor-ui-toolbar .section-help .page-table td.result {
 352+ width: 40%;
 353+}
 354+.wikiEditor-ui-toolbar .section-help .page-table td.description,
 355+.wikiEditor-ui-toolbar .section-help .page-table td.description {
 356+ width: 20%;
 357+}
 358+/* Characters Pages */
 359+.wikiEditor-ui-toolbar .page-characters div span {
 360+ border: solid 1px #DDDDDD;
 361+ padding: 5px;
 362+ padding-left: 8px;
 363+ padding-right: 8px;
 364+ margin-left: 5px;
 365+ margin-top: 5px;
 366+ height: 1em;
 367+ float: left;
 368+ display: block;
 369+ color: black;
 370+ text-decoration: none;
 371+ cursor: pointer;
 372+ font-family: monospace;
 373+ font-size: 1.25em;
 374+}
 375+body.rtl .wikiEditor-ui-toolbar .page-characters > div > span,
 376+.wikiEditor-ui-toolbar .page-characters div[dir=rtl] span {
 377+ direction: rtl;
 378+ float: right;
 379+ margin-left: 0;
 380+ margin-right: 5px;
 381+}
 382+.wikiEditor-ui-toolbar .page-characters div span:hover {
 383+ background-color: white;
 384+ text-decoration: none;
 385+ border-color: #a8d7f9;
 386+}
 387+.ui-widget table td.wikieditor-toolbar-table-preview-wrapper span {
 388+ padding: 4px 6px 0px;
 389+ display: block;
 390+}
 391+.ui-widget table .wikieditor-toolbar-table-preview-frame {
 392+ width: 340px;
 393+ background: #fff;
 394+ padding: 10px;
 395+ overflow: hidden;
 396+ display: block;
 397+ position: relative;
 398+}
 399+.ui-widget table .wikieditor-toolbar-table-preview-content {
 400+ width: 375px;
 401+ display: block;
 402+}
 403+.ui-widget table .wikieditor-toolbar-table-preview {
 404+ width: 340px;
 405+}
 406+.ui-widget table td.wikieditor-toolbar-table-preview-wrapper {
 407+ background: #e5e5e5;
 408+ padding: 10px;
 409+}
Property changes on: trunk/extensions/WikiEditor/modules/wikiEditor.toolbar.css
___________________________________________________________________
Added: svn:eol-style
1410 + native
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.previewDialog.js
@@ -0,0 +1,130 @@
 2+/* Publish module for wikiEditor */
 3+( function( $ ) { $.wikiEditor.modules.previewDialog = {
 4+
 5+/**
 6+ * Compatability map
 7+ */
 8+'browsers': {
 9+ // Left-to-right languages
 10+ 'ltr': {
 11+ 'msie': [['>=', 7]],
 12+ 'firefox': [['>=', 3]],
 13+ 'opera': [['>=', 9.6]],
 14+ 'safari': [['>=', 4]]
 15+ },
 16+ // Right-to-left languages
 17+ 'rtl': {
 18+ 'msie': [['>=', 8]],
 19+ 'firefox': [['>=', 3]],
 20+ 'opera': [['>=', 9.6]],
 21+ 'safari': [['>=', 4]]
 22+ }
 23+},
 24+/**
 25+ * Internally used functions
 26+ */
 27+fn: {
 28+ /**
 29+ * Creates a publish module within a wikiEditor
 30+ * @param context Context object of editor to create module in
 31+ * @param config Configuration object to create module from
 32+ */
 33+ create: function( context, config ) {
 34+ // Build the dialog behind the Publish button
 35+ var dialogID = 'wikiEditor-' + context.instance + '-preview-dialog';
 36+ $.wikiEditor.modules.dialogs.fn.create(
 37+ context,
 38+ {
 39+ preview: {
 40+ id: dialogID,
 41+ titleMsg: 'wikieditor-preview-tab',
 42+ html: '\
 43+ <div class="wikiEditor-ui-loading"><span></span></div>\
 44+ <div class="wikiEditor-preview-dialog-contents"></div>\
 45+ ',
 46+ init: function() {
 47+ },
 48+ dialog: {
 49+ buttons: {
 50+ 'wikieditor-publish-dialog-publish': function() {
 51+ var minorChecked = $( '#wikiEditor-' + context.instance +
 52+ '-dialog-minor' ).is( ':checked' ) ?
 53+ 'checked' : '';
 54+ var watchChecked = $( '#wikiEditor-' + context.instance +
 55+ '-dialog-watch' ).is( ':checked' ) ?
 56+ 'checked' : '';
 57+ $( '#wpMinoredit' ).attr( 'checked', minorChecked );
 58+ $( '#wpWatchthis' ).attr( 'checked', watchChecked );
 59+ $( '#wpSummary' ).val( $j( '#wikiEditor-' + context.instance +
 60+ '-dialog-summary' ).val() );
 61+ $( '#editform' ).submit();
 62+ },
 63+ 'wikieditor-publish-dialog-goback': function() {
 64+ $(this).dialog( 'close' );
 65+ }
 66+ },
 67+ resizable: false,
 68+ height: $( 'body' ).height() - 100,
 69+ width: $( 'body' ).width() - 300,
 70+ position: ['center', 'top'],
 71+ open: function() {
 72+ // Gets the latest copy of the wikitext
 73+ var wikitext = context.fn.getContents();
 74+ var $dialog = $( '#' + dialogID );
 75+ $dialog
 76+ .css( 'position', 'relative' )
 77+ .css( 'height', $( 'body' ).height() - 200 )
 78+ .parent()
 79+ .css( 'top', '25px' );
 80+ // $dialog.dialog( 'option', 'width', $( 'body' ).width() - 300 );
 81+ // Aborts when nothing has changed since the last preview
 82+ if ( context.modules.preview.previewText == wikitext ) {
 83+ return;
 84+ }
 85+
 86+ $dialog.find( '.wikiEditor-preview-dialog-contents' ).empty();
 87+ $dialog.find( '.wikiEditor-ui-loading' ).show();
 88+ $.post(
 89+ wgScriptPath + '/api.php',
 90+ {
 91+ 'action': 'parse',
 92+ 'title': wgPageName,
 93+ 'text': wikitext,
 94+ 'prop': 'text',
 95+ 'pst': '',
 96+ 'format': 'json'
 97+ },
 98+ function( data ) {
 99+ if (
 100+ typeof data.parse == 'undefined' ||
 101+ typeof data.parse.text == 'undefined' ||
 102+ typeof data.parse.text['*'] == 'undefined'
 103+ ) {
 104+ return;
 105+ }
 106+ context.modules.preview.previewText = wikitext;
 107+ $dialog.find( '.wikiEditor-ui-loading' ).hide();
 108+ $dialog.find( '.wikiEditor-preview-dialog-contents' )
 109+ .html( '<h1 class="firstHeading" id="firstHeading">'+wgTitle+'</h1>' +
 110+ data.parse.text['*'] )
 111+ .find( 'a:not([href^=#])' ).click( function() { return false; } );
 112+ },
 113+ 'json'
 114+ );
 115+ }
 116+ },
 117+ resizeme: false
 118+ }
 119+ }
 120+ );
 121+ context.fn.addButton( {
 122+ 'captionMsg': 'wikieditor-preview-tab',
 123+ 'action': function() {
 124+ context.$textarea.wikiEditor( 'openDialog', 'preview');
 125+ return false;
 126+ }
 127+ } );
 128+ }
 129+}
 130+
 131+}; } )( jQuery );
Property changes on: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.previewDialog.js
___________________________________________________________________
Added: svn:eol-style
1132 + native
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.js
@@ -0,0 +1,1894 @@
 2+/**
 3+ * This plugin provides a way to build a wiki-text editing user interface around a textarea.
 4+ *
 5+ * @example To intialize without any modules:
 6+ * $j( 'div#edittoolbar' ).wikiEditor();
 7+ *
 8+ * @example To initialize with one or more modules, or to add modules after it's already been initialized:
 9+ * $j( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'toolbar', { ... config ... } );
 10+ *
 11+ */
 12+( function( $ ) {
 13+
 14+/**
 15+ * Global static object for wikiEditor that provides generally useful functionality to all modules and contexts.
 16+ */
 17+$.wikiEditor = {
 18+ /**
 19+ * For each module that is loaded, static code shared by all instances is loaded into this object organized by
 20+ * module name. The existance of a module in this object only indicates the module is available. To check if a
 21+ * module is in use by a specific context check the context.modules object.
 22+ */
 23+ 'modules': {},
 24+ /**
 25+ * In some cases like with the iframe's HTML file, it's convienent to have a lookup table of all instances of the
 26+ * WikiEditor. Each context contains an instance field which contains a key that corrosponds to a reference to the
 27+ * textarea which the WikiEditor was build around. This way, by passing a simple integer you can provide a way back
 28+ * to a specific context.
 29+ */
 30+ 'instances': [],
 31+ /**
 32+ * For each browser name, an array of conditions that must be met are supplied in [operaton, value]-form where
 33+ * operation is a string containing a JavaScript compatible binary operator and value is either a number to be
 34+ * compared with $.browser.versionNumber or a string to be compared with $.browser.version. If a browser is not
 35+ * specifically mentioned, we just assume things will work.
 36+ */
 37+ 'browsers': {
 38+ // Left-to-right languages
 39+ 'ltr': {
 40+ // The toolbar layout is broken in IE6
 41+ 'msie': [['>=', 7]],
 42+ // Layout issues in FF < 2
 43+ 'firefox': [['>=', 2]],
 44+ // Text selection bugs galore - this may be a different situation with the new iframe-based solution
 45+ 'opera': [['>=', 9.6]],
 46+ // jQuery minimums
 47+ 'safari': [['>=', 3]],
 48+ 'chrome': [['>=', 3]],
 49+ 'netscape': [['>=', 9]],
 50+ 'blackberry': false,
 51+ 'ipod': false,
 52+ 'iphone': false
 53+ },
 54+ // Right-to-left languages
 55+ 'rtl': {
 56+ // The toolbar layout is broken in IE 7 in RTL mode, and IE6 in any mode
 57+ 'msie': [['>=', 8]],
 58+ // Layout issues in FF < 2
 59+ 'firefox': [['>=', 2]],
 60+ // Text selection bugs galore - this may be a different situation with the new iframe-based solution
 61+ 'opera': [['>=', 9.6]],
 62+ // jQuery minimums
 63+ 'safari': [['>=', 3]],
 64+ 'chrome': [['>=', 3]],
 65+ 'netscape': [['>=', 9]],
 66+ 'blackberry': false,
 67+ 'ipod': false,
 68+ 'iphone': false
 69+ }
 70+ },
 71+ /**
 72+ * Path to images - this is a bit messy, and it would need to change if this code (and images) gets moved into the
 73+ * core - or anywhere for that matter...
 74+ */
 75+ 'imgPath' : wgScriptPath + '/extensions/UsabilityInitiative/images/wikiEditor/',
 76+ /**
 77+ * Checks the current browser against the browsers object to determine if the browser has been black-listed or not.
 78+ * Because these rules are often very complex, the object contains configurable operators and can check against
 79+ * either the browser version number or string. This process also involves checking if the current browser is amung
 80+ * those which we have configured as compatible or not. If the browser was not configured as comptible we just go on
 81+ * assuming things will work - the argument here is to prevent the need to update the code when a new browser comes
 82+ * to market. The assumption here is that any new browser will be built on an existing engine or be otherwise so
 83+ * similar to another existing browser that things actually do work as expected. The merrits of this argument, which
 84+ * is essentially to blacklist rather than whitelist are debateable, but at this point we've decided it's the more
 85+ * "open-web" way to go.
 86+ * @param module Module object, defaults to $.wikiEditor
 87+ */
 88+ 'isSupported': function( module ) {
 89+ // Fallback to the wikiEditor browser map if no special map is provided in the module
 90+ var mod = module && 'browsers' in module ? module : $.wikiEditor;
 91+ // Check for and make use of cached value and early opportunities to bail
 92+ if ( typeof mod.supported !== 'undefined' ) {
 93+ // Cache hit
 94+ return mod.supported;
 95+ }
 96+ // Run a browser support test and then cache and return the result
 97+ return mod.supported = mw.usability.testBrowser( mod.browsers );
 98+ },
 99+ /**
 100+ * Checks if a module has a specific requirement
 101+ * @param module Module object
 102+ * @param requirement String identifying requirement
 103+ */
 104+ 'isRequired': function( module, requirement ) {
 105+ if ( typeof module['req'] !== 'undefined' ) {
 106+ for ( req in module['req'] ) {
 107+ if ( module['req'][req] == requirement ) {
 108+ return true;
 109+ }
 110+ }
 111+ }
 112+ return false;
 113+ },
 114+ /**
 115+ * Provides a way to extract messages from objects. Wraps the mw.usability.getMsg() function, which
 116+ * may eventually become a wrapper for some kind of core MW functionality.
 117+ *
 118+ * @param object Object to extract messages from
 119+ * @param property String of name of property which contains the message. This should be the base name of the
 120+ * property, which means that in the case of the object { this: 'that', fooMsg: 'bar' }, passing property as 'this'
 121+ * would return the raw text 'that', while passing property as 'foo' would return the internationalized message
 122+ * with the key 'bar'.
 123+ */
 124+ 'autoMsg': function( object, property ) {
 125+ // Accept array of possible properties, of which the first one found will be used
 126+ if ( typeof property == 'object' ) {
 127+ for ( var i in property ) {
 128+ if ( property[i] in object || property[i] + 'Msg' in object ) {
 129+ property = property[i];
 130+ break;
 131+ }
 132+ }
 133+ }
 134+ if ( property in object ) {
 135+ return object[property];
 136+ } else if ( property + 'Msg' in object ) {
 137+ if ( typeof object[property + 'Msg' ] == 'object' ) {
 138+ // [ messageKey, arg1, arg2, ... ]
 139+ return mw.usability.getMsg.apply( mw.usability, object[property + 'Msg' ] );
 140+ } else {
 141+ return mw.usability.getMsg( object[property + 'Msg'] );
 142+ }
 143+ } else {
 144+ return '';
 145+ }
 146+ },
 147+ /**
 148+ * Provides a way to extract a property of an object in a certain language, falling back on the property keyed as
 149+ * 'default'. If such key doesn't exist, the object itself is considered the actual value, which should ideally
 150+ * be the case so that you may use a string or object of any number of strings keyed by language with a default.
 151+ *
 152+ * @param object Object to extract property from
 153+ * @param lang Language code, defaults to wgUserLanguage
 154+ */
 155+ 'autoLang': function( object, lang ) {
 156+ return object[lang || wgUserLanguage] || object['default'] || object;
 157+ },
 158+ /**
 159+ * Provides a way to extract the path of an icon in a certain language, automatically appending a version number for
 160+ * caching purposes and prepending an image path when icon paths are relative.
 161+ *
 162+ * @param icon Icon object from e.g. toolbar config
 163+ * @param path Default icon path, defaults to $.wikiEditor.imgPath
 164+ * @param lang Language code, defaults to wgUserLanguage
 165+ */
 166+ 'autoIcon': function( icon, path, lang ) {
 167+ var src = $.wikiEditor.autoLang( icon, lang );
 168+ path = path || $.wikiEditor.imgPath;
 169+ // Prepend path if src is not absolute
 170+ if ( src.substr( 0, 7 ) != 'http://' && src.substr( 0, 8 ) != 'https://' && src[0] != '/' ) {
 171+ src = path + src;
 172+ }
 173+ return src + '?' + wgWikiEditorIconVersion;
 174+ },
 175+ /**
 176+ * Get the sprite offset for a language if available, icon for a language if available, or the default offset or icon,
 177+ * in that order of preference.
 178+ * @param icon Icon object, see autoIcon()
 179+ * @param offset Offset object
 180+ * @param path Icon path, see autoIcon()
 181+ * @param lang Language code, defaults to wgUserLanguage
 182+ */
 183+ 'autoIconOrOffset': function( icon, offset, path, lang ) {
 184+ lang = lang || wgUserLanguage;
 185+ if ( typeof offset == 'object' && lang in offset ) {
 186+ return offset[lang];
 187+ } else if ( typeof icon == 'object' && lang in icon ) {
 188+ return $.wikiEditor.autoIcon( icon, undefined, lang );
 189+ } else {
 190+ return $.wikiEditor.autoLang( offset, lang );
 191+ }
 192+ }
 193+};
 194+
 195+/**
 196+ * jQuery plugin that provides a way to initialize a wikiEditor instance on a textarea.
 197+ */
 198+$.fn.wikiEditor = function() {
 199+
 200+// Skip any further work when running in browsers that are unsupported
 201+if ( !$j.wikiEditor.isSupported() ) {
 202+ return $(this);
 203+}
 204+
 205+/* Initialization */
 206+
 207+// The wikiEditor context is stored in the element's data, so when this function gets called again we can pick up right
 208+// where we left off
 209+var context = $(this).data( 'wikiEditor-context' );
 210+// On first call, we need to set things up, but on all following calls we can skip right to the API handling
 211+if ( !context || typeof context == 'undefined' ) {
 212+
 213+ // Star filling the context with useful data - any jQuery selections, as usual should be named with a preceding $
 214+ context = {
 215+ // Reference to the textarea element which the wikiEditor is being built around
 216+ '$textarea': $(this),
 217+ // Container for any number of mutually exclusive views that are accessible by tabs
 218+ 'views': {},
 219+ // Container for any number of module-specific data - only including data for modules in use on this context
 220+ 'modules': {},
 221+ // General place to shouve bits of data into
 222+ 'data': {},
 223+ // Unique numeric ID of this instance used both for looking up and differentiating instances of wikiEditor
 224+ 'instance': $.wikiEditor.instances.push( $(this) ) - 1,
 225+ // Array mapping elements in the textarea to character offsets
 226+ 'offsets': null,
 227+ // Cache for context.fn.htmlToText()
 228+ 'htmlToTextMap': {},
 229+ // The previous HTML of the iframe, stored to detect whether something really changed.
 230+ 'oldHTML': null,
 231+ // Same for delayedChange()
 232+ 'oldDelayedHTML': null,
 233+ // The previous selection of the iframe, stored to detect whether the selection has changed
 234+ 'oldDelayedSel': null,
 235+ // Saved selection state for IE
 236+ 'savedSelection': null,
 237+ // Stack of states in { html: [string] } form
 238+ 'history': [],
 239+ // Current history state position - this is number of steps backwards, so it's always -1 or less
 240+ 'historyPosition': -1,
 241+ /// The previous historyPosition, stored to detect if change events were due to an undo or redo action
 242+ 'oldDelayedHistoryPosition': -1
 243+ };
 244+
 245+ /*
 246+ * Externally Accessible API
 247+ *
 248+ * These are available using calls to $j(selection).wikiEditor( call, data ) where selection is a jQuery selection
 249+ * of the textarea that the wikiEditor instance was built around.
 250+ */
 251+
 252+ context.api = {
 253+ /**
 254+ * Activates a module on a specific context with optional configuration data.
 255+ *
 256+ * @param data Either a string of the name of a module to add without any additional configuration parameters,
 257+ * or an object with members keyed with module names and valued with configuration objects.
 258+ */
 259+ 'addModule': function( context, data ) {
 260+ var modules = {};
 261+ if ( typeof data == 'string' ) {
 262+ modules[data] = {};
 263+ } else if ( typeof data == 'object' ) {
 264+ modules = data;
 265+ }
 266+ for ( var module in modules ) {
 267+ // Check for the existance of an available / supported module with a matching name and a create function
 268+ if ( typeof module == 'string' && $.wikiEditor.isSupported( $.wikiEditor.modules[module] ) ) {
 269+ // Extend the context's core API with this module's own API calls
 270+ if ( 'api' in $.wikiEditor.modules[module] ) {
 271+ for ( var call in $.wikiEditor.modules[module].api ) {
 272+ // Modules may not overwrite existing API functions - first come, first serve
 273+ if ( !( call in context.api ) ) {
 274+ context.api[call] = $.wikiEditor.modules[module].api[call];
 275+ }
 276+ }
 277+ }
 278+ // Activate the module on this context
 279+ if ( 'fn' in $.wikiEditor.modules[module] && 'create' in $.wikiEditor.modules[module].fn ) {
 280+ // Add a place for the module to put it's own stuff
 281+ context.modules[module] = {};
 282+ // Tell the module to create itself on the context
 283+ $.wikiEditor.modules[module].fn.create( context, modules[module] );
 284+ }
 285+ }
 286+ }
 287+ }
 288+ };
 289+
 290+ /*
 291+ * Event Handlers
 292+ *
 293+ * These act as filters returning false if the event should be ignored or returning true if it should be passed
 294+ * on to all modules. This is also where we can attach some extra information to the events.
 295+ */
 296+
 297+ context.evt = {
 298+ /**
 299+ * Filters change events, which occur when the user interacts with the contents of the iframe. The goal of this
 300+ * function is to both classify the scope of changes as 'division' or 'character' and to prevent further
 301+ * processing of events which did not actually change the content of the iframe.
 302+ */
 303+ 'keydown': function( event ) {
 304+ switch ( event.which ) {
 305+ case 90: // z
 306+ case 89: // y
 307+ if ( event.which == 89 && !$.browser.msie ) {
 308+ // only handle y events for IE
 309+ return true;
 310+ } else if ( ( event.ctrlKey || event.metaKey ) && context.history.length ) {
 311+ // HistoryPosition is a negative number between -1 and -context.history.length, in other words
 312+ // it's the number of steps backwards from the latest state.
 313+ var newPosition;
 314+ if ( event.shiftKey || event.which == 89 ) {
 315+ // Redo
 316+ newPosition = context.historyPosition + 1;
 317+ } else {
 318+ // Undo
 319+ newPosition = context.historyPosition - 1;
 320+ }
 321+ // Only act if we are switching to a valid state
 322+ if ( newPosition >= ( context.history.length * -1 ) && newPosition < 0 ) {
 323+ // Make sure we run the history storing code before we make this change
 324+ context.fn.updateHistory( context.oldDelayedHTML != context.$content.html() );
 325+ context.oldDelayedHistoryPosition = context.historyPosition;
 326+ context.historyPosition = newPosition;
 327+ // Change state
 328+ // FIXME: Destroys event handlers, will be a problem with template folding
 329+ context.$content.html(
 330+ context.history[context.history.length + context.historyPosition].html
 331+ );
 332+ context.fn.purgeOffsets();
 333+ if( context.history[context.history.length + context.historyPosition].sel ) {
 334+ context.fn.setSelection( {
 335+ start: context.history[context.history.length + context.historyPosition].sel[0],
 336+ end: context.history[context.history.length + context.historyPosition].sel[1]
 337+ } );
 338+ }
 339+ }
 340+ // Prevent the browser from jumping in and doing its stuff
 341+ return false;
 342+ }
 343+ break;
 344+ // Intercept all tab events to provide consisten behavior across browsers
 345+ // Webkit browsers insert tab characters by default into the iframe rather than changing input focus
 346+ case 9: //tab
 347+ // if any modifier keys are pressed, allow the browser to do it's thing
 348+ if ( event.ctrlKey || event.altKey || event.shiftKey ) {
 349+ return true;
 350+ } else {
 351+ var $tabindexList = $j( '[tabindex]:visible' ).sort( function( a, b ) {
 352+ return a.tabIndex - b.tabIndex;
 353+ } );
 354+ for( var i=0; i < $tabindexList.length; i++ ) {
 355+ if( $tabindexList.eq( i ).attr('id') == context.$iframe.attr( 'id' ) ) {
 356+ $tabindexList.get( i + 1 ).focus();
 357+ break;
 358+ }
 359+ }
 360+ return false;
 361+ }
 362+ break;
 363+ case 86: //v
 364+ if ( event.ctrlKey && $.browser.msie ) {
 365+ //paste, intercepted for IE
 366+ context.evt.paste( event );
 367+ }
 368+ break;
 369+ }
 370+ return true;
 371+ },
 372+ 'change': function( event ) {
 373+ event.data.scope = 'division';
 374+ var newHTML = context.$content.html();
 375+ if ( context.oldHTML != newHTML ) {
 376+ context.fn.purgeOffsets();
 377+ context.oldHTML = newHTML;
 378+ event.data.scope = 'realchange';
 379+ }
 380+ // Never let the body be totally empty
 381+ if ( context.$content.children().length == 0 ) {
 382+ context.$content.append( '<p></p>' );
 383+ }
 384+ return true;
 385+ },
 386+ 'delayedChange': function( event ) {
 387+ event.data.scope = 'division';
 388+ var newHTML = context.$content.html();
 389+ if ( context.oldDelayedHTML != newHTML ) {
 390+ context.oldDelayedHTML = newHTML;
 391+ event.data.scope = 'realchange';
 392+ // Surround by <p> if it does not already have it
 393+ var cursorPos = context.fn.getCaretPosition();
 394+ var t = context.fn.getOffset( cursorPos[0] );
 395+ if ( ! $.browser.msie && t && t.node.nodeName == '#text' && t.node.parentNode.nodeName.toLowerCase() == 'body' ) {
 396+ $( t.node ).wrap( "<p></p>" );
 397+ context.fn.purgeOffsets();
 398+ context.fn.setSelection( { start: cursorPos[0], end: cursorPos[1] } );
 399+ }
 400+ }
 401+ context.fn.updateHistory( event.data.scope == 'realchange' );
 402+ return true;
 403+ },
 404+ 'cut': function( event ) {
 405+ setTimeout( function() {
 406+ context.$content.find( 'br' ).each( function() {
 407+ if ( $(this).parent().is( 'body' ) ) {
 408+ $(this).wrap( $( '<p></p>' ) );
 409+ }
 410+ } );
 411+ }, 100 );
 412+ return true;
 413+ },
 414+ 'paste': function( event ) {
 415+ // Save the cursor position to restore it after all this voodoo
 416+ var cursorPos = context.fn.getCaretPosition();
 417+ var oldLength = context.fn.getContents().length;
 418+ var positionFromEnd = oldLength - cursorPos[1];
 419+
 420+ //give everything the wikiEditor class so that we can easily pick out things without that class as pasted
 421+ context.$content.find( '*' ).addClass( 'wikiEditor' );
 422+ if ( $.layout.name !== 'webkit' ) {
 423+ context.$content.addClass( 'pasting' );
 424+ }
 425+
 426+ setTimeout( function() {
 427+ // Kill stuff we know we don't want
 428+ context.$content.find( 'script,style,img,input,select,textarea,hr,button,link,meta' ).remove();
 429+ var nodeToDelete = [];
 430+ var pastedContent = [];
 431+ var firstDirtyNode;
 432+ var $lastDirtyNode;
 433+ var elementAtCursor;
 434+ if ( $.browser.msie && !context.offsets ) {
 435+ elementAtCursor = null;
 436+ } else {
 437+ elementAtCursor = context.fn.getOffset( cursorPos[0] );
 438+ }
 439+ if ( elementAtCursor == null || elementAtCursor.node == null ) {
 440+ context.$content.prepend( '<p class = wikiEditor></p>' );
 441+ firstDirtyNode = context.$content.children()[0];
 442+ } else {
 443+ firstDirtyNode = elementAtCursor.node;
 444+ }
 445+
 446+ //this is ugly but seems like the best way to handle the case where we select and replace all editor contents
 447+ try {
 448+ firstDirtyNode.parentNode;
 449+ } catch ( err ) {
 450+ context.$content.prepend( '<p class = wikiEditor></p>' );
 451+ firstDirtyNode = context.$content.children()[0];
 452+ }
 453+
 454+ while ( firstDirtyNode != null ) {
 455+ //we're going to replace the contents of the entire parent node.
 456+ while ( firstDirtyNode.parentNode && firstDirtyNode.parentNode.nodeName != 'BODY'
 457+ && ! $( firstDirtyNode ).hasClass( 'wikiEditor' )
 458+ ) {
 459+ firstDirtyNode = firstDirtyNode.parentNode;
 460+ }
 461+ //go back till we find the first pasted node
 462+ while ( firstDirtyNode.previousSibling != null
 463+ && ! $( firstDirtyNode.previousSibling ).hasClass( 'wikiEditor' )
 464+ ) {
 465+
 466+ if ( $( firstDirtyNode.previousSibling ).hasClass( '#comment' ) ) {
 467+ $( firstDirtyNode ).remove();
 468+ } else {
 469+ firstDirtyNode = firstDirtyNode.previousSibling;
 470+ }
 471+ }
 472+
 473+ if ( firstDirtyNode.previousSibling != null ) {
 474+ $lastDirtyNode = $( firstDirtyNode.previousSibling );
 475+ } else {
 476+ $lastDirtyNode = $( firstDirtyNode );
 477+ }
 478+
 479+ var cc = makeContentCollector( $.browser, null );
 480+ while ( firstDirtyNode != null ) {
 481+ cc.collectContent(firstDirtyNode);
 482+ cc.notifyNextNode(firstDirtyNode.nextSibling);
 483+
 484+ nodeToDelete.push( firstDirtyNode );
 485+
 486+ firstDirtyNode = firstDirtyNode.nextSibling;
 487+ if ( $( firstDirtyNode ).hasClass( 'wikiEditor' ) ) {
 488+ break;
 489+ }
 490+ }
 491+
 492+ var ccData = cc.finish();
 493+ pastedContent = ccData.lines;
 494+ var pastedPretty = '';
 495+ for ( var i = 0; i < pastedContent.length; i++ ) {
 496+ //escape html
 497+ pastedPretty = pastedContent[i].replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\r?\n/g, '\\n');
 498+ //replace leading white spaces with &nbsp;
 499+ match = pastedContent[i].match(/^[\s]+[^\s]/);
 500+ if ( match != null && match.length > 0 ) {
 501+ index = match[0].length;
 502+ leadingSpace = match[0].replace(/[\s]/g, '&nbsp;');
 503+ pastedPretty = leadingSpace + pastedPretty.substring(index, pastedPretty.length);
 504+ }
 505+
 506+
 507+ if( !pastedPretty && $.browser.msie && i == 0 ) {
 508+ continue;
 509+ }
 510+ $newElement = $( '<p class="wikiEditor pasted" ></p>' );
 511+ if ( pastedPretty ) {
 512+ $newElement.html( pastedPretty );
 513+ } else {
 514+ $newElement.html( '<br class="wikiEditor">' );
 515+ }
 516+ $newElement.insertAfter( $lastDirtyNode );
 517+
 518+ $lastDirtyNode = $newElement;
 519+
 520+ }
 521+
 522+ //now delete all the original nodes that we prettified already
 523+ while ( nodeToDelete.length > 0 ) {
 524+ $deleteNode = $( nodeToDelete.pop() );
 525+ $deleteNode.remove();
 526+ }
 527+
 528+ //anything without wikiEditor class was pasted.
 529+ $selection = context.$content.find( ':not(.wikiEditor)' );
 530+ if ( $selection.length == 0 ) {
 531+ break;
 532+ } else {
 533+ firstDirtyNode = $selection.eq( 0 )[0];
 534+ }
 535+ }
 536+ context.$content.find( '.wikiEditor' ).removeClass( 'wikiEditor' );
 537+
 538+ //now place the cursor at the end of pasted content
 539+ var newLength = context.fn.getContents().length;
 540+ var newPos = newLength - positionFromEnd;
 541+
 542+ context.fn.purgeOffsets();
 543+ context.fn.setSelection( { start: newPos, end: newPos } );
 544+
 545+ context.fn.scrollToCaretPosition();
 546+
 547+
 548+ }, 0 );
 549+ return true;
 550+ },
 551+ 'ready': function( event ) {
 552+ // Initialize our history queue
 553+ if ( context.$content ) {
 554+ context.history.push( { 'html': context.$content.html(), 'sel': context.fn.getCaretPosition() } );
 555+ } else {
 556+ context.history.push( { 'html': '', 'sel': context.fn.getCaretPosition() } );
 557+ }
 558+ return true;
 559+ }
 560+ };
 561+
 562+ /* Internal Functions */
 563+
 564+ context.fn = {
 565+ /**
 566+ * Executes core event filters as well as event handlers provided by modules.
 567+ */
 568+ 'trigger': function( name, event ) {
 569+ // Event is an optional argument, but from here on out, at least the type field should be dependable
 570+ if ( typeof event == 'undefined' ) {
 571+ event = { 'type': 'custom' };
 572+ }
 573+ // Ensure there's a place for extra information to live
 574+ if ( typeof event.data == 'undefined' ) {
 575+ event.data = {};
 576+ }
 577+
 578+ // Allow filtering to occur
 579+ if ( name in context.evt ) {
 580+ if ( !context.evt[name]( event ) ) {
 581+ return false;
 582+ }
 583+ }
 584+ var returnFromModules = null; //they return null by default
 585+ // Pass the event around to all modules activated on this context
 586+
 587+ for ( var module in context.modules ) {
 588+ if (
 589+ module in $.wikiEditor.modules &&
 590+ 'evt' in $.wikiEditor.modules[module] &&
 591+ name in $.wikiEditor.modules[module].evt
 592+ ) {
 593+ var ret = $.wikiEditor.modules[module].evt[name]( context, event );
 594+ if (ret != null) {
 595+ //if 1 returns false, the end result is false
 596+ if( returnFromModules == null ) {
 597+ returnFromModules = ret;
 598+ } else {
 599+ returnFromModules = returnFromModules && ret;
 600+ }
 601+ }
 602+ }
 603+ }
 604+ if ( returnFromModules != null ) {
 605+ return returnFromModules;
 606+ } else {
 607+ return true;
 608+ }
 609+ },
 610+ /**
 611+ * Adds a button to the UI
 612+ */
 613+ 'addButton': function( options ) {
 614+ // Ensure that buttons and tabs are visible
 615+ context.$controls.show();
 616+ context.$buttons.show();
 617+ return $( '<button />' )
 618+ .text( $.wikiEditor.autoMsg( options, 'caption' ) )
 619+ .click( options.action )
 620+ .appendTo( context.$buttons );
 621+ },
 622+ /**
 623+ * Adds a view to the UI, which is accessed using a set of tabs. Views are mutually exclusive and by default a
 624+ * wikitext view will be present. Only when more than one view exists will the tabs will be visible.
 625+ */
 626+ 'addView': function( options ) {
 627+ // Adds a tab
 628+ function addTab( options ) {
 629+ // Ensure that buttons and tabs are visible
 630+ context.$controls.show();
 631+ context.$tabs.show();
 632+ // Return the newly appended tab
 633+ return $( '<div></div>' )
 634+ .attr( 'rel', 'wikiEditor-ui-view-' + options.name )
 635+ .addClass( context.view == options.name ? 'current' : null )
 636+ .append( $( '<a></a>' )
 637+ .attr( 'href', '#' )
 638+ .mousedown( function() {
 639+ // No dragging!
 640+ return false;
 641+ } )
 642+ .click( function( event ) {
 643+ context.$ui.find( '.wikiEditor-ui-view' ).hide();
 644+ context.$ui.find( '.' + $(this).parent().attr( 'rel' ) ).show();
 645+ context.$tabs.find( 'div' ).removeClass( 'current' );
 646+ $(this).parent().addClass( 'current' );
 647+ $(this).blur();
 648+ if ( 'init' in options && typeof options.init == 'function' ) {
 649+ options.init( context );
 650+ }
 651+ event.preventDefault();
 652+ return false;
 653+ } )
 654+ .text( $.wikiEditor.autoMsg( options, 'title' ) )
 655+ )
 656+ .appendTo( context.$tabs );
 657+ }
 658+ // Automatically add the previously not-needed wikitext tab
 659+ if ( !context.$tabs.children().size() ) {
 660+ addTab( { 'name': 'wikitext', 'titleMsg': 'wikieditor-wikitext-tab' } );
 661+ }
 662+ // Add the tab for the view we were actually asked to add
 663+ addTab( options );
 664+ // Return newly appended view
 665+ return $( '<div></div>' )
 666+ .addClass( 'wikiEditor-ui-view wikiEditor-ui-view-' + options.name )
 667+ .hide()
 668+ .appendTo( context.$ui );
 669+ },
 670+ 'highlightLine': function( $element, mode ) {
 671+ if ( !$element.is( 'p' ) ) {
 672+ $element = $element.closest( 'p' );
 673+ }
 674+ $element.css( 'backgroundColor', '#AACCFF' );
 675+ setTimeout( function() { $element.animate( { 'backgroundColor': 'white' }, 'slow' ); }, 100 );
 676+ setTimeout( function() { $element.css( 'backgroundColor', 'white' ); }, 1000 );
 677+ },
 678+ 'htmlToText': function( html ) {
 679+ // This function is slow for large inputs, so aggressively cache input/output pairs
 680+ if ( html in context.htmlToTextMap ) {
 681+ return context.htmlToTextMap[html];
 682+ }
 683+ var origHTML = html;
 684+
 685+ // We use this elaborate trickery for cross-browser compatibility
 686+ // IE does overzealous whitespace collapsing for $( '<pre />' ).html( html );
 687+ // We also do <br> and easy cases for <p> conversion here, complicated cases are handled later
 688+ html = html
 689+ .replace( /\r?\n/g, "" ) // IE7 inserts newlines before block elements
 690+ .replace( /&nbsp;/g, " " ) // We inserted these to prevent IE from collapsing spaces
 691+ .replace( /\<br[^\>]*\>\<\/p\>/gi, '</p>' ) // Remove trailing <br> from <p>
 692+ .replace( /\<\/p\>\s*\<p[^\>]*\>/gi, "\n" ) // Easy case for <p> conversion
 693+ .replace( /\<br[^\>]*\>/gi, "\n" ) // <br> conversion
 694+ .replace( /\<\/p\>(\n*)\<p[^\>]*\>/gi, "$1\n" )
 695+ // Un-nest <p> tags
 696+ .replace( /\<p[^\>]*\><p[^\>]*\>/gi, '<p>' )
 697+ .replace( /\<\/p\><\/p\>/gi, '</p>' );
 698+ // Save leading and trailing whitespace now and restore it later. IE eats it all, and even Firefox
 699+ // won't leave everything alone
 700+ var leading = html.match( /^\s*/ )[0];
 701+ var trailing = html.match( /\s*$/ )[0];
 702+ html = html.substr( leading.length, html.length - leading.length - trailing.length );
 703+ var $pre = $( '<pre>' + html + '</pre>' );
 704+ $pre.find( '.wikiEditor-noinclude' ).each( function() { $( this ).remove(); } );
 705+ // Convert tabs, <p>s and <br>s back
 706+ $pre.find( '.wikiEditor-tab' ).each( function() { $( this ).text( "\t" ); } );
 707+ $pre.find( 'br' ).each( function() { $( this ).replaceWith( "\n" ); } );
 708+ // Converting <p>s is wrong if there's nothing before them, so check that.
 709+ // .find( '* + p' ) isn't good enough because textnodes aren't considered
 710+ $pre.find( 'p' ).each( function() {
 711+ var text = $( this ).text();
 712+ // If this <p> is preceded by some text, add a \n at the beginning, and if
 713+ // it's followed by a textnode, add a \n at the end
 714+ // We need the traverser because there can be other weird stuff in between
 715+
 716+ // Check for preceding text
 717+ var t = new context.fn.rawTraverser( this.firstChild, this, $pre.get( 0 ), true ).prev();
 718+ while ( t && t.node.nodeName != '#text' && t.node.nodeName != 'BR' && t.node.nodeName != 'P' ) {
 719+ t = t.prev();
 720+ }
 721+ if ( t ) {
 722+ text = "\n" + text;
 723+ }
 724+
 725+ // Check for following text
 726+ t = new context.fn.rawTraverser( this.lastChild, this, $pre.get( 0 ), true ).next();
 727+ while ( t && t.node.nodeName != '#text' && t.node.nodeName != 'BR' && t.node.nodeName != 'P' ) {
 728+ t = t.next();
 729+ }
 730+ if ( t && !t.inP && t.node.nodeName == '#text' && t.node.nodeValue.charAt( 0 ) != '\n'
 731+ && t.node.nodeValue.charAt( 0 ) != '\r' ) {
 732+ text += "\n";
 733+ }
 734+ $( this ).text( text );
 735+ } );
 736+ var retval;
 737+ if ( $.browser.msie ) {
 738+ // IE aggressively collapses whitespace in .text() after having done DOM manipulation,
 739+ // but for some crazy reason this does work. Also convert \r back to \n
 740+ retval = $( '<pre>' + $pre.html() + '</pre>' ).text().replace( /\r/g, '\n' );
 741+ } else {
 742+ retval = $pre.text();
 743+ }
 744+ return context.htmlToTextMap[origHTML] = leading + retval + trailing;
 745+ },
 746+ /**
 747+ * Get the first element before the selection that's in a certain class
 748+ * @param classname Class to match. Defaults to '', meaning any class
 749+ * @param strict If true, the element the selection starts in cannot match (default: false)
 750+ * @return jQuery object or null if unknown
 751+ */
 752+ 'beforeSelection': function( classname, strict ) {
 753+ if ( typeof classname == 'undefined' ) {
 754+ classname = '';
 755+ }
 756+ var e = null, offset = null;
 757+ if ( $.browser.msie && !context.$iframe[0].contentWindow.document.body ) {
 758+ return null;
 759+ }
 760+ if ( context.$iframe[0].contentWindow.getSelection ) {
 761+ // Firefox and Opera
 762+ var selection = context.$iframe[0].contentWindow.getSelection();
 763+ // On load, webkit seems to not have a valid selection
 764+ if ( selection.baseNode !== null ) {
 765+ // Start at the selection's start and traverse the DOM backwards
 766+ // This is done by traversing an element's children first, then the element itself, then its parent
 767+ e = selection.getRangeAt( 0 ).startContainer;
 768+ offset = selection.getRangeAt( 0 ).startOffset;
 769+ } else {
 770+ return null;
 771+ }
 772+
 773+ // When the cursor is on an empty line, Opera gives us a bogus range object with
 774+ // startContainer=endContainer=body and startOffset=endOffset=1
 775+ var body = context.$iframe[0].contentWindow.document.body;
 776+ if ( $.browser.opera && e == body && offset == 1 ) {
 777+ return null;
 778+ }
 779+ }
 780+ if ( !e && context.$iframe[0].contentWindow.document.selection ) {
 781+ // IE
 782+ // Because there's nothing like range.startContainer in IE, we need to do a DOM traversal
 783+ // to find the element the start of the selection is in
 784+ var range = context.$iframe[0].contentWindow.document.selection.createRange();
 785+ // Set range2 to the text before the selection
 786+ var range2 = context.$iframe[0].contentWindow.document.body.createTextRange();
 787+ // For some reason this call throws errors in certain cases, e.g. when the selection is
 788+ // not in the iframe
 789+ try {
 790+ range2.setEndPoint( 'EndToStart', range );
 791+ } catch ( ex ) {
 792+ return null;
 793+ }
 794+ var seekPos = context.fn.htmlToText( range2.htmlText ).length;
 795+ var offset = context.fn.getOffset( seekPos );
 796+ e = offset ? offset.node : null;
 797+ offset = offset ? offset.offset : null;
 798+ if ( !e ) {
 799+ return null;
 800+ }
 801+ }
 802+ if ( e.nodeName != '#text' ) {
 803+ // The selection is not in a textnode, but between two non-text nodes
 804+ // (usually inside the <body> between two <br>s). Go to the rightmost
 805+ // child of the node just before the selection
 806+ var newE = e.firstChild;
 807+ for ( var i = 0; i < offset - 1 && newE; i++ ) {
 808+ newE = newE.nextSibling;
 809+ }
 810+ while ( newE && newE.lastChild ) {
 811+ newE = newE.lastChild;
 812+ }
 813+ e = newE || e;
 814+ }
 815+
 816+ // We'd normally use if( $( e ).hasClass( class ) in the while loop, but running the jQuery
 817+ // constructor thousands of times is very inefficient
 818+ var classStr = ' ' + classname + ' ';
 819+ while ( e ) {
 820+ if ( !strict && ( !classname || ( ' ' + e.className + ' ' ).indexOf( classStr ) != -1 ) ) {
 821+ return $( e );
 822+ }
 823+ var next = e.previousSibling;
 824+ while ( next && next.lastChild ) {
 825+ next = next.lastChild;
 826+ }
 827+ e = next || e.parentNode;
 828+ strict = false;
 829+ }
 830+ return $( [] );
 831+ },
 832+ /**
 833+ * Object used by traverser(). Don't use this unless you know what you're doing
 834+ */
 835+ 'rawTraverser': function( node, inP, ancestor, skipNoinclude ) {
 836+ this.node = node;
 837+ this.inP = inP;
 838+ this.ancestor = ancestor;
 839+ this.skipNoinclude = skipNoinclude;
 840+ this.next = function() {
 841+ var p = this.node;
 842+ var nextInP = this.inP;
 843+ while ( p && !p.nextSibling ) {
 844+ p = p.parentNode;
 845+ if ( p == this.ancestor ) {
 846+ // We're back at the ancestor, stop here
 847+ p = null;
 848+ }
 849+ if ( p && p.nodeName == "P" ) {
 850+ nextInP = null;
 851+ }
 852+ }
 853+ p = p ? p.nextSibling : null;
 854+ if ( p && p.nodeName == "P" ) {
 855+ nextInP = p;
 856+ }
 857+ do {
 858+ // Filter nodes with the wikiEditor-noinclude class
 859+ // Don't use $( p ).hasClass( 'wikiEditor-noinclude' ) because
 860+ // $() is slow in a tight loop
 861+ if ( this.skipNoinclude ) {
 862+ while ( p && ( ' ' + p.className + ' ' ).indexOf( ' wikiEditor-noinclude ' ) != -1 ) {
 863+ p = p.nextSibling;
 864+ }
 865+ }
 866+ if ( p && p.firstChild ) {
 867+ p = p.firstChild;
 868+ if ( p.nodeName == "P" ) {
 869+ nextInP = p;
 870+ }
 871+ }
 872+ } while ( p && p.firstChild );
 873+ // Instead of calling the rawTraverser constructor, inline it. This avoids function call overhead
 874+ return p ? { 'node': p, 'inP': nextInP, 'ancestor': this.ancestor,
 875+ 'skipNoinclude': this.skipNoinclude, 'next': this.next, 'prev': this.prev } : null;
 876+ };
 877+ this.prev = function() {
 878+ var p = this.node;
 879+ var prevInP = this.inP;
 880+ while ( p && !p.previousSibling ) {
 881+ p = p.parentNode;
 882+ if ( p == this.ancestor ) {
 883+ // We're back at the ancestor, stop here
 884+ p = null;
 885+ }
 886+ if ( p && p.nodeName == "P" ) {
 887+ prevInP = null;
 888+ }
 889+ }
 890+ p = p ? p.previousSibling : null;
 891+ if ( p && p.nodeName == "P" ) {
 892+ prevInP = p;
 893+ }
 894+ do {
 895+ // Filter nodes with the wikiEditor-noinclude class
 896+ // Don't use $( p ).hasClass( 'wikiEditor-noinclude' ) because
 897+ // $() is slow in a tight loop
 898+ if ( this.skipNoinclude ) {
 899+ while ( p && ( ' ' + p.className + ' ' ).indexOf( ' wikiEditor-noinclude ' ) != -1 ) {
 900+ p = p.previousSibling;
 901+ }
 902+ }
 903+ if ( p && p.lastChild ) {
 904+ p = p.lastChild;
 905+ if ( p.nodeName == "P" ) {
 906+ prevInP = p;
 907+ }
 908+ }
 909+ } while ( p && p.lastChild );
 910+ // Instead of calling the rawTraverser constructor, inline it. This avoids function call overhead
 911+ return p ? { 'node': p, 'inP': prevInP, 'ancestor': this.ancestor,
 912+ 'skipNoinclude': this.skipNoinclude, 'next': this.next, 'prev': this.prev } : null;
 913+ };
 914+ },
 915+ /**
 916+ * Get an object used to traverse the leaf nodes in the iframe DOM. This traversal skips leaf nodes
 917+ * inside an element with the wikiEditor-noinclude class. This basically wraps rawTraverser
 918+ *
 919+ * @param start Node to start at
 920+ * @return Traverser object, use .next() or .prev() to get a traverser object referring to the
 921+ * previous/next node
 922+ */
 923+ 'traverser': function( start ) {
 924+ // Find the leftmost leaf node in the tree
 925+ var startNode = start.jquery ? start.get( 0 ) : start;
 926+ var node = startNode;
 927+ var inP = node.nodeName == "P" ? node : null;
 928+ do {
 929+ // Filter nodes with the wikiEditor-noinclude class
 930+ // Don't use $( p ).hasClass( 'wikiEditor-noinclude' ) because
 931+ // $() is slow in a tight loop
 932+ while ( node && ( ' ' + node.className + ' ' ).indexOf( ' wikiEditor-noinclude ' ) != -1 ) {
 933+ node = node.nextSibling;
 934+ }
 935+ if ( node && node.firstChild ) {
 936+ node = node.firstChild;
 937+ if ( node.nodeName == "P" ) {
 938+ inP = node;
 939+ }
 940+ }
 941+ } while ( node && node.firstChild );
 942+ return new context.fn.rawTraverser( node, inP, startNode, true );
 943+ },
 944+ 'getOffset': function( offset ) {
 945+ if ( !context.offsets ) {
 946+ context.fn.refreshOffsets();
 947+ }
 948+ if ( offset in context.offsets ) {
 949+ return context.offsets[offset];
 950+ }
 951+ // Our offset is not pre-cached. Find the highest offset below it and interpolate
 952+ // We need to traverse the entire object because for() doesn't traverse in order
 953+ // We don't do in-order traversal because the object is sparse
 954+ var lowerBound = -1;
 955+ for ( var o in context.offsets ) {
 956+ var realO = parseInt( o );
 957+ if ( realO < offset && realO > lowerBound) {
 958+ lowerBound = realO;
 959+ }
 960+ }
 961+ if ( !( lowerBound in context.offsets ) ) {
 962+ // Weird edge case: either offset is too large or the document is empty
 963+ return null;
 964+ }
 965+ var base = context.offsets[lowerBound];
 966+ return context.offsets[offset] = {
 967+ 'node': base.node,
 968+ 'offset': base.offset + offset - lowerBound,
 969+ 'length': base.length,
 970+ 'lastTextNode': base.lastTextNode
 971+ };
 972+ },
 973+ 'purgeOffsets': function() {
 974+ context.offsets = null;
 975+ },
 976+ 'refreshOffsets': function() {
 977+ context.offsets = [ ];
 978+ var t = context.fn.traverser( context.$content );
 979+ var pos = 0, lastTextNode = null;
 980+ while ( t ) {
 981+ if ( t.node.nodeName != '#text' && t.node.nodeName != 'BR' ) {
 982+ t = t.next();
 983+ continue;
 984+ }
 985+ var nextPos = t.node.nodeName == '#text' ? pos + t.node.nodeValue.length : pos + 1;
 986+ var nextT = t.next();
 987+ var leavingP = t.node.nodeName == '#text' && t.inP && nextT && ( !nextT.inP || nextT.inP != t.inP );
 988+ context.offsets[pos] = {
 989+ 'node': t.node,
 990+ 'offset': 0,
 991+ 'length': nextPos - pos + ( leavingP ? 1 : 0 ),
 992+ 'lastTextNode': lastTextNode
 993+ };
 994+ if ( leavingP ) {
 995+ // <p>Foo</p> looks like "Foo\n", make it quack like it too
 996+ // Basically we're faking the \n character much like we're treating <br>s
 997+ context.offsets[nextPos] = {
 998+ 'node': t.node,
 999+ 'offset': nextPos - pos,
 1000+ 'length': nextPos - pos + 1,
 1001+ 'lastTextNode': lastTextNode
 1002+ };
 1003+ }
 1004+ pos = nextPos + ( leavingP ? 1 : 0 );
 1005+ if ( t.node.nodeName == '#text' ) {
 1006+ lastTextNode = t.node;
 1007+ }
 1008+ t = nextT;
 1009+ }
 1010+ },
 1011+ 'saveSelection': function() {
 1012+ if ( !$.browser.msie ) {
 1013+ // Only IE needs this
 1014+ return;
 1015+ }
 1016+ if ( typeof context.$iframe != 'undefined' ) {
 1017+ context.$iframe[0].contentWindow.focus();
 1018+ context.savedSelection = context.$iframe[0].contentWindow.document.selection.createRange();
 1019+ } else {
 1020+ context.$textarea.focus();
 1021+ context.savedSelection = document.selection.createRange();
 1022+ }
 1023+ },
 1024+ 'restoreSelection': function() {
 1025+ if ( !$.browser.msie || context.savedSelection === null ) {
 1026+ return;
 1027+ }
 1028+ if ( typeof context.$iframe != 'undefined' ) {
 1029+ context.$iframe[0].contentWindow.focus();
 1030+ } else {
 1031+ context.$textarea.focus();
 1032+ }
 1033+ context.savedSelection.select();
 1034+ context.savedSelection = null;
 1035+ },
 1036+ /**
 1037+ * Update the history queue
 1038+ *
 1039+ * @param htmlChange pass true or false to inidicate if there was a text change that should potentially
 1040+ * be given a new history state.
 1041+ */
 1042+ 'updateHistory': function( htmlChange ) {
 1043+ var newHTML = context.$content.html();
 1044+ var newSel = context.fn.getCaretPosition();
 1045+ // Was text changed? Was it because of a REDO or UNDO action?
 1046+ if (
 1047+ context.history.length == 0 ||
 1048+ ( htmlChange && context.oldDelayedHistoryPosition == context.historyPosition )
 1049+ ) {
 1050+ context.oldDelayedSel = newSel;
 1051+ // Do we need to trim extras from our history?
 1052+ // FIXME: this should really be happing on change, not on the delay
 1053+ if ( context.historyPosition < -1 ) {
 1054+ //clear out the extras
 1055+ context.history.splice( context.history.length + context.historyPosition + 1 );
 1056+ context.historyPosition = -1;
 1057+ }
 1058+ context.history.push( { 'html': newHTML, 'sel': newSel } );
 1059+ // If the history has grown longer than 10 items, remove the earliest one
 1060+ while ( context.history.length > 10 ) {
 1061+ context.history.shift();
 1062+ }
 1063+ } else if ( context.oldDelayedSel != newSel ) {
 1064+ // If only the selection was changed, update it
 1065+ context.oldDelayedSel = newSel;
 1066+ context.history[context.history.length + context.historyPosition].sel = newSel;
 1067+ }
 1068+ // synch our old delayed history position until the next undo/redo action
 1069+ context.oldDelayedHistoryPosition = context.historyPosition;
 1070+ },
 1071+ /**
 1072+ * Sets up the iframe in place of the textarea to allow more advanced operations
 1073+ */
 1074+ 'setupIframe': function() {
 1075+ context.$iframe = $( '<iframe></iframe>' )
 1076+ .attr( {
 1077+ 'frameBorder': 0,
 1078+ 'border': 0,
 1079+ 'tabindex': 1,
 1080+ 'src': wgScriptPath + '/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.html?' +
 1081+ 'instance=' + context.instance + '&ts=' + ( new Date() ).getTime() + '&is=content',
 1082+ 'id': 'wikiEditor-iframe-' + context.instance
 1083+ } )
 1084+ .css( {
 1085+ 'backgroundColor': 'white',
 1086+ 'width': '100%',
 1087+ 'height': context.$textarea.height(),
 1088+ 'display': 'none',
 1089+ 'overflow-y': 'scroll',
 1090+ 'overflow-x': 'hidden'
 1091+ } )
 1092+ .insertAfter( context.$textarea )
 1093+ .load( function() {
 1094+ // Internet Explorer will reload the iframe once we turn on design mode, so we need to only turn it
 1095+ // on during the first run, and then bail
 1096+ if ( !this.isSecondRun ) {
 1097+ // Turn the document's design mode on
 1098+ context.$iframe[0].contentWindow.document.designMode = 'on';
 1099+ // Let the rest of this function happen next time around
 1100+ if ( $.browser.msie ) {
 1101+ this.isSecondRun = true;
 1102+ return;
 1103+ }
 1104+ }
 1105+ // Get a reference to the content area of the iframe
 1106+ context.$content = $( context.$iframe[0].contentWindow.document.body );
 1107+ // Add classes to the body to influence the styles based on what's enabled
 1108+ for ( module in context.modules ) {
 1109+ context.$content.addClass( 'wikiEditor-' + module );
 1110+ }
 1111+ // If we just do "context.$content.text( context.$textarea.val() )", Internet Explorer will strip
 1112+ // out the whitespace charcters, specifically "\n" - so we must manually encode text and append it
 1113+ // TODO: Refactor this into a textToHtml() function
 1114+ var html = context.$textarea.val()
 1115+ // We're gonna use &esc; as an escape sequence
 1116+ .replace( /&esc;/g, '&esc;esc;' )
 1117+ // Escape existing uses of <p>, </p>, &nbsp; and <span class="wikiEditor-tab"></span>
 1118+ .replace( /\<p\>/g, '&esc;&lt;p&gt;' )
 1119+ .replace( /\<\/p\>/g, '&esc;&lt;/p&gt;' )
 1120+ .replace(
 1121+ /\<span class="wikiEditor-tab"\>\<\/span\>/g,
 1122+ '&esc;&lt;span&nbsp;class=&quot;wikiEditor-tab&quot;&gt;&lt;/span&gt;'
 1123+ )
 1124+ .replace( /&nbsp;/g, '&esc;&amp;nbsp;' );
 1125+ // We must do some extra processing on IE to avoid dirty diffs, specifically IE will collapse
 1126+ // leading spaces - browser sniffing is not ideal, but executing this code on a non-broken browser
 1127+ // doesn't cause harm
 1128+ if ( $.browser.msie ) {
 1129+ html = html.replace( /\t/g, '<span class="wikiEditor-tab"></span>' );
 1130+ if ( $.browser.versionNumber <= 7 ) {
 1131+ // Replace all spaces matching &nbsp; - IE <= 7 needs this because of its overzealous
 1132+ // whitespace collapsing
 1133+ html = html.replace( / /g, "&nbsp;" );
 1134+ } else {
 1135+ // IE8 is happy if we just convert the first leading space to &nbsp;
 1136+ html = html.replace( /(^|\n) /g, "$1&nbsp;" );
 1137+ }
 1138+ }
 1139+ // Use a dummy div to escape all entities
 1140+ // This'll also escape <br>, <span> and &nbsp; , so we unescape those after
 1141+ // We also need to unescape the doubly-escaped things mentioned above
 1142+ html = $( '<div />' ).text( '<p>' + html.replace( /\r?\n/g, '</p><p>' ) + '</p>' ).html()
 1143+ .replace( /&amp;nbsp;/g, '&nbsp;' )
 1144+ // Allow <p> tags to survive encoding
 1145+ .replace( /&lt;p&gt;/g, '<p>' )
 1146+ .replace( /&lt;\/p&gt;/g, '</p>' )
 1147+ // And <span class="wikiEditor-tab"></span> too
 1148+ .replace(
 1149+ /&lt;span( |&nbsp;)class=("|&quot;)wikiEditor-tab("|&quot;)&gt;&lt;\/span&gt;/g,
 1150+ '<span class="wikiEditor-tab"></span>'
 1151+ )
 1152+ // Empty <p> tags need <br> tags in them
 1153+ .replace( /<p><\/p>/g, '<p><br></p>' )
 1154+ // Unescape &esc; stuff
 1155+ .replace( /&amp;esc;&amp;amp;nbsp;/g, '&amp;nbsp;' )
 1156+ .replace( /&amp;esc;&amp;lt;p&amp;gt;/g, '&lt;p&gt;' )
 1157+ .replace( /&amp;esc;&amp;lt;\/p&amp;gt;/g, '&lt;/p&gt;' )
 1158+ .replace(
 1159+ /&amp;esc;&amp;lt;span&amp;nbsp;class=&amp;quot;wikiEditor-tab&amp;quot;&amp;gt;&amp;lt;\/span&amp;gt;/g,
 1160+ '&lt;span class="wikiEditor-tab"&gt;&lt;\/span&gt;'
 1161+ )
 1162+ .replace( /&amp;esc;esc;/g, '&amp;esc;' );
 1163+ context.$content.html( html );
 1164+
 1165+ // Reflect direction of parent frame into child
 1166+ if ( $( 'body' ).is( '.rtl' ) ) {
 1167+ context.$content.addClass( 'rtl' ).attr( 'dir', 'rtl' );
 1168+ }
 1169+ // Activate the iframe, encoding the content of the textarea and copying it to the content of iframe
 1170+ context.$textarea.attr( 'disabled', true );
 1171+ context.$textarea.hide();
 1172+ context.$iframe.show();
 1173+ // Let modules know we're ready to start working with the content
 1174+ context.fn.trigger( 'ready' );
 1175+ // Only save HTML now: ready handlers may have modified it
 1176+ context.oldHTML = context.oldDelayedHTML = context.$content.html();
 1177+ //remove our temporary loading
 1178+ /* Disaling our loading div for now
 1179+ $( '.wikiEditor-ui-loading' ).fadeOut( 'fast', function() {
 1180+ $( this ).remove();
 1181+ } );
 1182+ */
 1183+ // Setup event handling on the iframe
 1184+ $( context.$iframe[0].contentWindow.document )
 1185+ .bind( 'keydown', function( event ) {
 1186+ event.jQueryNode = context.fn.getElementAtCursor();
 1187+ return context.fn.trigger( 'keydown', event );
 1188+
 1189+ } )
 1190+ .bind( 'keyup', function( event ) {
 1191+ event.jQueryNode = context.fn.getElementAtCursor();
 1192+ return context.fn.trigger( 'keyup', event );
 1193+ } )
 1194+ .bind( 'keypress', function( event ) {
 1195+ event.jQueryNode = context.fn.getElementAtCursor();
 1196+ return context.fn.trigger( 'keypress', event );
 1197+ } )
 1198+ .bind( 'paste', function( event ) {
 1199+ return context.fn.trigger( 'paste', event );
 1200+ } )
 1201+ .bind( 'cut', function( event ) {
 1202+ return context.fn.trigger( 'cut', event );
 1203+ } )
 1204+ .bind( 'keyup paste mouseup cut encapsulateSelection', function( event ) {
 1205+ return context.fn.trigger( 'change', event );
 1206+ } )
 1207+ .delayedBind( 250, 'keyup paste mouseup cut encapsulateSelection', function( event ) {
 1208+ context.fn.trigger( 'delayedChange', event );
 1209+ } );
 1210+ } );
 1211+ // Attach a submit handler to the form so that when the form is submitted the content of the iframe gets
 1212+ // decoded and copied over to the textarea
 1213+ context.$textarea.closest( 'form' ).submit( function() {
 1214+ context.$textarea.attr( 'disabled', false );
 1215+ context.$textarea.val( context.$textarea.textSelection( 'getContents' ) );
 1216+ } );
 1217+ /* FIXME: This was taken from EditWarning.js - maybe we could do a jquery plugin for this? */
 1218+ // Attach our own handler for onbeforeunload which respects the current one
 1219+ context.fallbackWindowOnBeforeUnload = window.onbeforeunload;
 1220+ window.onbeforeunload = function() {
 1221+ context.$textarea.val( context.$textarea.textSelection( 'getContents' ) );
 1222+ if ( context.fallbackWindowOnBeforeUnload ) {
 1223+ return context.fallbackWindowOnBeforeUnload();
 1224+ }
 1225+ };
 1226+ },
 1227+
 1228+ /*
 1229+ * Compatibility with the $.textSelection jQuery plug-in. When the iframe is in use, these functions provide
 1230+ * equivilant functionality to the otherwise textarea-based functionality.
 1231+ */
 1232+
 1233+ 'getElementAtCursor': function() {
 1234+ if ( context.$iframe[0].contentWindow.getSelection ) {
 1235+ // Firefox and Opera
 1236+ var selection = context.$iframe[0].contentWindow.getSelection();
 1237+ if ( selection.rangeCount == 0 ) {
 1238+ // We don't know where the cursor is
 1239+ return $( [] );
 1240+ }
 1241+ var sc = selection.getRangeAt( 0 ).startContainer;
 1242+ if ( sc.nodeName == "#text" ) sc = sc.parentNode;
 1243+ return $( sc );
 1244+ } else if ( context.$iframe[0].contentWindow.document.selection ) { // should come last; Opera!
 1245+ // IE
 1246+ var selection = context.$iframe[0].contentWindow.document.selection.createRange();
 1247+ return $( selection.parentElement() );
 1248+ }
 1249+ },
 1250+
 1251+ /**
 1252+ * Gets the complete contents of the iframe (in plain text, not HTML)
 1253+ */
 1254+ 'getContents': function() {
 1255+ // For <p></p>, .html() returns <p>&nbsp;</p> in IE
 1256+ // This seems to convince IE while not affecting display
 1257+ if ( !context.$content ) {
 1258+ return '';
 1259+ }
 1260+ var html;
 1261+ if ( $.browser.msie ) {
 1262+ // Don't manipulate the iframe DOM itself, causes cursor jumping issues
 1263+ var $c = $( context.$content.get( 0 ).cloneNode( true ) );
 1264+ $c.find( 'p' ).each( function() {
 1265+ if ( $(this).html() == '' ) {
 1266+ $(this).replaceWith( '<p></p>' );
 1267+ }
 1268+ } );
 1269+ html = $c.html();
 1270+ } else {
 1271+ html = context.$content.html();
 1272+ }
 1273+ return context.fn.htmlToText( html );
 1274+ },
 1275+ /**
 1276+ * Gets the currently selected text in the content
 1277+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
 1278+ */
 1279+ 'getSelection': function() {
 1280+ var retval;
 1281+ if ( context.$iframe[0].contentWindow.getSelection ) {
 1282+ // Firefox and Opera
 1283+ retval = context.$iframe[0].contentWindow.getSelection();
 1284+ if ( $.browser.opera ) {
 1285+ // Opera strips newlines in getSelection(), so we need something more sophisticated
 1286+ if ( retval.rangeCount > 0 ) {
 1287+ retval = context.fn.htmlToText( $( '<pre />' )
 1288+ .append( retval.getRangeAt( 0 ).cloneContents() )
 1289+ .html()
 1290+ );
 1291+ } else {
 1292+ retval = '';
 1293+ }
 1294+ }
 1295+ } else if ( context.$iframe[0].contentWindow.document.selection ) { // should come last; Opera!
 1296+ // IE
 1297+ retval = context.$iframe[0].contentWindow.document.selection.createRange();
 1298+ }
 1299+ if ( typeof retval.text != 'undefined' ) {
 1300+ // In IE8, retval.text is stripped of newlines, so we need to process retval.htmlText
 1301+ // to get a reliable answer. IE7 does get this right though
 1302+ // Run this fix for all IE versions anyway, it doesn't hurt
 1303+ retval = context.fn.htmlToText( retval.htmlText );
 1304+ } else if ( typeof retval.toString != 'undefined' ) {
 1305+ retval = retval.toString();
 1306+ }
 1307+ return retval;
 1308+ },
 1309+ /**
 1310+ * Inserts text at the begining and end of a text selection, optionally inserting text at the caret when
 1311+ * selection is empty.
 1312+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
 1313+ */
 1314+ 'encapsulateSelection': function( options ) {
 1315+ var selText = $(this).textSelection( 'getSelection' );
 1316+ var selTextArr;
 1317+ var collapseToEnd = false;
 1318+ var selectAfter = false;
 1319+ var setSelectionTo = null;
 1320+ var pre = options.pre, post = options.post;
 1321+ if ( !selText ) {
 1322+ selText = options.peri;
 1323+ selectAfter = true;
 1324+ } else if ( options.peri == selText.replace( /\s+$/, '' ) ) {
 1325+ // Probably a successive button press
 1326+ // strip any extra white space from selText
 1327+ selText = selText.replace( /\s+$/, '' );
 1328+ // set the collapseToEnd flag to ensure our selection is collapsed to the end before any insertion is done
 1329+ collapseToEnd = true;
 1330+ // set selectAfter to true since we know we'll be populating with our default text
 1331+ selectAfter = true;
 1332+ } else if ( options.replace ) {
 1333+ selText = options.peri;
 1334+ } else if ( selText.charAt( selText.length - 1 ) == ' ' ) {
 1335+ // Exclude ending space char
 1336+ // FIXME: Why?
 1337+ selText = selText.substring( 0, selText.length - 1 );
 1338+ post += ' ';
 1339+ }
 1340+ if ( options.splitlines ) {
 1341+ selTextArr = selText.split( /\n/ );
 1342+ }
 1343+
 1344+ if ( context.$iframe[0].contentWindow.getSelection ) {
 1345+ // Firefox and Opera
 1346+ var range = context.$iframe[0].contentWindow.getSelection().getRangeAt( 0 );
 1347+ // if our test above indicated that this was a sucessive button press, we need to collapse the
 1348+ // selection to the end to avoid replacing text
 1349+ if ( collapseToEnd ) {
 1350+ // Make sure we're not collapsing ourselves into a BR tag
 1351+ if ( range.endContainer.nodeName == 'BR' ) {
 1352+ range.setEndBefore( range.endContainer );
 1353+ }
 1354+ range.collapse( false );
 1355+ }
 1356+ if ( options.ownline ) {
 1357+ // We need to figure out if the cursor is at the start or end of a line
 1358+ var atStart = false, atEnd = false;
 1359+ var body = context.$content.get( 0 );
 1360+ if ( range.startOffset == 0 ) {
 1361+ // Start of a line
 1362+ // FIXME: Not necessarily the case with syntax highlighting or
 1363+ // template collapsing
 1364+ atStart = true;
 1365+ } else if ( range.startContainer == body ) {
 1366+ // Look up the node just before the start of the selection
 1367+ // If it's a <BR>, we're at the start of a line that starts with a
 1368+ // block element; if not, we're at the end of a line
 1369+ var n = body.firstChild;
 1370+ for ( var i = 0; i < range.startOffset - 1 && n; i++ ) {
 1371+ n = n.nextSibling;
 1372+ }
 1373+ if ( n && n.nodeName == 'BR' ) {
 1374+ atStart = true;
 1375+ } else {
 1376+ atEnd = true;
 1377+ }
 1378+ }
 1379+ if ( ( range.endOffset == 0 && range.endContainer.nodeValue == null ) ||
 1380+ ( range.endContainer.nodeName == '#text' &&
 1381+ range.endOffset == range.endContainer.nodeValue.length ) ||
 1382+ ( range.endContainer.nodeName == 'P' && range.endContainer.nodeValue == null ) ) {
 1383+ atEnd = true;
 1384+ }
 1385+ if ( !atStart ) {
 1386+ pre = "\n" + options.pre;
 1387+ }
 1388+ if ( !atEnd ) {
 1389+ post += "\n";
 1390+ }
 1391+ }
 1392+ var insertText = "";
 1393+ if ( options.splitlines ) {
 1394+ for( var j = 0; j < selTextArr.length; j++ ) {
 1395+ insertText = insertText + pre + selTextArr[j] + post;
 1396+ if( j != selTextArr.length - 1 ) {
 1397+ insertText += "\n";
 1398+ }
 1399+ }
 1400+ } else {
 1401+ insertText = pre + selText + post;
 1402+ }
 1403+ var insertLines = insertText.split( "\n" );
 1404+ range.extractContents();
 1405+ // Insert the contents one line at a time - insertNode() inserts at the beginning, so this has to happen
 1406+ // in reverse order
 1407+ // Track the first and last inserted node, and if we need to also track where the text we need to select
 1408+ // afterwards starts and ends
 1409+ var firstNode = null, lastNode = null;
 1410+ var selSC = null, selEC = null, selSO = null, selEO = null, offset = 0;
 1411+ for ( var i = insertLines.length - 1; i >= 0; i-- ) {
 1412+ firstNode = context.$iframe[0].contentWindow.document.createTextNode( insertLines[i] );
 1413+ range.insertNode( firstNode );
 1414+ lastNode = lastNode || firstNode;
 1415+ var newOffset = offset + insertLines[i].length;
 1416+ if ( !selEC && post.length <= newOffset ) {
 1417+ selEC = firstNode;
 1418+ selEO = selEC.nodeValue.length - ( post.length - offset );
 1419+ }
 1420+ if ( selEC && !selSC && pre.length >= insertText.length - newOffset ) {
 1421+ selSC = firstNode;
 1422+ selSO = pre.length - ( insertText.length - newOffset );
 1423+ }
 1424+ offset = newOffset;
 1425+ if ( i > 0 ) {
 1426+ firstNode = context.$iframe[0].contentWindow.document.createElement( 'br' );
 1427+ range.insertNode( firstNode );
 1428+ newOffset = offset + 1;
 1429+ if ( !selEC && post.length <= newOffset ) {
 1430+ selEC = firstNode;
 1431+ selEO = 1 - ( post.length - offset );
 1432+ }
 1433+ if ( selEC && !selSC && pre.length >= insertText.length - newOffset ) {
 1434+ selSC = firstNode;
 1435+ selSO = pre.length - ( insertText.length - newOffset );
 1436+ }
 1437+ offset = newOffset;
 1438+ }
 1439+ }
 1440+ if ( firstNode ) {
 1441+ context.fn.scrollToTop( $( firstNode.parentNode ) );
 1442+ }
 1443+ if ( selectAfter ) {
 1444+ setSelectionTo = {
 1445+ startContainer: selSC,
 1446+ endContainer: selEC,
 1447+ start: selSO,
 1448+ end: selEO
 1449+ };
 1450+ } else if ( lastNode ) {
 1451+ setSelectionTo = {
 1452+ startContainer: lastNode,
 1453+ endContainer: lastNode,
 1454+ start: lastNode.nodeValue.length,
 1455+ end: lastNode.nodeValue.length
 1456+ };
 1457+ }
 1458+ } else if ( context.$iframe[0].contentWindow.document.selection ) {
 1459+ // IE
 1460+ context.$iframe[0].contentWindow.focus();
 1461+ var range = context.$iframe[0].contentWindow.document.selection.createRange();
 1462+ if ( options.ownline && range.moveStart ) {
 1463+ // Check if we're at the start of a line
 1464+ // If not, prepend a newline
 1465+ var range2 = context.$iframe[0].contentWindow.document.selection.createRange();
 1466+ range2.collapse();
 1467+ range2.moveStart( 'character', -1 );
 1468+ // FIXME: Which check is correct?
 1469+ if ( range2.text != "\r" && range2.text != "\n" && range2.text != "" ) {
 1470+ pre = "\n" + pre;
 1471+ }
 1472+
 1473+ // Check if we're at the end of a line
 1474+ // If not, append a newline
 1475+ var range3 = context.$iframe[0].contentWindow.document.selection.createRange();
 1476+ range3.collapse( false );
 1477+ range3.moveEnd( 'character', 1 );
 1478+ if ( range3.text != "\r" && range3.text != "\n" && range3.text != "" ) {
 1479+ post += "\n";
 1480+ }
 1481+ }
 1482+ // if our test above indicated that this was a sucessive button press, we need to collapse the
 1483+ // selection to the end to avoid replacing text
 1484+ if ( collapseToEnd ) {
 1485+ range.collapse( false );
 1486+ }
 1487+ // TODO: Clean this up. Duplicate code due to the pre-existing browser specific structure of this
 1488+ // function
 1489+ var insertText = "";
 1490+ if ( options.splitlines ) {
 1491+ for( var j = 0; j < selTextArr.length; j++ ) {
 1492+ insertText = insertText + pre + selTextArr[j] + post;
 1493+ if( j != selTextArr.length - 1 ) {
 1494+ insertText += "\n";
 1495+ }
 1496+ }
 1497+ } else {
 1498+ insertText = pre + selText + post;
 1499+ }
 1500+ // TODO: Maybe find a more elegant way of doing this like the Firefox code above?
 1501+ range.pasteHTML( insertText
 1502+ .replace( /\</g, '&lt;' )
 1503+ .replace( />/g, '&gt;' )
 1504+ .replace( /\r?\n/g, '<br />' )
 1505+ );
 1506+ if ( selectAfter ) {
 1507+ range.moveStart( 'character', -post.length - selText.length );
 1508+ range.moveEnd( 'character', -post.length );
 1509+ range.select();
 1510+ }
 1511+ }
 1512+
 1513+ if ( setSelectionTo ) {
 1514+ context.fn.setSelection( setSelectionTo );
 1515+ }
 1516+ // Trigger the encapsulateSelection event (this might need to get named something else/done differently)
 1517+ $( context.$iframe[0].contentWindow.document ).trigger(
 1518+ 'encapsulateSelection', [ pre, options.peri, post, options.ownline, options.replace ]
 1519+ );
 1520+ return context.$textarea;
 1521+ },
 1522+ /**
 1523+ * Gets the position (in resolution of bytes not nessecarily characters) in a textarea
 1524+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
 1525+ */
 1526+ 'getCaretPosition': function( options ) {
 1527+ var startPos = null, endPos = null;
 1528+ if ( context.$iframe[0].contentWindow.getSelection ) {
 1529+ var selection = context.$iframe[0].contentWindow.getSelection();
 1530+ if ( selection.rangeCount == 0 ) {
 1531+ // We don't know where the cursor is
 1532+ return [ 0, 0 ];
 1533+ }
 1534+ var sc = selection.getRangeAt( 0 ).startContainer, ec = selection.getRangeAt( 0 ).endContainer;
 1535+ var so = selection.getRangeAt( 0 ).startOffset, eo = selection.getRangeAt( 0 ).endOffset;
 1536+ if ( sc.nodeName == 'BODY' ) {
 1537+ // Grab the node just before the start of the selection
 1538+ var n = sc.firstChild;
 1539+ for ( var i = 0; i < so - 1 && n; i++ ) {
 1540+ n = n.nextSibling;
 1541+ }
 1542+ sc = n;
 1543+ so = 0;
 1544+ }
 1545+ if ( ec.nodeName == 'BODY' ) {
 1546+ var n = ec.firstChild;
 1547+ for ( var i = 0; i < eo - 1 && n; i++ ) {
 1548+ n = n.nextSibling;
 1549+ }
 1550+ ec = n;
 1551+ eo = 0;
 1552+ }
 1553+
 1554+ // Make sure sc and ec are leaf nodes
 1555+ while ( sc.firstChild ) {
 1556+ sc = sc.firstChild;
 1557+ }
 1558+ while ( ec.firstChild ) {
 1559+ ec = ec.firstChild;
 1560+ }
 1561+ // Make sure the offsets are regenerated if necessary
 1562+ context.fn.getOffset( 0 );
 1563+ var o;
 1564+ for ( o in context.offsets ) {
 1565+ if ( startPos === null && context.offsets[o].node == sc ) {
 1566+ // For some wicked reason o is a string, even though
 1567+ // we put it in as an integer. Use ~~ to coerce it too an int
 1568+ startPos = ~~o + so - context.offsets[o].offset;
 1569+ }
 1570+ if ( startPos !== null && context.offsets[o].node == ec ) {
 1571+ endPos = ~~o + eo - context.offsets[o].offset;
 1572+ break;
 1573+ }
 1574+ }
 1575+ } else if ( context.$iframe[0].contentWindow.document.selection ) {
 1576+ // IE
 1577+ // FIXME: This is mostly copypasted from the textSelection plugin
 1578+ var d = context.$iframe[0].contentWindow.document;
 1579+ var postFinished = false;
 1580+ var periFinished = false;
 1581+ var postFinished = false;
 1582+ var preText, rawPreText, periText;
 1583+ var rawPeriText, postText, rawPostText;
 1584+ // Depending on the document state, and if the cursor has ever been manually placed within the document
 1585+ // the following call such as setEndPoint can result in nasty errors. These cases are always cases
 1586+ // in which the start and end points can safely be assumed to be 0, so we will just try our best to do
 1587+ // the full process but fall back to 0.
 1588+ try {
 1589+ // Create range containing text in the selection
 1590+ var periRange = d.selection.createRange().duplicate();
 1591+ // Create range containing text before the selection
 1592+ var preRange = d.body.createTextRange();
 1593+ // Move the end where we need it
 1594+ preRange.setEndPoint( "EndToStart", periRange );
 1595+ // Create range containing text after the selection
 1596+ var postRange = d.body.createTextRange();
 1597+ // Move the start where we need it
 1598+ postRange.setEndPoint( "StartToEnd", periRange );
 1599+ // Load the text values we need to compare
 1600+ preText = rawPreText = preRange.text;
 1601+ periText = rawPeriText = periRange.text;
 1602+ postText = rawPostText = postRange.text;
 1603+ /*
 1604+ * Check each range for trimmed newlines by shrinking the range by 1
 1605+ * character and seeing if the text property has changed. If it has
 1606+ * not changed then we know that IE has trimmed a \r\n from the end.
 1607+ */
 1608+ do {
 1609+ if ( !postFinished ) {
 1610+ if ( preRange.compareEndPoints( "StartToEnd", preRange ) == 0 ) {
 1611+ postFinished = true;
 1612+ } else {
 1613+ preRange.moveEnd( "character", -1 )
 1614+ if ( preRange.text == preText ) {
 1615+ rawPreText += "\r\n";
 1616+ } else {
 1617+ postFinished = true;
 1618+ }
 1619+ }
 1620+ }
 1621+ if ( !periFinished ) {
 1622+ if ( periRange.compareEndPoints( "StartToEnd", periRange ) == 0 ) {
 1623+ periFinished = true;
 1624+ } else {
 1625+ periRange.moveEnd( "character", -1 )
 1626+ if ( periRange.text == periText ) {
 1627+ rawPeriText += "\r\n";
 1628+ } else {
 1629+ periFinished = true;
 1630+ }
 1631+ }
 1632+ }
 1633+ if ( !postFinished ) {
 1634+ if ( postRange.compareEndPoints("StartToEnd", postRange) == 0 ) {
 1635+ postFinished = true;
 1636+ } else {
 1637+ postRange.moveEnd( "character", -1 )
 1638+ if ( postRange.text == postText ) {
 1639+ rawPostText += "\r\n";
 1640+ } else {
 1641+ postFinished = true;
 1642+ }
 1643+ }
 1644+ }
 1645+ } while ( ( !postFinished || !periFinished || !postFinished ) );
 1646+ startPos = rawPreText.replace( /\r\n/g, "\n" ).length;
 1647+ endPos = startPos + rawPeriText.replace( /\r\n/g, "\n" ).length;
 1648+ } catch( e ) {
 1649+ startPos = endPos = 0;
 1650+ }
 1651+ }
 1652+ return [ startPos, endPos ];
 1653+ },
 1654+ /**
 1655+ * Sets the selection of the content
 1656+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
 1657+ *
 1658+ * @param start Character offset of selection start
 1659+ * @param end Character offset of selection end
 1660+ * @param startContainer Element in iframe to start selection in. If not set, start is a character offset
 1661+ * @param endContainer Element in iframe to end selection in. If not set, end is a character offset
 1662+ */
 1663+ 'setSelection': function( options ) {
 1664+ var sc = options.startContainer, ec = options.endContainer;
 1665+ sc = sc && sc.jquery ? sc[0] : sc;
 1666+ ec = ec && ec.jquery ? ec[0] : ec;
 1667+ if ( context.$iframe[0].contentWindow.getSelection ) {
 1668+ // Firefox and Opera
 1669+ var start = options.start, end = options.end;
 1670+ if ( !sc || !ec ) {
 1671+ var s = context.fn.getOffset( start );
 1672+ var e = context.fn.getOffset( end );
 1673+ sc = s ? s.node : null;
 1674+ ec = e ? e.node : null;
 1675+ start = s ? s.offset : null;
 1676+ end = e ? e.offset : null;
 1677+ // Don't try to set the selection past the end of a node, causes errors
 1678+ // Just put the selection at the end of the node in this case
 1679+ if ( sc != null && sc.nodeName == '#text' && start > sc.nodeValue.length ) {
 1680+ start = sc.nodeValue.length - 1;
 1681+ }
 1682+ if ( ec != null && ec.nodeName == '#text' && end > ec.nodeValue.length ) {
 1683+ end = ec.nodeValue.length - 1;
 1684+ }
 1685+ }
 1686+ if ( !sc || !ec ) {
 1687+ // The requested offset isn't in the offsets array
 1688+ // Give up
 1689+ return context.$textarea;
 1690+ }
 1691+
 1692+ var sel = context.$iframe[0].contentWindow.getSelection();
 1693+ while ( sc.firstChild && sc.nodeName != '#text' ) {
 1694+ sc = sc.firstChild;
 1695+ }
 1696+ while ( ec.firstChild && ec.nodeName != '#text' ) {
 1697+ ec = ec.firstChild;
 1698+ }
 1699+ var range = context.$iframe[0].contentWindow.document.createRange();
 1700+ range.setStart( sc, start );
 1701+ range.setEnd( ec, end );
 1702+ sel.removeAllRanges();
 1703+ sel.addRange( range );
 1704+ context.$iframe[0].contentWindow.focus();
 1705+ } else if ( context.$iframe[0].contentWindow.document.body.createTextRange ) {
 1706+ // IE
 1707+ var range = context.$iframe[0].contentWindow.document.body.createTextRange();
 1708+ if ( sc ) {
 1709+ range.moveToElementText( sc );
 1710+ }
 1711+ range.collapse();
 1712+ range.moveEnd( 'character', options.start );
 1713+
 1714+ var range2 = context.$iframe[0].contentWindow.document.body.createTextRange();
 1715+ if ( ec ) {
 1716+ range2.moveToElementText( ec );
 1717+ }
 1718+ range2.collapse();
 1719+ range2.moveEnd( 'character', options.end );
 1720+
 1721+ // IE does newline emulation for <p>s: <p>foo</p><p>bar</p> becomes foo\nbar just fine
 1722+ // but <p>foo</p><br><br><p>bar</p> becomes foo\n\n\n\nbar , one \n too many
 1723+ // Correct for this
 1724+ var matches, counted = 0;
 1725+ // while ( matches = range.htmlText.match( regex ) && matches.length <= counted ) doesn't work
 1726+ // because the assignment side effect hasn't happened yet when the second term is evaluated
 1727+ while ( matches = range.htmlText.match( /\<\/p\>(\<br[^\>]*\>)+\<p\>/gi ) ) {
 1728+ if ( matches.length <= counted )
 1729+ break;
 1730+ range.moveEnd( 'character', matches.length );
 1731+ counted += matches.length;
 1732+ }
 1733+ range2.moveEnd( 'character', counted );
 1734+ while ( matches = range2.htmlText.match( /\<\/p\>(\<br[^\>]*\>)+\<p\>/gi ) ) {
 1735+ if ( matches.length <= counted )
 1736+ break;
 1737+ range2.moveEnd( 'character', matches.length );
 1738+ counted += matches.length;
 1739+ }
 1740+
 1741+ range2.setEndPoint( 'StartToEnd', range );
 1742+ range2.select();
 1743+ }
 1744+ return context.$textarea;
 1745+ },
 1746+ /**
 1747+ * Scroll a textarea to the current cursor position. You can set the cursor position with setSelection()
 1748+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
 1749+ */
 1750+ 'scrollToCaretPosition': function( options ) {
 1751+ context.fn.scrollToTop( context.fn.getElementAtCursor(), true );
 1752+ },
 1753+ /**
 1754+ * Scroll an element to the top of the iframe
 1755+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
 1756+ *
 1757+ * @param $element jQuery object containing an element in the iframe
 1758+ * @param force If true, scroll the element even if it's already visible
 1759+ */
 1760+ 'scrollToTop': function( $element, force ) {
 1761+ var html = context.$content.closest( 'html' ),
 1762+ body = context.$content.closest( 'body' ),
 1763+ parentHtml = $( 'html' ),
 1764+ parentBody = $( 'body' );
 1765+ var y = $element.offset().top;
 1766+ if ( !$.browser.msie && ! $element.is( 'body' ) ) {
 1767+ y = parentHtml.scrollTop() > 0 ? y + html.scrollTop() - parentHtml.scrollTop() : y;
 1768+ y = parentBody.scrollTop() > 0 ? y + body.scrollTop() - parentBody.scrollTop() : y;
 1769+ }
 1770+ var topBound = html.scrollTop() > body.scrollTop() ? html.scrollTop() : body.scrollTop(),
 1771+ bottomBound = topBound + context.$iframe.height();
 1772+ if ( force || y < topBound || y > bottomBound ) {
 1773+ html.scrollTop( y );
 1774+ body.scrollTop( y );
 1775+ }
 1776+ $element.trigger( 'scrollToTop' );
 1777+ },
 1778+ /**
 1779+ * Save scrollTop and cursor position for IE.
 1780+ */
 1781+ 'saveStuffForIE': function() {
 1782+ // Only need this for IE in textarea mode
 1783+ if ( !$.browser.msie || context.$iframe )
 1784+ return;
 1785+ var IHateIE = {
 1786+ 'scrollTop' : context.$textarea.scrollTop(),
 1787+ 'pos': context.$textarea.textSelection( 'getCaretPosition', { startAndEnd: true } )
 1788+ };
 1789+ context.$textarea.data( 'IHateIE', IHateIE );
 1790+ },
 1791+ /**
 1792+ * Restore scrollTo and cursor position for IE.
 1793+ */
 1794+ 'restoreStuffForIE': function() {
 1795+ // Only need this for IE in textarea mode
 1796+ if ( !$.browser.msie || context.$iframe )
 1797+ return;
 1798+ var IHateIE = context.$textarea.data( 'IHateIE' );
 1799+ if ( !IHateIE )
 1800+ return;
 1801+ context.$textarea.scrollTop( IHateIE.scrollTop );
 1802+ context.$textarea.textSelection( 'setSelection', { start: IHateIE.pos[0], end: IHateIE.pos[1] } );
 1803+ context.$textarea.data( 'IHateIE', null );
 1804+ }
 1805+ };
 1806+
 1807+ /*
 1808+ * Base UI Construction
 1809+ *
 1810+ * The UI is built from several containers, the outer-most being a div classed as "wikiEditor-ui". These containers
 1811+ * provide a certain amount of "free" layout, but in some situations procedural layout is needed, which is performed
 1812+ * as a response to the "resize" event.
 1813+ */
 1814+
 1815+ // Assemble a temporary div to place over the wikiEditor while it's being constructed
 1816+ /* Disabling our loading div for now
 1817+ var $loader = $( '<div></div>' )
 1818+ .addClass( 'wikiEditor-ui-loading' )
 1819+ .append( $( '<span>' + mw.usability.getMsg( 'wikieditor-loading' ) + '</span>' )
 1820+ .css( 'marginTop', context.$textarea.height() / 2 ) );
 1821+ */
 1822+ // Encapsulate the textarea with some containers for layout
 1823+ context.$textarea
 1824+ /* Disabling our loading div for now
 1825+ .after( $loader )
 1826+ .add( $loader )
 1827+ */
 1828+ .wrapAll( $( '<div></div>' ).addClass( 'wikiEditor-ui' ) )
 1829+ .wrapAll( $( '<div></div>' ).addClass( 'wikiEditor-ui-view wikiEditor-ui-view-wikitext' ) )
 1830+ .wrapAll( $( '<div></div>' ).addClass( 'wikiEditor-ui-left' ) )
 1831+ .wrapAll( $( '<div></div>' ).addClass( 'wikiEditor-ui-bottom' ) )
 1832+ .wrapAll( $( '<div></div>' ).addClass( 'wikiEditor-ui-text' ) );
 1833+ // Get references to some of the newly created containers
 1834+ context.$ui = context.$textarea.parent().parent().parent().parent().parent();
 1835+ context.$wikitext = context.$textarea.parent().parent().parent().parent();
 1836+ // Add in tab and button containers
 1837+ context.$wikitext
 1838+ .before(
 1839+ $( '<div></div>' ).addClass( 'wikiEditor-ui-controls' )
 1840+ .append( $( '<div></div>' ).addClass( 'wikiEditor-ui-tabs' ).hide() )
 1841+ .append( $( '<div></div>' ).addClass( 'wikiEditor-ui-buttons' ) )
 1842+ )
 1843+ .before( $( '<div style="clear:both;"></div>' ) );
 1844+ // Get references to some of the newly created containers
 1845+ context.$controls = context.$ui.find( '.wikiEditor-ui-buttons' ).hide();
 1846+ context.$buttons = context.$ui.find( '.wikiEditor-ui-buttons' );
 1847+ context.$tabs = context.$ui.find( '.wikiEditor-ui-tabs' );
 1848+ // Clear all floating after the UI
 1849+ context.$ui.after( $( '<div style="clear:both;"></div>' ) );
 1850+ // Attach a right container
 1851+ context.$wikitext.append( $( '<div></div>' ).addClass( 'wikiEditor-ui-right' ) );
 1852+ // Attach a top container to the left pane
 1853+ context.$wikitext.find( '.wikiEditor-ui-left' ).prepend( $( '<div></div>' ).addClass( 'wikiEditor-ui-top' ) );
 1854+ // Setup the intial view
 1855+ context.view = 'wikitext';
 1856+ // Trigger the "resize" event anytime the window is resized
 1857+ $( window ).resize( function( event ) { context.fn.trigger( 'resize', event ); } );
 1858+}
 1859+
 1860+/* API Execution */
 1861+
 1862+// Since javascript gives arguments as an object, we need to convert them so they can be used more easily
 1863+var args = $.makeArray( arguments );
 1864+
 1865+// Dynamically setup the Iframe when needed when adding modules
 1866+if ( typeof context.$iframe === 'undefined' && args[0] == 'addModule' && typeof args[1] != 'undefined' ) {
 1867+ var modules = args[1];
 1868+ if ( typeof modules != "object" ) {
 1869+ modules = {};
 1870+ modules[args[1]] = '';
 1871+ }
 1872+ for ( module in modules ) {
 1873+ // Only allow modules which are supported (and thus actually being turned on) affect this decision
 1874+ if ( module in $.wikiEditor.modules && $.wikiEditor.isSupported( $.wikiEditor.modules[module] ) &&
 1875+ $.wikiEditor.isRequired( $.wikiEditor.modules[module], 'iframe' ) ) {
 1876+
 1877+ context.fn.setupIframe();
 1878+ break;
 1879+ }
 1880+ }
 1881+}
 1882+
 1883+// There would need to be some arguments if the API is being called
 1884+if ( args.length > 0 ) {
 1885+ // Handle API calls
 1886+ var call = args.shift();
 1887+ if ( call in context.api ) {
 1888+ context.api[call]( context, typeof args[0] == 'undefined' ? {} : args[0] );
 1889+ }
 1890+}
 1891+
 1892+// Store the context for next time, and support chaining
 1893+return $(this).data( 'wikiEditor-context', context );
 1894+
 1895+}; } )( jQuery );
Property changes on: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.js
___________________________________________________________________
Added: svn:eol-style
11896 + native
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.js
@@ -0,0 +1,765 @@
 2+/**
 3+ * Toolbar module for wikiEditor
 4+ */
 5+( function( $ ) { $.wikiEditor.modules.toolbar = {
 6+
 7+/**
 8+ * API accessible functions
 9+ */
 10+api : {
 11+ addToToolbar : function( context, data ) {
 12+ for ( type in data ) {
 13+ switch ( type ) {
 14+ case 'sections':
 15+ var $sections = context.modules.toolbar.$toolbar.find( 'div.sections' );
 16+ var $tabs = context.modules.toolbar.$toolbar.find( 'div.tabs' );
 17+ for ( section in data[type] ) {
 18+ if ( section == 'main' ) {
 19+ // Section
 20+ context.modules.toolbar.$toolbar.prepend(
 21+ $.wikiEditor.modules.toolbar.fn.buildSection(
 22+ context, section, data[type][section]
 23+ )
 24+ );
 25+ continue;
 26+ }
 27+ // Section
 28+ $sections.append(
 29+ $.wikiEditor.modules.toolbar.fn.buildSection( context, section, data[type][section] )
 30+ );
 31+ // Tab
 32+ $tabs.append(
 33+ $.wikiEditor.modules.toolbar.fn.buildTab( context, section, data[type][section] )
 34+ );
 35+ // Update visibility of section
 36+ $section = $sections.find( '.section:visible' );
 37+ if ( $section.size() ) {
 38+ $sections.animate( { 'height': $section.outerHeight() }, 'fast' );
 39+ }
 40+ }
 41+ break;
 42+ case 'groups':
 43+ if ( ! ( 'section' in data ) ) {
 44+ continue;
 45+ }
 46+ var $section = context.modules.toolbar.$toolbar.find( 'div[rel=' + data.section + '].section' );
 47+ for ( group in data[type] ) {
 48+ // Group
 49+ $section.append(
 50+ $.wikiEditor.modules.toolbar.fn.buildGroup( context, group, data[type][group] )
 51+ );
 52+ }
 53+ break;
 54+ case 'tools':
 55+ if ( ! ( 'section' in data && 'group' in data ) ) {
 56+ continue;
 57+ }
 58+ var $group = context.modules.toolbar.$toolbar.find(
 59+ 'div[rel=' + data.section + '].section ' + 'div[rel=' + data.group + '].group'
 60+ );
 61+ for ( tool in data[type] ) {
 62+ // Tool
 63+ $group.append( $.wikiEditor.modules.toolbar.fn.buildTool( context, tool,data[type][tool] ) );
 64+ }
 65+ if ( $group.children().length ) {
 66+ $group.show();
 67+ }
 68+ break;
 69+ case 'pages':
 70+ if ( ! ( 'section' in data ) ) {
 71+ continue;
 72+ }
 73+ var $pages = context.modules.toolbar.$toolbar.find(
 74+ 'div[rel=' + data.section + '].section .pages'
 75+ );
 76+ var $index = context.modules.toolbar.$toolbar.find(
 77+ 'div[rel=' + data.section + '].section .index'
 78+ );
 79+ for ( page in data[type] ) {
 80+ // Page
 81+ $pages.append( $.wikiEditor.modules.toolbar.fn.buildPage( context, page, data[type][page] ) );
 82+ // Index
 83+ $index.append(
 84+ $.wikiEditor.modules.toolbar.fn.buildBookmark( context, page, data[type][page] )
 85+ );
 86+ }
 87+ $.wikiEditor.modules.toolbar.fn.updateBookletSelection( context, page, $pages, $index );
 88+ break;
 89+ case 'rows':
 90+ if ( ! ( 'section' in data && 'page' in data ) ) {
 91+ continue;
 92+ }
 93+ var $table = context.modules.toolbar.$toolbar.find(
 94+ 'div[rel=' + data.section + '].section ' + 'div[rel=' + data.page + '].page table'
 95+ );
 96+ for ( row in data[type] ) {
 97+ // Row
 98+ $table.append( $.wikiEditor.modules.toolbar.fn.buildRow( context, data[type][row] ) );
 99+ }
 100+ break;
 101+ case 'characters':
 102+ if ( ! ( 'section' in data && 'page' in data ) ) {
 103+ continue;
 104+ }
 105+ $characters = context.modules.toolbar.$toolbar.find(
 106+ 'div[rel=' + data.section + '].section ' + 'div[rel=' + data.page + '].page div'
 107+ );
 108+ var actions = $characters.data( 'actions' );
 109+ for ( character in data[type] ) {
 110+ // Character
 111+ $characters
 112+ .append(
 113+ $( $.wikiEditor.modules.toolbar.fn.buildCharacter( data[type][character], actions ) )
 114+ .mousedown( function( e ) {
 115+ context.fn.saveStuffForIE();
 116+ // No dragging!
 117+ e.preventDefault();
 118+ return false;
 119+ } )
 120+ .click( function( e ) {
 121+ $.wikiEditor.modules.toolbar.fn.doAction( $(this).parent().data( 'context' ),
 122+ $(this).parent().data( 'actions' )[$(this).attr( 'rel' )] );
 123+ e.preventDefault();
 124+ return false;
 125+ } )
 126+ );
 127+ }
 128+ break;
 129+ default: break;
 130+ }
 131+ }
 132+ },
 133+ removeFromToolbar : function( context, data ) {
 134+ if ( typeof data.section == 'string' ) {
 135+ // Section
 136+ var tab = 'div.tabs span[rel=' + data.section + '].tab';
 137+ var target = 'div[rel=' + data.section + '].section';
 138+ var group = null;
 139+ if ( typeof data.group == 'string' ) {
 140+ // Toolbar group
 141+ target += ' div[rel=' + data.group + '].group';
 142+ if ( typeof data.tool == 'string' ) {
 143+ // Save for later checking if empty
 144+ group = target;
 145+ // Tool
 146+ target += ' div[rel=' + data.tool + '].tool';
 147+ }
 148+ } else if ( typeof data.page == 'string' ) {
 149+ // Booklet page
 150+ var index = target + ' div.index div[rel=' + data.page + ']';
 151+ target += ' div.pages div[rel=' + data.page + '].page';
 152+ if ( typeof data.character == 'string' ) {
 153+ // Character
 154+ target += ' a[rel=' + data.character + ']';
 155+ } else if ( typeof data.row == 'number' ) {
 156+ // Table row
 157+ target += ' table tr:not(:has(th)):eq(' + data.row + ')';
 158+ } else {
 159+ // Just a page, remove the index too!
 160+ context.modules.toolbar.$toolbar.find( index ).remove();
 161+ $.wikiEditor.modules.toolbar.fn.updateBookletSelection(
 162+ context,
 163+ null,
 164+ context.modules.toolbar.$toolbar.find( target ),
 165+ context.modules.toolbar.$toolbar.find( index )
 166+ );
 167+ }
 168+ } else {
 169+ // Just a section, remove the tab too!
 170+ context.modules.toolbar.$toolbar.find( tab ).remove();
 171+ }
 172+ context.modules.toolbar.$toolbar.find( target ).remove();
 173+ // Hide empty groups
 174+ if ( group ) {
 175+ $group = context.modules.toolbar.$toolbar.find( group );
 176+ if ( $group.children().length == 0 ) {
 177+ $group.hide();
 178+ }
 179+ }
 180+ }
 181+ }
 182+},
 183+/**
 184+ * Event handlers
 185+ */
 186+evt: {
 187+ resize: function( context, event ) {
 188+ context.$ui.find( '.sections' ).height( context.$ui.find( '.sections .section-visible' ).outerHeight() );
 189+ },
 190+ tocCollapse: function( context, event ) {
 191+ $.wikiEditor.modules.toolbar.evt.resize( context, event );
 192+ },
 193+ tocExpand: function( context, event ) {
 194+ $.wikiEditor.modules.toolbar.evt.resize( context, event );
 195+ }
 196+},
 197+/**
 198+ * Internally used functions
 199+ */
 200+fn: {
 201+ /**
 202+ * Creates a toolbar module within a wikiEditor
 203+ *
 204+ * @param {Object} context Context object of editor to create module in
 205+ * @param {Object} config Configuration object to create module from
 206+ */
 207+ create : function( context, config ) {
 208+ if ( '$toolbar' in context.modules.toolbar ) {
 209+ return;
 210+ }
 211+ context.modules.toolbar.$toolbar = $( '<div />' )
 212+ .addClass( 'wikiEditor-ui-toolbar' )
 213+ .attr( 'id', 'wikiEditor-ui-toolbar' );
 214+ $.wikiEditor.modules.toolbar.fn.build( context, config );
 215+ context.$ui.find( '.wikiEditor-ui-top' ).append( context.modules.toolbar.$toolbar );
 216+ },
 217+ /**
 218+ * Performs an operation based on parameters
 219+ *
 220+ * @param {Object} context
 221+ * @param {Object} action
 222+ * @param {Object} source
 223+ */
 224+ doAction : function( context, action, source ) {
 225+ // Verify that this has been called from a source that's within the toolbar
 226+ // 'trackAction' defined in click tracking
 227+ if ( $.trackAction != undefined && source.closest( '.wikiEditor-ui-toolbar' ).size() ) {
 228+ // Build a unique id for this action by tracking the parent rel attributes up to the toolbar level
 229+ var rels = [];
 230+ var step = source;
 231+ var i = 0;
 232+ while ( !step.hasClass( 'wikiEditor-ui-toolbar' ) ) {
 233+ if ( i > 25 ) {
 234+ break;
 235+ }
 236+ i++;
 237+ var rel = step.attr( 'rel' );
 238+ if ( rel ) {
 239+ rels.push( step.attr( 'rel' ) );
 240+ }
 241+ step = step.parent();
 242+ }
 243+ rels.reverse();
 244+ var id = rels.join( '.' );
 245+ $.trackAction( id );
 246+ }
 247+ switch ( action.type ) {
 248+ case 'replace':
 249+ case 'encapsulate':
 250+ var parts = {
 251+ 'pre' : $.wikiEditor.autoMsg( action.options, 'pre' ),
 252+ 'peri' : $.wikiEditor.autoMsg( action.options, 'peri' ),
 253+ 'post' : $.wikiEditor.autoMsg( action.options, 'post' )
 254+ };
 255+ var replace = action.type == 'replace';
 256+ if ( 'regex' in action.options && 'regexReplace' in action.options ) {
 257+ var selection = context.$textarea.textSelection( 'getSelection' );
 258+ if ( selection != '' && selection.match( action.options.regex ) ) {
 259+ parts.peri = selection.replace( action.options.regex,
 260+ action.options.regexReplace );
 261+ parts.pre = parts.post = '';
 262+ replace = true;
 263+ }
 264+ }
 265+ context.$textarea.textSelection(
 266+ 'encapsulateSelection',
 267+ $.extend( {}, action.options, parts, { 'replace': replace } )
 268+ );
 269+ if ( typeof context.$iframe !== 'undefined' ) {
 270+ context.$iframe[0].contentWindow.focus();
 271+ }
 272+ break;
 273+ case 'callback':
 274+ if ( typeof action.execute == 'function' ) {
 275+ action.execute( context );
 276+ }
 277+ break;
 278+ case 'dialog':
 279+ context.fn.saveSelection();
 280+ context.$textarea.wikiEditor( 'openDialog', action.module );
 281+ break;
 282+ default: break;
 283+ }
 284+ },
 285+ buildGroup : function( context, id, group ) {
 286+ var $group = $( '<div />' ).attr( { 'class' : 'group group-' + id, 'rel' : id } );
 287+ var label = $.wikiEditor.autoMsg( group, 'label' );
 288+ if ( label ) {
 289+ $group.append( '<div class="label">' + label + '</div>' )
 290+ }
 291+ var empty = true;
 292+ if ( 'tools' in group ) {
 293+ for ( tool in group.tools ) {
 294+ var tool = $.wikiEditor.modules.toolbar.fn.buildTool( context, tool, group.tools[tool] );
 295+ if ( tool ) {
 296+ // Consider a group with only hidden tools empty as well
 297+ // .is( ':visible' ) always returns false because tool is not attached to the DOM yet
 298+ empty = empty && tool.css( 'display' ) == 'none';
 299+ $group.append( tool );
 300+ }
 301+ }
 302+ }
 303+ if ( empty ) {
 304+ $group.hide();
 305+ }
 306+ return $group;
 307+ },
 308+ buildTool : function( context, id, tool ) {
 309+ if ( 'filters' in tool ) {
 310+ for ( filter in tool.filters ) {
 311+ if ( $( tool.filters[filter] ).size() == 0 ) {
 312+ return null;
 313+ }
 314+ }
 315+ }
 316+ var label = $.wikiEditor.autoMsg( tool, 'label' );
 317+ switch ( tool.type ) {
 318+ case 'button':
 319+ var src = $.wikiEditor.autoIcon( tool.icon, $.wikiEditor.imgPath + 'toolbar/' );
 320+ var $button = null;
 321+ if ( 'offset' in tool ) {
 322+ var offsetOrIcon = $.wikiEditor.autoIconOrOffset( tool.icon, tool.offset,
 323+ $.wikiEditor.imgPath + 'toolbar/'
 324+ );
 325+ if ( typeof offsetOrIcon == 'object' ) {
 326+ $button = $( '<span />' )
 327+ .attr( {
 328+ 'alt' : label,
 329+ 'title' : label,
 330+ 'rel' : id,
 331+ 'class' : 'wikiEditor-toolbar-spritedButton'
 332+ } )
 333+ .text( label )
 334+ .css( 'backgroundPosition', offsetOrIcon[0] + 'px ' + offsetOrIcon[1] + 'px' );
 335+ }
 336+ }
 337+ if ( !$button ) {
 338+ $button = $( '<img />' )
 339+ .attr( {
 340+ 'src' : src,
 341+ 'width' : 22,
 342+ 'height' : 22,
 343+ 'alt' : label,
 344+ 'title' : label,
 345+ 'rel' : id,
 346+ 'class' : 'tool tool-button'
 347+ } );
 348+ }
 349+ if ( 'action' in tool ) {
 350+ $button
 351+ .data( 'action', tool.action )
 352+ .data( 'context', context )
 353+ .mousedown( function( e ) {
 354+ context.fn.saveStuffForIE();
 355+ // No dragging!
 356+ e.preventDefault();
 357+ return false;
 358+ } )
 359+ .click( function( e ) {
 360+ $.wikiEditor.modules.toolbar.fn.doAction(
 361+ $(this).data( 'context' ), $(this).data( 'action' ), $(this)
 362+ );
 363+ e.preventDefault();
 364+ return false;
 365+ } );
 366+ // If the action is a dialog that hasn't been set up yet, hide the button
 367+ // until the dialog is loaded
 368+ if ( tool.action.type == 'dialog' &&
 369+ !( tool.action.module in $.wikiEditor.modules.dialogs.modules ) ) {
 370+ $button.hide();
 371+ // JavaScript won't propagate the $button variable itself, it needs help
 372+ context.$textarea.bind( 'wikiEditor-dialogs-setup-' + tool.action.module,
 373+ { button: $button }, function( event ) {
 374+ event.data.button.show().parent().show();
 375+ } );
 376+ }
 377+ }
 378+ return $button;
 379+ case 'select':
 380+ var $select = $( '<div />' )
 381+ .attr( { 'rel' : id, 'class' : 'tool tool-select' } );
 382+ var $options = $( '<div />' ).addClass( 'options' );
 383+ if ( 'list' in tool ) {
 384+ for ( option in tool.list ) {
 385+ var optionLabel = $.wikiEditor.autoMsg( tool.list[option], 'label' );
 386+ $options.append(
 387+ $( '<a />' )
 388+ .data( 'action', tool.list[option].action )
 389+ .data( 'context', context )
 390+ .mousedown( function( e ) {
 391+ context.fn.saveStuffForIE();
 392+ // No dragging!
 393+ e.preventDefault();
 394+ return false;
 395+ } )
 396+ .click( function( e ) {
 397+ $.wikiEditor.modules.toolbar.fn.doAction(
 398+ $(this).data( 'context' ), $(this).data( 'action' ), $(this)
 399+ );
 400+ // Hide the dropdown
 401+ // Sanity check: if this somehow gets called while the dropdown
 402+ // is hidden, don't show it
 403+ if ( $(this).parent().is( ':visible' ) ) {
 404+ $(this).parent().animate( { 'opacity': 'toggle' }, 'fast' );
 405+ }
 406+ e.preventDefault();
 407+ return false;
 408+ } )
 409+ .text( optionLabel )
 410+ .addClass( 'option' )
 411+ .attr( { 'rel': option, 'href': '#' } )
 412+ );
 413+ }
 414+ }
 415+ $select.append( $( '<div />' ).addClass( 'menu' ).append( $options ) );
 416+ $select.append( $( '<a />' )
 417+ .addClass( 'label' )
 418+ .text( label )
 419+ .data( 'options', $options )
 420+ .attr( 'href', '#' )
 421+ .mousedown( function( e ) {
 422+ // No dragging!
 423+ e.preventDefault();
 424+ return false;
 425+ } )
 426+ .click( function( e ) {
 427+ $(this).data( 'options' ).animate( { 'opacity': 'toggle' }, 'fast' );
 428+ e.preventDefault();
 429+ return false;
 430+ } )
 431+ );
 432+ return $select;
 433+ default:
 434+ return null;
 435+ }
 436+ },
 437+ buildBookmark : function( context, id, page ) {
 438+ var label = $.wikiEditor.autoMsg( page,
 439+ 'label' );
 440+ return $( '<div />' )
 441+ .text( label )
 442+ .attr( 'rel', id )
 443+ .data( 'context', context )
 444+ .mousedown( function( e ) {
 445+ // No dragging!
 446+ e.preventDefault();
 447+ return false;
 448+ } )
 449+ .click( function( event ) {
 450+ $(this).parent().parent().find( '.page' ).hide();
 451+ $(this).parent().parent().find( '.page-' + $(this).attr( 'rel' ) ).show();
 452+ $(this).siblings().removeClass( 'current' );
 453+ $(this).addClass( 'current' );
 454+ var section = $(this).parent().parent().attr( 'rel' );
 455+ $.cookie(
 456+ 'wikiEditor-' + $(this).data( 'context' ).instance + '-booklet-' + section + '-page',
 457+ $(this).attr( 'rel' ),
 458+ { expires: 30, path: '/' }
 459+ );
 460+ // Click tracking
 461+ if($.trackAction != undefined){
 462+ $.trackAction(section + '.' + $(this).attr('rel'));
 463+ }
 464+ // No dragging!
 465+ event.preventDefault();
 466+ return false;
 467+ } )
 468+ },
 469+ buildPage : function( context, id, page ) {
 470+ var $page = $( '<div />' ).attr( {
 471+ 'class' : 'page page-' + id,
 472+ 'rel' : id
 473+ } );
 474+ switch ( page.layout ) {
 475+ case 'table':
 476+ $page.addClass( 'page-table' );
 477+ var html =
 478+ '<table cellpadding=0 cellspacing=0 ' + 'border=0 width="100%" class="table table-' + id + '">';
 479+ if ( 'headings' in page ) {
 480+ html += $.wikiEditor.modules.toolbar.fn.buildHeading( context, page.headings )
 481+ }
 482+ if ( 'rows' in page ) {
 483+ for ( row in page.rows ) {
 484+ html += $.wikiEditor.modules.toolbar.fn.buildRow( context, page.rows[row] )
 485+ }
 486+ }
 487+ $page.html( html );
 488+ break;
 489+ case 'characters':
 490+ $page.addClass( 'page-characters' );
 491+ $characters = $( '<div />' ).data( 'context', context ).data( 'actions', {} );
 492+ var actions = $characters.data( 'actions' );
 493+ if ( 'language' in page ) {
 494+ $characters.attr( 'lang', page.language );
 495+ }
 496+ if ( 'direction' in page ) {
 497+ $characters.attr( 'dir', page.direction );
 498+ }
 499+ if ( 'characters' in page ) {
 500+ var html = '';
 501+ for ( var i = 0; i < page.characters.length; i++ ) {
 502+ html += $.wikiEditor.modules.toolbar.fn.buildCharacter( page.characters[i], actions );
 503+ }
 504+ $characters
 505+ .html( html )
 506+ .children()
 507+ .mousedown( function( e ) {
 508+ context.fn.saveStuffForIE();
 509+ // No dragging!
 510+ e.preventDefault();
 511+ return false;
 512+ } )
 513+ .click( function( e ) {
 514+ $.wikiEditor.modules.toolbar.fn.doAction(
 515+ $(this).parent().data( 'context' ),
 516+ $(this).parent().data( 'actions' )[$(this).attr( 'rel' )],
 517+ $(this)
 518+ );
 519+ e.preventDefault();
 520+ return false;
 521+ } );
 522+ }
 523+ $page.append( $characters );
 524+ break;
 525+ }
 526+ return $page;
 527+ },
 528+ buildHeading : function( context, headings ) {
 529+ var html = '<tr>';
 530+ for ( heading in headings ) {
 531+ html += '<th>' + $.wikiEditor.autoMsg( headings[heading], ['html', 'text'] ) + '</th>';
 532+ }
 533+ return html;
 534+ },
 535+ buildRow : function( context, row ) {
 536+ var html = '<tr>';
 537+ for ( cell in row ) {
 538+ html += '<td class="cell cell-' + cell + '" valign="top"><span>' +
 539+ $.wikiEditor.autoMsg( row[cell], ['html', 'text'] ) + '</span></td>';
 540+ }
 541+ html += '</tr>';
 542+ return html;
 543+ },
 544+ buildCharacter : function( character, actions ) {
 545+ if ( typeof character == 'string' ) {
 546+ character = {
 547+ 'label' : character,
 548+ 'action' : {
 549+ 'type' : 'replace',
 550+ 'options' : {
 551+ 'peri' : character,
 552+ 'selectPeri': false
 553+ }
 554+ }
 555+ };
 556+ } else if ( 0 in character && 1 in character ) {
 557+ character = {
 558+ 'label' : character[0],
 559+ 'action' : {
 560+ 'type' : 'replace',
 561+ 'options' : {
 562+ 'peri' : character[1],
 563+ 'selectPeri': false
 564+ }
 565+ }
 566+ };
 567+ }
 568+ if ( 'action' in character && 'label' in character ) {
 569+ actions[character.label] = character.action;
 570+ return '<span rel="' + character.label + '">' + character.label + '</span>';
 571+ }
 572+ },
 573+ buildTab : function( context, id, section ) {
 574+ var selected = $.cookie( 'wikiEditor-' + context.instance + '-toolbar-section' );
 575+ // Re-save cookie
 576+ if ( selected != null ) {
 577+ $.cookie( 'wikiEditor-' + context.instance + '-toolbar-section', selected, { expires: 30, path: '/' } );
 578+ }
 579+ return $( '<span />' )
 580+ .attr( { 'class' : 'tab tab-' + id, 'rel' : id } )
 581+ .append(
 582+ $( '<a />' )
 583+ .addClass( selected == id ? 'current' : null )
 584+ .attr( 'href', '#' )
 585+ .text( $.wikiEditor.autoMsg( section, 'label' ) )
 586+ .data( 'context', context )
 587+ .mouseup( function( e ) {
 588+ $(this).blur();
 589+ } )
 590+ .mousedown( function( e ) {
 591+ // No dragging!
 592+ e.preventDefault();
 593+ return false;
 594+ } )
 595+ .click( function( e ) {
 596+ var $sections = $(this).data( 'context' ).$ui.find( '.sections' );
 597+ var $section =
 598+ $(this).data( 'context' ).$ui.find( '.section-' + $(this).parent().attr( 'rel' ) );
 599+ var show = $section.css( 'display' ) == 'none';
 600+ $previousSections = $section.parent().find( '.section-visible' );
 601+ $previousSections.css( 'position', 'absolute' );
 602+ $previousSections.removeClass( 'section-visible' );
 603+ $previousSections.fadeOut( 'fast', function() { $(this).css( 'position', 'relative' ); } );
 604+ $(this).parent().parent().find( 'a' ).removeClass( 'current' );
 605+ $sections.css( 'overflow', 'hidden' );
 606+ function animate( $this ) {
 607+ $sections
 608+ .css( 'display', 'block' )
 609+ .animate( { 'height': $section.outerHeight() }, $section.outerHeight() * 2, function() {
 610+ $( this ).css( 'overflow', 'visible' ).css( 'height', 'auto' );
 611+ context.fn.trigger( 'resize' );
 612+ } );
 613+ }
 614+ if ( show ) {
 615+ $section.addClass( 'section-visible' );
 616+ $section.fadeIn( 'fast' );
 617+ if ( $section.hasClass( 'loading' ) ) {
 618+ // Loading of this section was deferred, load it now
 619+ var $this = $(this);
 620+ $this.addClass( 'current loading' );
 621+ setTimeout( function() {
 622+ $section.trigger( 'loadSection' );
 623+ animate( $(this) );
 624+ $this.removeClass( 'loading' );
 625+ }, 1000 );
 626+ } else {
 627+ animate( $(this) );
 628+ $(this).addClass( 'current' );
 629+ }
 630+ } else {
 631+ $sections
 632+ .css( 'height', $section.outerHeight() )
 633+ .animate( { 'height': 'hide' }, $section.outerHeight() * 2, function() {
 634+ $(this).css( { 'overflow': 'visible', 'height': 0 } );
 635+ context.fn.trigger( 'resize' );
 636+ } );
 637+ }
 638+ // Click tracking
 639+ if ( $.trackAction != undefined ) {
 640+ $.trackAction( $section.attr('rel') + '.' + ( show ? 'show': 'hide' ) );
 641+ }
 642+ // Save the currently visible section
 643+ $.cookie(
 644+ 'wikiEditor-' + $(this).data( 'context' ).instance + '-toolbar-section',
 645+ show ? $section.attr( 'rel' ) : null,
 646+ { expires: 30, path: '/' }
 647+ );
 648+ e.preventDefault();
 649+ return false;
 650+ } )
 651+ );
 652+ },
 653+ buildSection: function( context, id, section ) {
 654+ var $section = $( '<div />' ).attr( { 'class': section.type + ' section section-' + id, 'rel': id } );
 655+ var selected = $.cookie( 'wikiEditor-' + context.instance + '-toolbar-section' );
 656+ var show = selected == id;
 657+
 658+ if ( typeof section.deferLoad != 'undefined' && section.deferLoad && id !== 'main' && !show ) {
 659+ // This class shows the spinner and serves as a marker for the click handler in buildTab()
 660+ $section.addClass( 'loading' ).append( $( '<div />' ).addClass( 'spinner' ) );
 661+ $section.bind( 'loadSection', function() {
 662+ $.wikiEditor.modules.toolbar.fn.reallyBuildSection( context, id, section, $section );
 663+ $section.removeClass( 'loading' );
 664+ } );
 665+ } else {
 666+ $.wikiEditor.modules.toolbar.fn.reallyBuildSection( context, id, section, $section );
 667+ }
 668+
 669+ // Show or hide section
 670+ if ( id !== 'main' ) {
 671+ $section.css( 'display', show ? 'block' : 'none' );
 672+ if ( show )
 673+ $section.addClass( 'section-visible' );
 674+ }
 675+ return $section;
 676+ },
 677+ reallyBuildSection: function( context, id, section, $section ) {
 678+ context.$textarea.trigger( 'wikiEditor-toolbar-buildSection-' + $section.attr( 'rel' ), [section] );
 679+ switch ( section.type ) {
 680+ case 'toolbar':
 681+ if ( 'groups' in section ) {
 682+ for ( group in section.groups ) {
 683+ $section.append(
 684+ $.wikiEditor.modules.toolbar.fn.buildGroup( context, group, section.groups[group] )
 685+ );
 686+ }
 687+ }
 688+ break;
 689+ case 'booklet':
 690+ var $pages = $( '<div />' ).addClass( 'pages' );
 691+ var $index = $( '<div />' ).addClass( 'index' );
 692+ if ( 'pages' in section ) {
 693+ for ( page in section.pages ) {
 694+ $pages.append(
 695+ $.wikiEditor.modules.toolbar.fn.buildPage( context, page, section.pages[page] )
 696+ );
 697+ $index.append(
 698+ $.wikiEditor.modules.toolbar.fn.buildBookmark( context, page, section.pages[page] )
 699+ );
 700+ }
 701+ }
 702+ $section.append( $index ).append( $pages );
 703+ $.wikiEditor.modules.toolbar.fn.updateBookletSelection( context, id, $pages, $index );
 704+ break;
 705+ }
 706+ },
 707+ updateBookletSelection : function( context, id, $pages, $index ) {
 708+ var cookie = 'wikiEditor-' + context.instance + '-booklet-' + id + '-page';
 709+ var selected = $.cookie( cookie );
 710+ // Re-save cookie
 711+ if ( selected != null ) {
 712+ $.cookie( cookie, selected, { expires: 30, path: '/' } );
 713+ }
 714+ var $selectedIndex = $index.find( '*[rel=' + selected + ']' );
 715+ if ( $selectedIndex.size() == 0 ) {
 716+ selected = $index.children().eq( 0 ).attr( 'rel' );
 717+ $.cookie( cookie, selected, { expires: 30, path: '/' } );
 718+ }
 719+ $pages.children().hide();
 720+ $pages.find( '*[rel=' + selected + ']' ).show();
 721+ $index.children().removeClass( 'current' );
 722+ $selectedIndex.addClass( 'current' );
 723+ },
 724+ build : function( context, config ) {
 725+ var $tabs = $( '<div />' ).addClass( 'tabs' ).appendTo( context.modules.toolbar.$toolbar );
 726+ var $sections = $( '<div />' ).addClass( 'sections' ).appendTo( context.modules.toolbar.$toolbar );
 727+ context.modules.toolbar.$toolbar.append( $( '<div />' ).css( 'clear', 'both' ) );
 728+ var sectionQueue = [];
 729+ for ( section in config ) {
 730+ if ( section == 'main' ) {
 731+ context.modules.toolbar.$toolbar.prepend(
 732+ $.wikiEditor.modules.toolbar.fn.buildSection( context, section, config[section] )
 733+ );
 734+ } else {
 735+ sectionQueue.push( {
 736+ '$sections' : $sections,
 737+ 'context' : context,
 738+ 'id' : section,
 739+ 'config' : config[section]
 740+ } );
 741+ $tabs.append( $.wikiEditor.modules.toolbar.fn.buildTab( context, section, config[section] ) );
 742+ }
 743+ }
 744+ $.eachAsync( sectionQueue, {
 745+ 'bulk' : 0,
 746+ 'end' : function() {
 747+ // HACK: Opera doesn't seem to want to redraw after these bits
 748+ // are added to the DOM, so we can just FORCE it!
 749+ var oldValue = $( 'body' ).css( 'position' );
 750+ $( 'body' ).css( 'position', 'static' );
 751+ $( 'body' ).css( 'position', oldValue );
 752+ },
 753+ 'loop' : function( i, s ) {
 754+ s.$sections.append( $.wikiEditor.modules.toolbar.fn.buildSection( s.context, s.id, s.config ) );
 755+ var $section = s.$sections.find( '.section:visible' );
 756+ if ( $section.size() ) {
 757+ $sections.animate( { 'height': $section.outerHeight() }, $section.outerHeight() * 2, function( ) {
 758+ context.fn.trigger( 'resize' );
 759+ } );
 760+ }
 761+ }
 762+ } );
 763+ }
 764+}
 765+
 766+}; } )( jQuery );
Property changes on: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.js
___________________________________________________________________
Added: svn:eol-style
1767 + native
Index: trunk/extensions/WikiEditor/modules/images/dialogs/close_x.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/close_x.png
___________________________________________________________________
Added: svn:mime-type
2768 + image/png
Index: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-exists.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-exists.png
___________________________________________________________________
Added: svn:mime-type
3769 + image/png
Index: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-notexists.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-notexists.png
___________________________________________________________________
Added: svn:mime-type
4770 + image/png
Index: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-external-rtl.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-external-rtl.png
___________________________________________________________________
Added: svn:mime-type
5771 + image/png
Index: trunk/extensions/WikiEditor/modules/images/dialogs/loading.gif
Cannot display: file marked as a binary type.
svn:mime-type = image/gif
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/loading.gif
___________________________________________________________________
Added: svn:mime-type
6772 + image/gif
Index: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-external.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-external.png
___________________________________________________________________
Added: svn:mime-type
7773 + image/png
Index: trunk/extensions/WikiEditor/modules/images/dialogs/button_disabled.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/button_disabled.png
___________________________________________________________________
Added: svn:mime-type
8774 + image/png
Index: trunk/extensions/WikiEditor/modules/images/dialogs/button_down.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/button_down.png
___________________________________________________________________
Added: svn:mime-type
9775 + image/png
Index: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-invalid.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-invalid.png
___________________________________________________________________
Added: svn:mime-type
10776 + image/png
Index: trunk/extensions/WikiEditor/modules/images/dialogs/button_off.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/button_off.png
___________________________________________________________________
Added: svn:mime-type
11777 + image/png
Index: trunk/extensions/WikiEditor/modules/images/dialogs/titlebar_fade.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/titlebar_fade.png
___________________________________________________________________
Added: svn:mime-type
12778 + image/png
Index: trunk/extensions/WikiEditor/modules/images/dialogs/button_over.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/button_over.png
___________________________________________________________________
Added: svn:mime-type
13779 + image/png
Index: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-error.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/dialogs/insert-link-error.png
___________________________________________________________________
Added: svn:mime-type
14780 + image/png
Index: trunk/extensions/WikiEditor/modules/images/templateEditor/text-base.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/templateEditor/text-base.png
___________________________________________________________________
Added: svn:mime-type
15781 + image/png
Index: trunk/extensions/WikiEditor/modules/images/templateEditor/expand.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/templateEditor/expand.png
___________________________________________________________________
Added: svn:mime-type
16782 + image/png
Index: trunk/extensions/WikiEditor/modules/images/templateEditor/collapse.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/templateEditor/collapse.png
___________________________________________________________________
Added: svn:mime-type
17783 + image/png
Index: trunk/extensions/WikiEditor/modules/images/templateEditor/dialog-collapsed.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/templateEditor/dialog-collapsed.png
___________________________________________________________________
Added: svn:mime-type
18784 + image/png
Index: trunk/extensions/WikiEditor/modules/images/templateEditor/dialog-expanded.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/templateEditor/dialog-expanded.png
___________________________________________________________________
Added: svn:mime-type
19785 + image/png
Index: trunk/extensions/WikiEditor/modules/images/templateEditor/wiki-text.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/templateEditor/wiki-text.png
___________________________________________________________________
Added: svn:mime-type
20786 + image/png
Index: trunk/extensions/WikiEditor/modules/images/templateEditor/name-base.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/templateEditor/name-base.png
___________________________________________________________________
Added: svn:mime-type
21787 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/loading.gif
Cannot display: file marked as a binary type.
svn:mime-type = image/gif
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/loading.gif
___________________________________________________________________
Added: svn:mime-type
22788 + image/gif
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-ka.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-ka.png
___________________________________________________________________
Added: svn:mime-type
23789 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/insert-link.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/insert-link.png
___________________________________________________________________
Added: svn:mime-type
24790 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/insert-reference.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/insert-reference.png
___________________________________________________________________
Added: svn:mime-type
25791 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/insert-redirect.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/insert-redirect.png
___________________________________________________________________
Added: svn:mime-type
26792 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/insert-signature.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/insert-signature.png
___________________________________________________________________
Added: svn:mime-type
27793 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-subscript.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-subscript.png
___________________________________________________________________
Added: svn:mime-type
28794 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/loading-small.gif
Cannot display: file marked as a binary type.
svn:mime-type = image/gif
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/loading-small.gif
___________________________________________________________________
Added: svn:mime-type
29795 + image/gif
Index: trunk/extensions/WikiEditor/modules/images/toolbar/arrow-right.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/arrow-right.png
___________________________________________________________________
Added: svn:mime-type
30796 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold.png
___________________________________________________________________
Added: svn:mime-type
31797 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-indent.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-indent.png
___________________________________________________________________
Added: svn:mime-type
32798 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-superscript.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-superscript.png
___________________________________________________________________
Added: svn:mime-type
33799 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-A.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-A.png
___________________________________________________________________
Added: svn:mime-type
34800 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-C.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-C.png
___________________________________________________________________
Added: svn:mime-type
35801 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/example-image.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/example-image.png
___________________________________________________________________
Added: svn:mime-type
36802 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-D.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-D.png
___________________________________________________________________
Added: svn:mime-type
37803 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/button-sprite.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/button-sprite.png
___________________________________________________________________
Added: svn:mime-type
38804 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-I.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-I.png
___________________________________________________________________
Added: svn:mime-type
39805 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-K.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-K.png
___________________________________________________________________
Added: svn:mime-type
40806 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-A.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-A.png
___________________________________________________________________
Added: svn:mime-type
41807 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-ka.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic-ka.png
___________________________________________________________________
Added: svn:mime-type
42808 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-B.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-B.png
___________________________________________________________________
Added: svn:mime-type
43809 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-F.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-F.png
___________________________________________________________________
Added: svn:mime-type
44810 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-G.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-G.png
___________________________________________________________________
Added: svn:mime-type
45811 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-small.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-small.png
___________________________________________________________________
Added: svn:mime-type
46812 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/arrow-down.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/arrow-down.png
___________________________________________________________________
Added: svn:mime-type
47813 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-N.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-N.png
___________________________________________________________________
Added: svn:mime-type
48814 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/insert-gallery.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/insert-gallery.png
___________________________________________________________________
Added: svn:mime-type
49815 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/insert-nowiki.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/insert-nowiki.png
___________________________________________________________________
Added: svn:mime-type
50816 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/insert-newline.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/insert-newline.png
___________________________________________________________________
Added: svn:mime-type
51817 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-P.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-P.png
___________________________________________________________________
Added: svn:mime-type
52818 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-link.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-link.png
___________________________________________________________________
Added: svn:mime-type
53819 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-reference.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-reference.png
___________________________________________________________________
Added: svn:mime-type
54820 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-signature.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-signature.png
___________________________________________________________________
Added: svn:mime-type
55821 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-redirect.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-redirect.png
___________________________________________________________________
Added: svn:mime-type
56822 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-subscript.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-subscript.png
___________________________________________________________________
Added: svn:mime-type
57823 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/arrow-right.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/arrow-right.png
___________________________________________________________________
Added: svn:mime-type
58824 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold.png
___________________________________________________________________
Added: svn:mime-type
59825 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-superscript.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-superscript.png
___________________________________________________________________
Added: svn:mime-type
60826 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-A.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-A.png
___________________________________________________________________
Added: svn:mime-type
61827 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-C.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-C.png
___________________________________________________________________
Added: svn:mime-type
62828 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-I.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-I.png
___________________________________________________________________
Added: svn:mime-type
63829 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-K.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-K.png
___________________________________________________________________
Added: svn:mime-type
64830 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-A.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-A.png
___________________________________________________________________
Added: svn:mime-type
65831 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-B.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-B.png
___________________________________________________________________
Added: svn:mime-type
66832 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-F.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-F.png
___________________________________________________________________
Added: svn:mime-type
67833 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-G.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-G.png
___________________________________________________________________
Added: svn:mime-type
68834 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-small.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-small.png
___________________________________________________________________
Added: svn:mime-type
69835 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/arrow-down.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/arrow-down.png
___________________________________________________________________
Added: svn:mime-type
70836 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-N.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-N.png
___________________________________________________________________
Added: svn:mime-type
71837 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-gallery.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-gallery.png
___________________________________________________________________
Added: svn:mime-type
72838 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-nowiki.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-nowiki.png
___________________________________________________________________
Added: svn:mime-type
73839 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-P.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-P.png
___________________________________________________________________
Added: svn:mime-type
74840 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-newline.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-newline.png
___________________________________________________________________
Added: svn:mime-type
75841 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-V.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-V.png
___________________________________________________________________
Added: svn:mime-type
76842 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/search-replace.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/search-replace.png
___________________________________________________________________
Added: svn:mime-type
77843 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-olist.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-olist.png
___________________________________________________________________
Added: svn:mime-type
78844 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/generate.sh
@@ -0,0 +1,12 @@
 2+#! /bin/bash
 3+
 4+# Compresses all PNGs in the current directory and puts the compressed
 5+# version in the parent directory
 6+#
 7+# Requires pngcrush
 8+
 9+for f in *.png
 10+do
 11+ pngcrush $f ../$f
 12+done
 13+
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/generate.sh
___________________________________________________________________
Added: svn:eol-style
114 + native
Added: svn:executable
215 + *
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-file.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-file.png
___________________________________________________________________
Added: svn:mime-type
316 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/arrow-left.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/arrow-left.png
___________________________________________________________________
Added: svn:mime-type
417 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-ulist.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-ulist.png
___________________________________________________________________
Added: svn:mime-type
518 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-big.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-big.png
___________________________________________________________________
Added: svn:mime-type
619 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-table.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/insert-table.png
___________________________________________________________________
Added: svn:mime-type
720 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-italic.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/png24/format-italic.png
___________________________________________________________________
Added: svn:mime-type
821 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/insert-ilink.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/insert-ilink.png
___________________________________________________________________
Added: svn:mime-type
922 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-V.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-V.png
___________________________________________________________________
Added: svn:mime-type
1023 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/base.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/base.png
___________________________________________________________________
Added: svn:mime-type
1124 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/search-replace.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/search-replace.png
___________________________________________________________________
Added: svn:mime-type
1225 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/magnify-clip.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/magnify-clip.png
___________________________________________________________________
Added: svn:mime-type
1326 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-olist.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-olist.png
___________________________________________________________________
Added: svn:mime-type
1427 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/insert-file.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/insert-file.png
___________________________________________________________________
Added: svn:mime-type
1528 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/arrow-left.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/arrow-left.png
___________________________________________________________________
Added: svn:mime-type
1629 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-ulist.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-ulist.png
___________________________________________________________________
Added: svn:mime-type
1730 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-big.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-big.png
___________________________________________________________________
Added: svn:mime-type
1831 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/insert-xlink.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/insert-xlink.png
___________________________________________________________________
Added: svn:mime-type
1932 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/insert-table.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/insert-table.png
___________________________________________________________________
Added: svn:mime-type
2033 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-ru.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-bold-ru.png
___________________________________________________________________
Added: svn:mime-type
2134 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toolbar/format-italic.png
___________________________________________________________________
Added: svn:mime-type
2235 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toc/grip.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toc/grip.png
___________________________________________________________________
Added: svn:mime-type
2336 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toc/open.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toc/open.png
___________________________________________________________________
Added: svn:mime-type
2437 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toc/close.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toc/close.png
___________________________________________________________________
Added: svn:mime-type
2538 + image/png
Index: trunk/extensions/WikiEditor/modules/images/toc/grab.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/WikiEditor/modules/images/toc/grab.png
___________________________________________________________________
Added: svn:mime-type
2639 + image/png
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.publish.js
@@ -0,0 +1,146 @@
 2+/* Publish module for wikiEditor */
 3+( function( $ ) { $.wikiEditor.modules.publish = {
 4+
 5+/**
 6+ * Compatability map
 7+ */
 8+'browsers': {
 9+ // Left-to-right languages
 10+ 'ltr': {
 11+ 'msie': [['>=', 7]],
 12+ 'firefox': [['>=', 3]],
 13+ 'opera': [['>=', 9.6]],
 14+ 'safari': [['>=', 4]]
 15+ },
 16+ // Right-to-left languages
 17+ 'rtl': {
 18+ 'msie': [['>=', 8]],
 19+ 'firefox': [['>=', 3]],
 20+ 'opera': [['>=', 9.6]],
 21+ 'safari': [['>=', 4]]
 22+ }
 23+},
 24+/**
 25+ * Internally used functions
 26+ */
 27+fn: {
 28+ /**
 29+ * Creates a publish module within a wikiEditor
 30+ * @param context Context object of editor to create module in
 31+ * @param config Configuration object to create module from
 32+ */
 33+ create: function( context, config ) {
 34+ // Build the dialog behind the Publish button
 35+ var dialogID = 'wikiEditor-' + context.instance + '-dialog';
 36+ $.wikiEditor.modules.dialogs.fn.create(
 37+ context,
 38+ {
 39+ previewsave: {
 40+ id: dialogID,
 41+ titleMsg: 'wikieditor-publish-dialog-title',
 42+ html: '\
 43+ <div class="wikiEditor-publish-dialog-copywarn"></div>\
 44+ <div class="wikiEditor-publish-dialog-editoptions">\
 45+ <form id="wikieditor-' + context.instance + '-publish-dialog-form">\
 46+ <div class="wikiEditor-publish-dialog-summary">\
 47+ <label for="wikiEditor-' + context.instance + '-dialog-summary"\
 48+ rel="wikieditor-publish-dialog-summary"></label>\
 49+ <br />\
 50+ <input type="text" id="wikiEditor-' + context.instance + '-dialog-summary"\
 51+ style="width: 100%;" />\
 52+ </div>\
 53+ <div class="wikiEditor-publish-dialog-options">\
 54+ <input type="checkbox"\
 55+ id="wikiEditor-' + context.instance + '-dialog-minor" />\
 56+ <label for="wikiEditor-' + context.instance + '-dialog-minor"\
 57+ rel="wikieditor-publish-dialog-minor"></label>\
 58+ <input type="checkbox"\
 59+ id="wikiEditor-' + context.instance + '-dialog-watch" />\
 60+ <label for="wikiEditor-' + context.instance + '-dialog-watch"\
 61+ rel="wikieditor-publish-dialog-watch"></label>\
 62+ </div>\
 63+ </form>\
 64+ </div>',
 65+ init: function() {
 66+ $(this).find( '[rel]' ).each( function() {
 67+ $(this).text( mw.usability.getMsg( $(this).attr( 'rel' ) ) );
 68+ });
 69+
 70+ /* REALLY DIRTY HACK! */
 71+ // Reformat the copyright warning stuff
 72+ var copyWarnHTML = $( '#editpage-copywarn p' ).html();
 73+ // TODO: internationalize by splitting on other characters that end statements
 74+ var copyWarnStatements = copyWarnHTML.split( '. ' );
 75+ var newCopyWarnHTML = '<ul>';
 76+ for ( var i = 0; i < copyWarnStatements.length; i++ ) {
 77+ if ( copyWarnStatements[i] != '' ) {
 78+ var copyWarnStatement = $j.trim( copyWarnStatements[i] ).replace( /\.*$/, '' );
 79+ newCopyWarnHTML += '<li>' + copyWarnStatement + '.</li>';
 80+ }
 81+ }
 82+ newCopyWarnHTML += '</ul>';
 83+ // No list if there's only one element
 84+ $(this).find( '.wikiEditor-publish-dialog-copywarn' ).html(
 85+ copyWarnStatements.length > 1 ? newCopyWarnHTML : copyWarnHTML
 86+ );
 87+ /* END OF REALLY DIRTY HACK */
 88+
 89+ if ( $( '#wpMinoredit' ).size() == 0 )
 90+ $( '#wikiEditor-' + context.instance + '-dialog-minor' ).hide();
 91+ else if ( $( '#wpMinoredit' ).is( ':checked' ) )
 92+ $( '#wikiEditor-' + context.instance + '-dialog-minor' )
 93+ .attr( 'checked', 'checked' );
 94+ if ( $( '#wpWatchthis' ).size() == 0 )
 95+ $( '#wikiEditor-' + context.instance + '-dialog-watch' ).hide();
 96+ else if ( $( '#wpWatchthis' ).is( ':checked' ) )
 97+ $( '#wikiEditor-' + context.instance + '-dialog-watch' )
 98+ .attr( 'checked', 'checked' );
 99+
 100+ $(this).find( 'form' ).submit( function( e ) {
 101+ $(this).closest( '.ui-dialog' ).find( 'button:first' ).click();
 102+ e.preventDefault();
 103+ });
 104+ },
 105+ dialog: {
 106+ buttons: {
 107+ 'wikieditor-publish-dialog-publish': function() {
 108+ var minorChecked = $( '#wikiEditor-' + context.instance +
 109+ '-dialog-minor' ).is( ':checked' ) ?
 110+ 'checked' : '';
 111+ var watchChecked = $( '#wikiEditor-' + context.instance +
 112+ '-dialog-watch' ).is( ':checked' ) ?
 113+ 'checked' : '';
 114+ $( '#wpMinoredit' ).attr( 'checked', minorChecked );
 115+ $( '#wpWatchthis' ).attr( 'checked', watchChecked );
 116+ $( '#wpSummary' ).val( $j( '#wikiEditor-' + context.instance +
 117+ '-dialog-summary' ).val() );
 118+ $( '#editform' ).submit();
 119+ },
 120+ 'wikieditor-publish-dialog-goback': function() {
 121+ $(this).dialog( 'close' );
 122+ }
 123+ },
 124+ open: function() {
 125+ $( '#wikiEditor-' + context.instance + '-dialog-summary' ).focus();
 126+ },
 127+ width: 500
 128+ },
 129+ resizeme: false
 130+ }
 131+ }
 132+ );
 133+ context.fn.addButton( {
 134+ 'captionMsg': 'wikieditor-publish-button-publish',
 135+ 'action': function() {
 136+ $( '#' + dialogID ).dialog( 'open' );
 137+ return false;
 138+ }
 139+ } );
 140+ context.fn.addButton( {
 141+ 'captionMsg': 'wikieditor-publish-button-cancel',
 142+ 'action': function() { }
 143+ } );
 144+ }
 145+}
 146+
 147+}; } )( jQuery );
Property changes on: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.publish.js
___________________________________________________________________
Added: svn:eol-style
1148 + native
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js
@@ -0,0 +1,357 @@
 2+/* Highlight module for wikiEditor */
 3+
 4+( function( $ ) { $.wikiEditor.modules.highlight = {
 5+
 6+/**
 7+ * Core Requirements
 8+ */
 9+'req': [ 'iframe' ],
 10+/**
 11+ * Configuration
 12+ */
 13+'cfg': {
 14+ 'styleVersion': 3
 15+},
 16+/**
 17+ * Internally used event handlers
 18+ */
 19+'evt': {
 20+ 'delayedChange': function( context, event ) {
 21+ if ( event.data.scope == 'realchange' ) {
 22+ $.wikiEditor.modules.highlight.fn.scan( context );
 23+ $.wikiEditor.modules.highlight.fn.mark( context, event.data.scope );
 24+ }
 25+ },
 26+ 'ready': function( context, event ) {
 27+ $.wikiEditor.modules.highlight.fn.scan( context );
 28+ $.wikiEditor.modules.highlight.fn.mark( context, 'ready' );
 29+ }
 30+},
 31+/**
 32+ * Internally used functions
 33+ */
 34+'fn': {
 35+ /**
 36+ * Creates a highlight module within a wikiEditor
 37+ *
 38+ * @param config Configuration object to create module from
 39+ */
 40+ 'create': function( context, config ) {
 41+ context.modules.highlight.markersStr = '';
 42+ },
 43+ /**
 44+ * Scans text division for tokens
 45+ *
 46+ * @param division
 47+ */
 48+ 'scan': function( context, division ) {
 49+ // Remove all existing tokens
 50+ var tokenArray = context.modules.highlight.tokenArray = [];
 51+ // Scan text for new tokens
 52+ var text = context.fn.getContents();
 53+ // Perform a scan for each module which provides any expressions to scan for
 54+ // FIXME: This traverses the entire string once for every regex. Investigate
 55+ // whether |-concatenating regexes then traversing once is faster.
 56+ for ( var module in context.modules ) {
 57+ if ( module in $.wikiEditor.modules && 'exp' in $.wikiEditor.modules[module] ) {
 58+ for ( var exp in $.wikiEditor.modules[module].exp ) {
 59+ // Prepare configuration
 60+ var regex = $.wikiEditor.modules[module].exp[exp].regex;
 61+ var label = $.wikiEditor.modules[module].exp[exp].label;
 62+ var markAfter = $.wikiEditor.modules[module].exp[exp].markAfter || false;
 63+ // Search for tokens
 64+ var offset = 0, left, right, match;
 65+ while ( ( match = text.substr( offset ).match( regex ) ) != null ) {
 66+ right = ( left = offset + match.index ) + match[0].length;
 67+ tokenArray[tokenArray.length] = {
 68+ 'offset': markAfter ? right : left,
 69+ 'label': label,
 70+ 'tokenStart': left,
 71+ 'match': match
 72+ };
 73+ // Move to the right of this match
 74+ offset = right;
 75+ }
 76+ }
 77+ }
 78+ }
 79+ // Sort by start
 80+ tokenArray.sort( function( a, b ) { return a.tokenStart - b.tokenStart; } );
 81+ // Let the world know, a scan just happened!
 82+ context.fn.trigger( 'scan' );
 83+ },
 84+ /**
 85+ * Marks up text with HTML
 86+ *
 87+ * @param division
 88+ * @param tokens
 89+ */
 90+ // FIXME: What do division and tokens do?
 91+ // TODO: Document the scan() and mark() APIs somewhere
 92+ 'mark': function( context, division, tokens ) {
 93+ // Reset markers
 94+ var markers = [];
 95+
 96+ // Recycle markers that will be skipped in this run
 97+ if ( context.modules.highlight.markers && division != '' ) {
 98+ for ( var i = 0; i < context.modules.highlight.markers.length; i++ ) {
 99+ if ( context.modules.highlight.markers[i].skipDivision == division ) {
 100+ markers.push( context.modules.highlight.markers[i] );
 101+ }
 102+ }
 103+ }
 104+ context.modules.highlight.markers = markers;
 105+
 106+ // Get all markers
 107+ context.fn.trigger( 'mark' );
 108+ markers.sort( function( a, b ) { return a.start - b.start || a.end - b.end; } );
 109+
 110+ // Serialize the markers array to a string and compare it with the one stored in the previous run - if they're
 111+ // equal, there's no markers to change
 112+ var markersStr = '';
 113+ for ( var i = 0; i < markers.length; i++ ) {
 114+ markersStr += markers[i].start + ',' + markers[i].end + ',' + markers[i].type + ',';
 115+ }
 116+ if ( context.modules.highlight.markersStr == markersStr ) {
 117+ // No change, bail out
 118+ return;
 119+ }
 120+ context.modules.highlight.markersStr = markersStr;
 121+
 122+ // Traverse the iframe DOM, inserting markers where they're needed - store visited markers here so we know which
 123+ // markers should be removed
 124+ var visited = [], v = 0;
 125+ for ( var i = 0; i < markers.length; i++ ) {
 126+ if ( typeof markers[i].skipDivision !== 'undefined' && ( division == markers[i].skipDivision ) ) {
 127+ continue;
 128+ }
 129+
 130+ // We want to isolate each marker, so we may need to split textNodes if a marker starts or ends halfway one.
 131+ var start = markers[i].start;
 132+ var s = context.fn.getOffset( start );
 133+ if ( !s ) {
 134+ // This shouldn't happen
 135+ continue;
 136+ }
 137+ var startNode = s.node;
 138+
 139+ // Don't wrap leading BRs, produces undesirable results
 140+ // FIXME: It's also possible that the offset is a bit high because getOffset() has incremented .length to
 141+ // fake the newline caused by startNode being in a P. In this case, prevent the textnode splitting below
 142+ // from making startNode an empty textnode, IE barfs on that
 143+ while ( startNode.nodeName == 'BR' || s.offset == startNode.nodeValue.length ) {
 144+ start++;
 145+ s = context.fn.getOffset( start );
 146+ startNode = s.node;
 147+ }
 148+
 149+ // The next marker starts somewhere in this textNode or at this BR
 150+ if ( s.offset > 0 && s.node.nodeName == '#text' ) {
 151+ // Split off the prefix - this leaves the prefix in the current node and puts the rest in a new node
 152+ // which is our start node
 153+ var newStartNode = startNode.splitText( s.offset < s.node.nodeValue.length ?
 154+ s.offset : s.node.nodeValue.length - 1
 155+ );
 156+ var oldStartNode = startNode;
 157+ startNode = newStartNode;
 158+ // Update offset objects. We don't need purgeOffsets(), simply manipulating the existing offset objects
 159+ // will suffice
 160+ // FIXME: This manipulates context.offsets directly, which is ugly, but the performance improvement vs.
 161+ // purgeOffsets() is worth it - this code doesn't set lastTextNode to newStartNode for offset objects
 162+ // with lastTextNode == oldStartNode, but that doesn't really matter
 163+ var subtracted = s.offset;
 164+ var oldLength = s.length;
 165+
 166+ var j, o;
 167+ // Update offset objects referring to oldStartNode
 168+ for ( j = start - subtracted; j < start; j++ ) {
 169+ if ( j in context.offsets ) {
 170+ o = context.offsets[j];
 171+ o.node = oldStartNode;
 172+ o.length = subtracted;
 173+ }
 174+ }
 175+ // Update offset objects referring to newStartNode
 176+ for ( j = start; j < start - subtracted + oldLength; j++ ) {
 177+ if ( j in context.offsets ) {
 178+ o = context.offsets[j];
 179+ o.node = newStartNode;
 180+ o.offset -= subtracted;
 181+ o.length -= subtracted;
 182+ o.lastTextNode = oldStartNode;
 183+ }
 184+ }
 185+ }
 186+ var end = markers[i].end;
 187+ // To avoid ending up at the first char of the next node, we grab the offset for end - 1 and add one to the
 188+ // offset
 189+ var e = context.fn.getOffset( end - 1 );
 190+ if ( !e ) {
 191+ // This shouldn't happen
 192+ continue;
 193+ }
 194+ var endNode = e.node;
 195+ if ( e.offset + 1 < e.length - 1 && endNode.nodeName == '#text' ) {
 196+ // Split off the suffix. This puts the suffix in a new node and leaves the rest in endNode
 197+ var oldEndNode = endNode;
 198+ var newEndNode = endNode.splitText( e.offset + 1 );
 199+ // Update offset objects
 200+ var subtracted = e.offset + 1;
 201+ var oldLength = e.length;
 202+ var j, o;
 203+ // Update offset objects referring to oldEndNode
 204+ for ( j = end - subtracted; j < end; j++ ) {
 205+ if ( j in context.offsets ) {
 206+ o = context.offsets[j];
 207+ o.node = oldEndNode;
 208+ o.length = subtracted;
 209+ }
 210+ }
 211+ // We have to insert this one, as it might not exist: we didn't call getOffset( end )
 212+ context.offsets[end] = {
 213+ 'node': newEndNode,
 214+ 'offset': 0,
 215+ 'length': oldLength - subtracted,
 216+ 'lastTextNode': oldEndNode
 217+ };
 218+ // Update offset objects referring to newEndNode
 219+ for ( j = end + 1; j < end - subtracted + oldLength; j++ ) {
 220+ if ( j in context.offsets ) {
 221+ o = context.offsets[j];
 222+ o.node = newEndNode;
 223+ o.offset -= subtracted;
 224+ o.length -= subtracted;
 225+ o.lastTextNode = oldEndNode;
 226+ }
 227+ }
 228+ }
 229+ // Don't wrap trailing BRs, doing that causes weird issues
 230+ if ( endNode.nodeName == 'BR' ) {
 231+ endNode = e.lastTextNode;
 232+ }
 233+ // If startNode and endNode have different parents, we need to pull endNode and all textnodes in between
 234+ // into startNode's parent and replace </p><p> with <br>
 235+ if ( startNode.parentNode != endNode.parentNode ) {
 236+ var startP = $( startNode ).closest( 'p' ).get( 0 );
 237+ var t = new context.fn.rawTraverser( startNode, startP, context.$content.get( 0 ), false );
 238+ var afterStart = startNode.nextSibling;
 239+ var lastP = startP;
 240+ var nextT = t.next();
 241+ while ( nextT && t.node != endNode ) {
 242+ t = nextT;
 243+ nextT = t.next();
 244+ // If t.node has a different parent, merge t.node.parentNode with startNode.parentNode
 245+ if ( t.node.parentNode != startNode.parentNode ) {
 246+ var oldParent = t.node.parentNode;
 247+ if ( afterStart ) {
 248+ if ( lastP != t.inP ) {
 249+ // We're entering a new <p>, insert a <br>
 250+ startNode.parentNode.insertBefore(
 251+ startNode.ownerDocument.createElement( 'br' ),
 252+ afterStart
 253+ );
 254+ }
 255+ // A <p> with just a <br> in it is an empty line, so let's not bother with unwrapping it
 256+ if ( !( oldParent.childNodes.length == 1 && oldParent.firstChild.nodeName == 'BR' ) ) {
 257+ // Move all children of oldParent into startNode's parent
 258+ while ( oldParent.firstChild ) {
 259+ startNode.parentNode.insertBefore( oldParent.firstChild, afterStart );
 260+ }
 261+ }
 262+ } else {
 263+ if ( lastP != t.inP ) {
 264+ // We're entering a new <p>, insert a <br>
 265+ startNode.parentNode.appendChild(
 266+ startNode.ownerDocument.createElement( 'br' )
 267+ );
 268+ }
 269+ // A <p> with just a <br> in it is an empty line, so let's not bother with unwrapping it
 270+ if ( !( oldParent.childNodes.length == 1 && oldParent.firstChild.nodeName == 'BR' ) ) {
 271+ // Move all children of oldParent into startNode's parent
 272+ while ( oldParent.firstChild ) {
 273+ startNode.parentNode.appendChild( oldParent.firstChild );
 274+ }
 275+ }
 276+ }
 277+ // Remove oldParent, which is now empty
 278+ oldParent.parentNode.removeChild( oldParent );
 279+ }
 280+ lastP = t.inP;
 281+ }
 282+ // Moving nodes around like this invalidates offset objects
 283+ // TODO: Update offset objects ourselves for performance. Requires rewriting this code block to be
 284+ // offset-based rather than traverser-based
 285+ }
 286+ // Now wrap everything between startNode and endNode (may be equal).
 287+ var ca1 = startNode, ca2 = endNode;
 288+ if ( ca1 && ca2 && ca1.parentNode ) {
 289+ var anchor = markers[i].getAnchor( ca1, ca2 );
 290+ if ( !anchor ) {
 291+ var commonAncestor = ca1.parentNode;
 292+ if ( markers[i].anchor == 'wrap') {
 293+ // We have to store things like .parentNode and .nextSibling because appendChild() changes these
 294+ var newNode = ca1.ownerDocument.createElement( 'span' );
 295+ var nextNode = ca2.nextSibling;
 296+ // Append all nodes between ca1 and ca2 (inclusive) to newNode
 297+ var n = ca1;
 298+ while ( n != nextNode ) {
 299+ var ns = n.nextSibling;
 300+ newNode.appendChild( n );
 301+ n = ns;
 302+ }
 303+ // Insert newNode in the right place
 304+ if ( nextNode ) {
 305+ commonAncestor.insertBefore( newNode, nextNode );
 306+ } else {
 307+ commonAncestor.appendChild( newNode );
 308+ }
 309+ anchor = newNode;
 310+ } else if ( markers[i].anchor == 'tag' ) {
 311+ anchor = commonAncestor;
 312+ }
 313+ $( anchor ).data( 'marker', markers[i] ).addClass( 'wikiEditor-highlight' );
 314+ // Allow the module adding this marker to manipulate it
 315+ markers[i].afterWrap( anchor, markers[i] );
 316+
 317+ } else {
 318+ // Update the marker object
 319+ $( anchor ).data( 'marker', markers[i] );
 320+ if ( typeof markers[i].onSkip == 'function' ) {
 321+ markers[i].onSkip( anchor );
 322+ }
 323+ }
 324+ visited[v++] = anchor;
 325+ }
 326+ }
 327+ // Remove markers that were previously inserted but weren't passed to this function - visited[] contains the
 328+ // visited elements in order and find() and each() preserve order
 329+ var j = 0;
 330+ context.$content.find( '.wikiEditor-highlight' ).each( function() {
 331+ if ( visited[j] == this ) {
 332+ // This marker is legit, leave it in
 333+ j++;
 334+ return true;
 335+ }
 336+ // Remove this marker
 337+ var marker = $(this).data( 'marker' );
 338+ if ( marker && typeof marker.skipDivision != 'undefined' && ( division == marker.skipDivision ) ) {
 339+ // Don't remove these either
 340+ return true;
 341+ }
 342+ if ( marker && typeof marker.beforeUnwrap == 'function' )
 343+ marker.beforeUnwrap( this );
 344+ if ( ( marker && marker.anchor == 'tag' ) || $(this).is( 'p' ) ) {
 345+ // Remove all classes
 346+ $(this).removeAttr( 'class' );
 347+ } else {
 348+ // Assume anchor == 'wrap'
 349+ $(this).replaceWith( this.childNodes );
 350+ }
 351+ context.fn.purgeOffsets();
 352+ });
 353+
 354+ }
 355+}
 356+
 357+}; })( jQuery );
 358+
Property changes on: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js
___________________________________________________________________
Added: svn:eol-style
1359 + native
Index: trunk/extensions/WikiEditor/modules/Templates.js
@@ -0,0 +1,16 @@
 2+/* JavaScript for WikiEditor Templates module */
 3+
 4+$j(document).ready( function() {
 5+ // Check preferences for templates
 6+ if ( !wgWikiEditorEnabledModules.templates ) {
 7+ return true;
 8+ }
 9+ // Disable for template namespace
 10+ if ( wgNamespaceNumber == 10 ) {
 11+ return true;
 12+ }
 13+ // Add the templates module
 14+ if ( $j.fn.wikiEditor ) {
 15+ $j( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'templates' );
 16+ }
 17+});
Property changes on: trunk/extensions/WikiEditor/modules/Templates.js
___________________________________________________________________
Added: svn:eol-style
118 + native
Index: trunk/extensions/WikiEditor/modules/PreviewDialog.js
@@ -0,0 +1,12 @@
 2+/* JavaScript for WikiEditor PreviewDialog module */
 3+
 4+$j(document).ready( function() {
 5+ // Check preferences for preview
 6+ if ( !wgWikiEditorEnabledModules.previewDialog ) {
 7+ return true;
 8+ }
 9+ // Add the preview module
 10+ if ( $j.fn.wikiEditor ) {
 11+ $j( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'previewDialog' );
 12+ }
 13+});
Property changes on: trunk/extensions/WikiEditor/modules/PreviewDialog.js
___________________________________________________________________
Added: svn:eol-style
114 + native
Index: trunk/extensions/WikiEditor/modules/Toolbar.js
@@ -0,0 +1,2124 @@
 2+/* JavaScript for WikiEditor Toolbar module */
 3+
 4+$j(document).ready( function() {
 5+ // Check preferences for toolbar
 6+ if ( !wgWikiEditorEnabledModules.toolbar ) {
 7+ return true;
 8+ }
 9+ // Only show content generation dialogs if enabled
 10+ if ( wgWikiEditorPreferences.toolbar.dialogs && $j.wikiEditor.isSupported( $j.wikiEditor.modules.dialogs ) ) {
 11+ $j( '#wpTextbox1' ).addClass( 'toolbar-dialogs' );
 12+ }
 13+ if ( $j.fn.wikiEditor && $j.wikiEditor.isSupported( $j.wikiEditor.modules.toolbar ) ) {
 14+ // Remove the old toolbar
 15+ $j( '#toolbar' ).remove();
 16+ // Add toolbar module
 17+ $j( '#wpTextbox1' ).wikiEditor( 'addModule', {
 18+'toolbar': {
 19+ // Main section
 20+ 'main': {
 21+ type: 'toolbar',
 22+ groups: {
 23+ 'format': {
 24+ tools: {
 25+ 'bold': {
 26+ labelMsg: 'wikieditor-toolbar-tool-bold',
 27+ type: 'button',
 28+ offset: {
 29+ 'default': [2, -574],
 30+ 'en': [2, -142],
 31+ 'cs': [2, -142],
 32+ 'de': [2, -214],
 33+ 'fr': [2, -286],
 34+ 'es': [2, -358],
 35+ 'he': [2, -142],
 36+ 'hu': [2, -214],
 37+ 'it': [2, -286],
 38+ 'nl': [2, -502],
 39+ 'pt': [2, -358],
 40+ 'pt-br': [2, -358],
 41+ 'pl': [2, -142],
 42+ 'ml': [2, -142]
 43+ },
 44+ icon: {
 45+ 'default': 'format-bold.png',
 46+ 'en': 'format-bold-B.png',
 47+ 'cs': 'format-bold-B.png',
 48+ 'de': 'format-bold-F.png',
 49+ 'fr': 'format-bold-G.png',
 50+ 'es': 'format-bold-N.png',
 51+ 'he': 'format-bold-B.png',
 52+ 'hu': 'format-bold-F.png',
 53+ 'it': 'format-bold-G.png',
 54+ 'ka': 'format-bold-ka.png',
 55+ 'nl': 'format-bold-V.png',
 56+ 'pt': 'format-bold-N.png',
 57+ 'pt-br': 'format-bold-N.png',
 58+ 'pl': 'format-bold-B.png',
 59+ 'ru': 'format-bold-ru.png',
 60+ 'ml': 'format-bold-B.png'
 61+ },
 62+ action: {
 63+ type: 'encapsulate',
 64+ options: {
 65+ pre: "'''",
 66+ periMsg: 'wikieditor-toolbar-tool-bold-example',
 67+ post: "'''"
 68+ }
 69+ }
 70+ },
 71+ 'italic': {
 72+ section: 'main',
 73+ group: 'format',
 74+ id: 'italic',
 75+ labelMsg: 'wikieditor-toolbar-tool-italic',
 76+ type: 'button',
 77+ offset: {
 78+ 'default': [2, -718],
 79+ 'en': [2, -862],
 80+ 'cs': [2, -862],
 81+ 'de': [2, -934],
 82+ 'fr': [2, -862],
 83+ 'es': [2, -790],
 84+ 'he': [2, -862],
 85+ 'it': [2, -790],
 86+ 'nl': [2, -790],
 87+ 'pt': [2, -862],
 88+ 'pt-br': [2, -862],
 89+ 'pl': [2, -862],
 90+ 'ru': [2, -934],
 91+ 'ml': [2, -862]
 92+ },
 93+ icon: {
 94+ 'default': 'format-italic.png',
 95+ 'en': 'format-italic-I.png',
 96+ 'cs': 'format-italic-I.png',
 97+ 'de': 'format-italic-K.png',
 98+ 'fr': 'format-italic-I.png',
 99+ 'es': 'format-italic-C.png',
 100+ 'he': 'format-italic-I.png',
 101+ 'hu': 'format-italic-D.png',
 102+ 'it': 'format-italic-C.png',
 103+ 'ka': 'format-italic-ka.png',
 104+ 'nl': 'format-italic-C.png',
 105+ 'pt': 'format-italic-I.png',
 106+ 'pt-br': 'format-italic-I.png',
 107+ 'pl': 'format-italic-I.png',
 108+ 'ru': 'format-italic-K.png',
 109+ 'ml': 'format-italic-I.png'
 110+ },
 111+ action: {
 112+ type: 'encapsulate',
 113+ options: {
 114+ pre: "''",
 115+ periMsg: 'wikieditor-toolbar-tool-italic-example',
 116+ post: "''"
 117+ }
 118+ }
 119+ }
 120+ }
 121+ },
 122+ 'insert': {
 123+ tools: {
 124+ 'xlink': {
 125+ labelMsg: 'wikieditor-toolbar-tool-xlink',
 126+ type: 'button',
 127+ icon: 'insert-xlink.png',
 128+ offset: [-70, 2],
 129+ filters: [ '#wpTextbox1:not(.toolbar-dialogs)' ],
 130+ action: {
 131+ type: 'encapsulate',
 132+ options: {
 133+ pre: "[",
 134+ periMsg: 'wikieditor-toolbar-tool-xlink-example',
 135+ post: "]"
 136+ }
 137+ }
 138+ },
 139+ 'ilink': {
 140+ labelMsg: 'wikieditor-toolbar-tool-ilink',
 141+ type: 'button',
 142+ icon: 'insert-ilink.png',
 143+ offset: [2, -1582],
 144+ filters: [ '#wpTextbox1:not(.toolbar-dialogs)' ],
 145+ action: {
 146+ type: 'encapsulate',
 147+ options: {
 148+ pre: "[[",
 149+ periMsg: 'wikieditor-toolbar-tool-ilink-example',
 150+ post: "]]"
 151+ }
 152+ }
 153+ },
 154+ 'linkCGD': {
 155+ labelMsg: 'wikieditor-toolbar-tool-link',
 156+ type: 'button',
 157+ icon: 'insert-link.png',
 158+ offset: [2, -1654],
 159+ filters: [ '#wpTextbox1.toolbar-dialogs' ],
 160+ action: {
 161+ type: 'dialog',
 162+ module: 'insert-link'
 163+ }
 164+ },
 165+ 'file': {
 166+ labelMsg: 'wikieditor-toolbar-tool-file',
 167+ type: 'button',
 168+ icon: 'insert-file.png',
 169+ offset: [2, -1438],
 170+ action: {
 171+ type: 'encapsulate',
 172+ options: {
 173+ // FIXME: Why the hell was this done this way?
 174+ preMsg: [ 'wikieditor-toolbar-tool-file-pre', '[[' ],
 175+ periMsg: 'wikieditor-toolbar-tool-file-example',
 176+ post: "]]"
 177+ }
 178+ }
 179+ },
 180+ 'referenceCGD': {
 181+ labelMsg: 'wikieditor-toolbar-tool-reference',
 182+ type: 'button',
 183+ icon: 'insert-reference.png',
 184+ offset: [2, -1798],
 185+ filters: [ 'body.ns-subject', '#wpTextbox1.toolbar-dialogs' ],
 186+ action: {
 187+ type: 'dialog',
 188+ module: 'insert-reference'
 189+ }
 190+ },
 191+ 'reference': {
 192+ labelMsg: 'wikieditor-toolbar-tool-reference',
 193+ filters: [ 'body.ns-subject', '#wpTextbox1:not(.toolbar-dialogs)' ],
 194+ type: 'button',
 195+ offset: [2, -1798],
 196+ icon: 'insert-reference.png',
 197+ action: {
 198+ type: 'encapsulate',
 199+ options: {
 200+ pre: "<ref>",
 201+ periMsg: 'wikieditor-toolbar-tool-reference-example',
 202+ post: "</ref>"
 203+ }
 204+ }
 205+ },
 206+ 'signature': {
 207+ labelMsg: 'wikieditor-toolbar-tool-signature',
 208+ filters: [ 'body:not(.ns-0)' ],
 209+ type: 'button',
 210+ offset: [2, -1872],
 211+ icon: 'insert-signature.png',
 212+ action: {
 213+ type: 'encapsulate',
 214+ options: {
 215+ post: "--~~~~"
 216+ }
 217+ }
 218+ }
 219+ }
 220+ }
 221+ }
 222+ },
 223+ // Format section
 224+ 'advanced': {
 225+ labelMsg: 'wikieditor-toolbar-section-advanced',
 226+ type: 'toolbar',
 227+ groups: {
 228+ 'heading': {
 229+ tools: {
 230+ 'heading': {
 231+ labelMsg: 'wikieditor-toolbar-tool-heading',
 232+ type: 'select',
 233+ list: {
 234+ 'heading-2' : {
 235+ labelMsg: 'wikieditor-toolbar-tool-heading-2',
 236+ action: {
 237+ type: 'encapsulate',
 238+ options: {
 239+ pre: '== ',
 240+ periMsg: 'wikieditor-toolbar-tool-heading-example',
 241+ post: ' ==',
 242+ regex: /^(\s*)(={1,6})(.*?)\2(\s*)$/,
 243+ regexReplace: "\$1==\$3==\$4",
 244+ ownline: true
 245+ }
 246+ }
 247+ },
 248+ 'heading-3' : {
 249+ labelMsg: 'wikieditor-toolbar-tool-heading-3',
 250+ action: {
 251+ type: 'encapsulate',
 252+ options: {
 253+ pre: '=== ',
 254+ periMsg: 'wikieditor-toolbar-tool-heading-example',
 255+ post: ' ===',
 256+ regex: /^(\s*)(={1,6})(.*?)\2(\s*)$/,
 257+ regexReplace: "\$1===\$3===\$4",
 258+ ownline: true
 259+ }
 260+ }
 261+ },
 262+ 'heading-4' : {
 263+ labelMsg: 'wikieditor-toolbar-tool-heading-4',
 264+ action: {
 265+ type: 'encapsulate',
 266+ options: {
 267+ pre: '==== ',
 268+ periMsg: 'wikieditor-toolbar-tool-heading-example',
 269+ post: ' ====',
 270+ regex: /^(\s*)(={1,6})(.*?)\2(\s*)$/,
 271+ regexReplace: "\$1====\$3====\$4",
 272+ ownline: true
 273+ }
 274+ }
 275+ },
 276+ 'heading-5' : {
 277+ labelMsg: 'wikieditor-toolbar-tool-heading-5',
 278+ action: {
 279+ type: 'encapsulate',
 280+ options: {
 281+ pre: '===== ',
 282+ periMsg: 'wikieditor-toolbar-tool-heading-example',
 283+ post: ' =====',
 284+ regex: /^(\s*)(={1,6})(.*?)\2(\s*)$/,
 285+ regexReplace: "\$1=====\$3=====\$4",
 286+ ownline: true
 287+ }
 288+ }
 289+ }
 290+ }
 291+ }
 292+ }
 293+ },
 294+ 'format': {
 295+ labelMsg: 'wikieditor-toolbar-group-format',
 296+ tools: {
 297+ 'ulist': {
 298+ labelMsg: 'wikieditor-toolbar-tool-ulist',
 299+ type: 'button',
 300+ icon: 'format-ulist.png',
 301+ offset: [2, -1366],
 302+ action: {
 303+ type: 'encapsulate',
 304+ options: {
 305+ pre: "* ",
 306+ periMsg: 'wikieditor-toolbar-tool-ulist-example',
 307+ post: "",
 308+ ownline: true
 309+ }
 310+ }
 311+ },
 312+ 'olist': {
 313+ labelMsg: 'wikieditor-toolbar-tool-olist',
 314+ type: 'button',
 315+ icon: 'format-olist.png',
 316+ offset: [2, -1078],
 317+ action: {
 318+ type: 'encapsulate',
 319+ options: {
 320+ pre: "# ",
 321+ periMsg: 'wikieditor-toolbar-tool-olist-example',
 322+ post: "",
 323+ ownline: true
 324+ }
 325+ }
 326+ },
 327+ 'indent': {
 328+ labelMsg: 'wikieditor-toolbar-tool-indent',
 329+ type: 'button',
 330+ icon: 'format-indent.png',
 331+ offset: [2, -646],
 332+ action: {
 333+ type: 'encapsulate',
 334+ options: {
 335+ pre: ":",
 336+ periMsg: 'wikieditor-toolbar-tool-indent-example',
 337+ post: "",
 338+ ownline: true,
 339+ splitlines: true
 340+ }
 341+ }
 342+ },
 343+ 'nowiki': {
 344+ labelMsg: 'wikieditor-toolbar-tool-nowiki',
 345+ type: 'button',
 346+ icon: 'insert-nowiki.png',
 347+ offset: [-70, -70],
 348+ action: {
 349+ type: 'encapsulate',
 350+ options: {
 351+ pre: "<nowiki>",
 352+ periMsg: 'wikieditor-toolbar-tool-nowiki-example',
 353+ post: "</nowiki>"
 354+ }
 355+ }
 356+ },
 357+ 'newline': {
 358+ labelMsg: 'wikieditor-toolbar-tool-newline',
 359+ type: 'button',
 360+ icon: 'insert-newline.png',
 361+ offset: [2, -1726],
 362+ action: {
 363+ type: 'encapsulate',
 364+ options: {
 365+ pre: "<br />\n"
 366+ }
 367+ }
 368+ }
 369+ }
 370+ },
 371+ 'size': {
 372+ tools: {
 373+ 'big': {
 374+ labelMsg: 'wikieditor-toolbar-tool-big',
 375+ type: 'button',
 376+ icon: 'format-big.png',
 377+ offset: [2, 2],
 378+ action: {
 379+ type: 'encapsulate',
 380+ options: {
 381+ pre: "<big>",
 382+ periMsg: 'wikieditor-toolbar-tool-big-example',
 383+ post: "</big>"
 384+ }
 385+ }
 386+ },
 387+ 'small': {
 388+ labelMsg: 'wikieditor-toolbar-tool-small',
 389+ type: 'button',
 390+ icon: 'format-small.png',
 391+ offset: [2, -1150],
 392+ action: {
 393+ type: 'encapsulate',
 394+ options: {
 395+ pre: "<small>",
 396+ periMsg: 'wikieditor-toolbar-tool-small-example',
 397+ post: "</small>"
 398+ }
 399+ }
 400+ },
 401+ 'superscript': {
 402+ labelMsg: 'wikieditor-toolbar-tool-superscript',
 403+ type: 'button',
 404+ icon: 'format-superscript.png',
 405+ offset: [2, -1294],
 406+ action: {
 407+ type: 'encapsulate',
 408+ options: {
 409+ pre: "<sup>",
 410+ periMsg: 'wikieditor-toolbar-tool-superscript-example',
 411+ post: "</sup>"
 412+ }
 413+ }
 414+ },
 415+ 'subscript': {
 416+ labelMsg: 'wikieditor-toolbar-tool-subscript',
 417+ type: 'button',
 418+ icon: 'format-subscript.png',
 419+ offset: [2, -1222],
 420+ action: {
 421+ type: 'encapsulate',
 422+ options: {
 423+ pre: "<sub>",
 424+ periMsg: 'wikieditor-toolbar-tool-subscript-example',
 425+ post: "</sub>"
 426+ }
 427+ }
 428+ }
 429+ }
 430+ },
 431+ 'insert': {
 432+ labelMsg: 'wikieditor-toolbar-group-insert',
 433+ tools: {
 434+ 'gallery': {
 435+ labelMsg: 'wikieditor-toolbar-tool-gallery',
 436+ type: 'button',
 437+ icon: 'insert-gallery.png',
 438+ offset: [2, -1510],
 439+ action: {
 440+ type: 'encapsulate',
 441+ options: {
 442+ pre: "<gallery>\n",
 443+ periMsg: 'wikieditor-toolbar-tool-gallery-example',
 444+ post: "\n</gallery>",
 445+ ownline: true
 446+ }
 447+ }
 448+ },
 449+ 'tableCGD': {
 450+ labelMsg: 'wikieditor-toolbar-tool-table',
 451+ type: 'button',
 452+ icon: 'insert-table.png',
 453+ offset: [2, -1942],
 454+ filters: [ '#wpTextbox1.toolbar-dialogs' ],
 455+ action: {
 456+ type: 'dialog',
 457+ module: 'insert-table'
 458+ }
 459+ },
 460+ 'table': {
 461+ labelMsg: 'wikieditor-toolbar-tool-table',
 462+ type: 'button',
 463+ icon: 'insert-table.png',
 464+ offset: [2, -1942],
 465+ filters: [ '#wpTextbox1:not(.toolbar-dialogs)' ],
 466+ action: {
 467+ type: 'encapsulate',
 468+ options: {
 469+ pre: "{| class=\"wikitable\" border=\"1\"\n|",
 470+ periMsg: 'wikieditor-toolbar-tool-table-example-old',
 471+ post: "\n|}",
 472+ ownline: true
 473+ }
 474+ }
 475+ },
 476+ 'redirect': {
 477+ labelMsg: 'wikieditor-toolbar-tool-redirect',
 478+ type: 'button',
 479+ icon: 'insert-redirect.png',
 480+ offset: [-70, -142],
 481+ action: {
 482+ type: 'encapsulate',
 483+ options: {
 484+ pre: "#REDIRECT [[",
 485+ periMsg: 'wikieditor-toolbar-tool-redirect-example',
 486+ post: "]]",
 487+ ownline: true
 488+ }
 489+ }
 490+ }
 491+ }
 492+ },
 493+ 'search': {
 494+ tools: {
 495+ 'replace': {
 496+ labelMsg: 'wikieditor-toolbar-tool-replace',
 497+ type: 'button',
 498+ icon: 'search-replace.png',
 499+ offset: [-70, -214],
 500+ filters: [ '#wpTextbox1.toolbar-dialogs' ],
 501+ action: {
 502+ type: 'dialog',
 503+ module: 'search-and-replace'
 504+ }
 505+ }
 506+ }
 507+ }
 508+ }
 509+ },
 510+ 'characters': {
 511+ labelMsg: 'wikieditor-toolbar-section-characters',
 512+ type: 'booklet',
 513+ deferLoad: true,
 514+ pages: {
 515+ 'latin': {
 516+ 'labelMsg': 'wikieditor-toolbar-characters-page-latin',
 517+ 'layout': 'characters',
 518+ 'characters': [
 519+ "\u00c1", "\u00e1", "\u00c0", "\u00e0", "\u00c2", "\u00e2", "\u00c4", "\u00e4", "\u00c3", "\u00e3",
 520+ "\u01cd", "\u01ce", "\u0100", "\u0101", "\u0102", "\u0103", "\u0104", "\u0105", "\u00c5", "\u00e5",
 521+ "\u0106", "\u0107", "\u0108", "\u0109", "\u00c7", "\u00e7", "\u010c", "\u010d", "\u010a", "\u010b",
 522+ "\u0110", "\u0111", "\u010e", "\u010f", "\u00c9", "\u00e9", "\u00c8", "\u00e8", "\u00ca", "\u00ea",
 523+ "\u00cb", "\u00eb", "\u011a", "\u011b", "\u0112", "\u0113", "\u0114", "\u0115", "\u0116", "\u0117",
 524+ "\u0118", "\u0119", "\u011c", "\u011d", "\u0122", "\u0123", "\u011e", "\u011f", "\u0120", "\u0121",
 525+ "\u0124", "\u0125", "\u0126", "\u0127", "\u00cd", "\u00ed", "\u00cc", "\u00ec", "\u00ce", "\u00ee",
 526+ "\u00cf", "\u00ef", "\u0128", "\u0129", "\u01cf", "\u01d0", "\u012a", "\u012b", "\u012c", "\u012d",
 527+ "\u0130", "\u0131", "\u012e", "\u012f", "\u0134", "\u0135", "\u0136", "\u0137", "\u0139", "\u013a",
 528+ "\u013b", "\u013c", "\u013d", "\u013e", "\u0141", "\u0142", "\u013f", "\u0140", "\u0143", "\u0144",
 529+ "\u00d1", "\u00f1", "\u0145", "\u0146", "\u0147", "\u0148", "\u00d3", "\u00f3", "\u00d2", "\u00f2",
 530+ "\u00d4", "\u00f4", "\u00d6", "\u00f6", "\u00d5", "\u00f5", "\u01d1", "\u01d2", "\u014c", "\u014d",
 531+ "\u014e", "\u014f", "\u01ea", "\u01eb", "\u0150", "\u0151", "\u0154", "\u0155", "\u0156", "\u0157",
 532+ "\u0158", "\u0159", "\u015a", "\u015b", "\u015c", "\u015d", "\u015e", "\u015f", "\u0160", "\u0161",
 533+ "\u0162", "\u0163", "\u0164", "\u0165", "\u00da", "\u00fa", "\u00d9", "\u00f9", "\u00db", "\u00fb",
 534+ "\u00dc", "\u00fc", "\u0168", "\u0169", "\u016e", "\u016f", "\u01d3", "\u01d4", "\u016a", "\u016b",
 535+ "\u01d6", "\u01d8", "\u01da", "\u01dc", "\u016c", "\u016d", "\u0172", "\u0173", "\u0170", "\u0171",
 536+ "\u0174", "\u0175", "\u00dd", "\u00fd", "\u0176", "\u0177", "\u0178", "\u00ff", "\u0232", "\u0233",
 537+ "\u0179", "\u017a", "\u017d", "\u017e", "\u017b", "\u017c", "\u00c6", "\u00e6", "\u01e2", "\u01e3",
 538+ "\u00d8", "\u00f8", "\u0152", "\u0153", "\u00df", "\u00f0", "\u00de", "\u00fe", "\u018f", "\u0259"
 539+ ]
 540+ },
 541+ 'latinextended': {
 542+ 'labelMsg': 'wikieditor-toolbar-characters-page-latinextended',
 543+ 'layout': 'characters',
 544+ 'characters': [
 545+ "\u1e00", "\u1e01", "\u1e9a", "\u1ea0", "\u1ea1", "\u1ea2", "\u1ea3", "\u1ea4", "\u1ea5", "\u1ea6",
 546+ "\u1ea7", "\u1ea8", "\u1ea9", "\u1eaa", "\u1eab", "\u1eac", "\u1ead", "\u1eae", "\u1eaf", "\u1eb0",
 547+ "\u1eb1", "\u1eb2", "\u1eb3", "\u1eb4", "\u1eb5", "\u1eb6", "\u1eb7", "\u1e02", "\u1e03", "\u1e04",
 548+ "\u1e05", "\u1e06", "\u1e07", "\u1e08", "\u1e09", "\u1e0a", "\u1e0b", "\u1e0c", "\u1e0d", "\u1e0e",
 549+ "\u1e0f", "\u1e10", "\u1e11", "\u1e12", "\u1e13", "\u1e14", "\u1e15", "\u1e16", "\u1e17", "\u1e18",
 550+ "\u1e19", "\u1e1a", "\u1e1b", "\u1e1c", "\u1e1d", "\u1eb8", "\u1eb9", "\u1eba", "\u1ebb", "\u1ebc",
 551+ "\u1ebd", "\u1ebe", "\u1ebf", "\u1ec0", "\u1ec1", "\u1ec2", "\u1ec3", "\u1ec4", "\u1ec5", "\u1ec6",
 552+ "\u1ec7", "\u1e1e", "\u1e1f", "\u1e20", "\u1e21", "\u1e22", "\u1e23", "\u1e24", "\u1e25", "\u1e26",
 553+ "\u1e27", "\u1e28", "\u1e29", "\u1e2a", "\u1e2b", "\u1e96", "\u1e2c", "\u1e2d", "\u1e2e", "\u1e2f",
 554+ "\u1ec8", "\u1ec9", "\u1eca", "\u1ecb", "\u1e30", "\u1e31", "\u1e32", "\u1e33", "\u1e34", "\u1e35",
 555+ "\u1e36", "\u1e37", "\u1e38", "\u1e39", "\u1e3a", "\u1e3b", "\u1e3c", "\u1e3d", "\u1efa", "\u1efb",
 556+ "\u1e3e", "\u1e3f", "\u1e40", "\u1e41", "\u1e42", "\u1e43", "\u1e44", "\u1e45", "\u1e46", "\u1e47",
 557+ "\u1e48", "\u1e49", "\u1e4a", "\u1e4b", "\u1e4c", "\u1e4d", "\u1e4e", "\u1e4f", "\u1e50", "\u1e51",
 558+ "\u1e52", "\u1e53", "\u1ecc", "\u1ecd", "\u1ece", "\u1ecf", "\u1ed0", "\u1ed1", "\u1ed2", "\u1ed3",
 559+ "\u1ed4", "\u1ed5", "\u1ed6", "\u1ed7", "\u1ed8", "\u1ed9", "\u1eda", "\u1edb", "\u1edc", "\u1edd",
 560+ "\u1ede", "\u1edf", "\u1ee0", "\u1ee1", "\u1ee2", "\u1ee3", "\u1e54", "\u1e55", "\u1e56", "\u1e57",
 561+ "\u1e58", "\u1e59", "\u1e5a", "\u1e5b", "\u1e5c", "\u1e5d", "\u1e5e", "\u1e5f", "\u1e60", "\u1e61",
 562+ "\u1e9b", "\u1e62", "\u1e63", "\u1e64", "\u1e65", "\u1e66", "\u1e67", "\u1e68", "\u1e69", "\u1e9c",
 563+ "\u1e9d", "\u1e6a", "\u1e6b", "\u1e6c", "\u1e6d", "\u1e6e", "\u1e6f", "\u1e70", "\u1e71", "\u1e97",
 564+ "\u1e72", "\u1e73", "\u1e74", "\u1e75", "\u1e76", "\u1e77", "\u1e78", "\u1e79", "\u1e7a", "\u1e7b",
 565+ "\u1ee4", "\u1ee5", "\u1ee6", "\u1ee7", "\u1ee8", "\u1ee9", "\u1eea", "\u1eeb", "\u1eec", "\u1eed",
 566+ "\u1eee", "\u1eef", "\u1ef0", "\u1ef1", "\u1e7c", "\u1e7d", "\u1e7e", "\u1e7f", "\u1efc", "\u1efd",
 567+ "\u1e80", "\u1e81", "\u1e82", "\u1e83", "\u1e84", "\u1e85", "\u1e86", "\u1e87", "\u1e88", "\u1e89",
 568+ "\u1e98", "\u1e8a", "\u1e8b", "\u1e8c", "\u1e8d", "\u1e8e", "\u1e8f", "\u1e99", "\u1ef2", "\u1ef3",
 569+ "\u1ef4", "\u1ef5", "\u1ef6", "\u1ef7", "\u1ef8", "\u1ef9", "\u1efe", "\u1eff", "\u1e90", "\u1e91",
 570+ "\u1e92", "\u1e93", "\u1e94", "\u1e95", "\u1e9e", "\u1e9f"
 571+ ]
 572+ },
 573+ 'ipa': {
 574+ labelMsg: 'wikieditor-toolbar-characters-page-ipa',
 575+ layout: 'characters',
 576+ characters: [
 577+ "p", "t\u032a", "t", "\u0288", "c", "k", "q", "\u02a1", "\u0294", "b","d\u032a", "d", "\u0256",
 578+ "\u025f", "\u0261", "\u0262", "\u0253", "\u0257", "\u0284", "\u0260", "\u029b", "t\u0361s",
 579+ "t\u0361\u0283", "t\u0361\u0255", "d\u0361z", "d\u0361\u0292", "d\u0361\u0291", "\u0278", "f",
 580+ "\u03b8", "s", "\u0283", "\u0285", "\u0286", "\u0282", "\u0255", "\u00e7", "\u0267", "x", "\u03c7",
 581+ "\u0127", "\u029c", "h", "\u03b2", "v", "\u028d", "\u00f0", "z", "\u0292", "\u0293", "\u0290",
 582+ "\u0291", "\u029d", "\u0263", "\u0281", "\u0295", "\u0296", "\u02a2", "\u0266", "\u026c", "\u026e",
 583+ "m", "m\u0329", "\u0271", "\u0271\u0329", "\u0271\u030d", "n\u032a", "n\u032a\u030d", "n",
 584+ "n\u0329", "\u0273", "\u0273\u0329", "\u0272", "\u0272\u0329", "\u014b", "\u014b\u030d",
 585+ "\u014b\u0329", "\u0274", "\u0274\u0329", "\u0299", "\u0299\u0329", "r", "r\u0329", "\u0280",
 586+ "\u0280\u0329", "\u027e", "\u027d", "\u027f", "\u027a", "l\u032a", "l\u032a\u0329", "l", "l\u0329",
 587+ "\u026b", "\u026b\u0329", "\u026d", "\u026d\u0329", "\u028e", "\u028e\u0329", "\u029f",
 588+ "\u029f\u0329", "w", "\u0265", "\u028b", "\u0279", "\u027b", "j", "\u0270", "\u0298", "\u01c2",
 589+ "\u01c0", "!", "\u01c1", "\u02b0", "\u02b1", "\u02b7", "\u02b8", "\u02b2", "\u02b3", "\u207f",
 590+ "\u02e1", "\u02b4", "\u02b5", "\u02e2", "\u02e3", "\u02e0", "\u02b6", "\u02e4", "\u02c1", "\u02c0",
 591+ "\u02bc", "i", "i\u032f", "\u0129", "y", "y\u032f", "\u1ef9", "\u026a", "\u026a\u032f",
 592+ "\u026a\u0303", "\u028f", "\u028f\u032f", "\u028f\u0303", "\u0268", "\u0268\u032f", "\u0268\u0303",
 593+ "\u0289", "\u0289\u032f", "\u0289\u0303", "\u026f", "\u026f\u032f", "\u026f\u0303", "u", "u\u032f",
 594+ "\u0169", "\u028a", "\u028a\u032f", "\u028a\u0303", "e", "e\u032f", "\u1ebd", "\u00f8",
 595+ "\u00f8\u032f", "\u00f8\u0303", "\u0258", "\u0258\u032f", "\u0258\u0303", "\u0275", "\u0275\u032f",
 596+ "\u0275\u0303", "\u0264", "\u0264\u032f", "\u0264\u0303", "o", "o\u032f", "\u00f5", "\u025b",
 597+ "\u025b\u032f", "\u025b\u0303", "\u0153", "\u0153\u032f", "\u0153\u0303", "\u025c", "\u025c\u032f",
 598+ "\u025c\u0303", "\u0259", "\u0259\u032f", "\u0259\u0303", "\u025e", "\u025e\u032f", "\u025e\u0303",
 599+ "\u028c", "\u028c\u032f", "\u028c\u0303", "\u0254", "\u0254\u032f", "\u0254\u0303", "\u00e6",
 600+ "\u00e6\u032f", "\u00e6\u0303", "\u0276", "\u0276\u032f", "\u0276\u0303", "a", "a\u032f", "\u00e3",
 601+ "\u0250", "\u0250\u032f", "\u0250\u0303", "\u0251", "\u0251\u032f", "\u0251\u0303", "\u0252",
 602+ "\u0252\u032f", "\u0252\u0303", "\u02c8", "\u02cc", "\u02d0", "\u02d1", "\u02d8", ".", "\u203f",
 603+ "|", "\u2016"
 604+ ]
 605+ },
 606+ 'symbols': {
 607+ 'labelMsg': 'wikieditor-toolbar-characters-page-symbols',
 608+ 'layout': 'characters',
 609+ 'characters': [
 610+ "~", "|", "\u00a1", "\u00bf", "\u2020", "\u2021", "\u2194", "\u2191", "\u2193", "\u2022", "\u00b6",
 611+ "#", "\u00bd", "\u2153", "\u2154", "\u00bc", "\u00be", "\u215b", "\u215c", "\u215d", "\u215e",
 612+ "\u221e", "\u2018", "\u2019",
 613+ {
 614+ 'label': "\u201c\u201d",
 615+ 'action': {
 616+ 'type': 'encapsulate', 'options': { 'pre': "\u201c", 'post': "\u201d" }
 617+ }
 618+ },
 619+ {
 620+ 'label': "\u201e\u201c",
 621+ 'action': {
 622+ 'type': 'encapsulate', 'options': { 'pre': "\u201e", 'post': "\u201c" }
 623+ }
 624+ },
 625+ {
 626+ 'label': "\u00ab\u00bb",
 627+ 'action': {
 628+ 'type': 'encapsulate', 'options': { 'pre': "\u00ab", 'post': "\u00bb" }
 629+ }
 630+ },
 631+ "\u00a4", "\u20b3", "\u0e3f", "\u20b5", "\u00a2", "\u20a1", "\u20a2", "$", "\u20ab", "\u20af",
 632+ "\u20ac", "\u20a0", "\u20a3", "\u0192", "\u20b4", "\u20ad", "\u20a4", "\u2133", "\u20a5", "\u20a6",
 633+ "\u2116", "\u20a7", "\u20b0", "\u00a3", "\u17db", "\u20a8", "\u20aa", "\u09f3", "\u20ae", "\u20a9",
 634+ "\u00a5", "\u2660", "\u2663", "\u2665", "\u2666", "m\u00b2", "m\u00b3", "\u2013", "\u2014",
 635+ "\u2026", "\u2018", "\u2019", "\u201c", "\u201d", "\u00b0", "\u2032", "\u2033", "\u2248", "\u2260",
 636+ "\u2264", "\u2265", "\u00b1", "\u2212", "\u00d7", "\u00f7", "\u2190", "\u2192", "\u00b7", "\u00a7"
 637+ ]
 638+ },
 639+ 'greek': {
 640+ 'labelMsg': 'wikieditor-toolbar-characters-page-greek',
 641+ 'layout': 'characters',
 642+ 'language': 'hl',
 643+ 'characters': [
 644+ "\u0391", "\u0386", "\u03b1", "\u03ac", "\u0392", "\u03b2", "\u0393", "\u03b3", "\u0394", "\u03b4",
 645+ "\u0395", "\u0388", "\u03b5", "\u03ad", "\u0396", "\u03b6", "\u0397", "\u0389", "\u03b7", "\u03ae",
 646+ "\u0398", "\u03b8", "\u0399", "\u038a", "\u03b9", "\u03af", "\u039a", "\u03ba", "\u039b", "\u03bb",
 647+ "\u039c", "\u03bc", "\u039d", "\u03bd", "\u039e", "\u03be", "\u039f", "\u038c", "\u03bf", "\u03cc",
 648+ "\u03a0", "\u03c0", "\u03a1", "\u03c1", "\u03a3", "\u03c3", "\u03c2", "\u03a4", "\u03c4", "\u03a5",
 649+ "\u038e", "\u03c5", "\u03cd", "\u03a6", "\u03c6", "\u03a7", "\u03c7", "\u03a8", "\u03c8", "\u03a9",
 650+ "\u038f", "\u03c9", "\u03ce"
 651+ ]
 652+ },
 653+ 'cyrillic': {
 654+ 'labelMsg': 'wikieditor-toolbar-characters-page-cyrillic',
 655+ 'layout': 'characters',
 656+ 'characters': [
 657+ "\u0410", "\u0430", "\u04d8", "\u04d9", "\u0411", "\u0431", "\u0412", "\u0432", "\u0413", "\u0433",
 658+ "\u0490", "\u0491", "\u0403", "\u0453", "\u0492", "\u0493", "\u0414", "\u0434", "\u0402", "\u0452",
 659+ "\u0415", "\u0435", "\u0404", "\u0454", "\u0401", "\u0451", "\u0416", "\u0436", "\u0417", "\u0437",
 660+ "\u0405", "\u0455", "\u0418", "\u0438", "\u0406", "\u0456", "\u0407", "\u0457", "\u04c0", "\u0419",
 661+ "\u0439", "\u04e2", "\u04e3", "\u0408", "\u0458", "\u041a", "\u043a", "\u040c", "\u045c", "\u049a",
 662+ "\u049b", "\u041b", "\u043b", "\u0409", "\u0459", "\u041c", "\u043c", "\u041d", "\u043d", "\u040a",
 663+ "\u045a", "\u04a2", "\u04a3", "\u041e", "\u043e", "\u04e8", "\u04e9", "\u041f", "\u043f", "\u0420",
 664+ "\u0440", "\u0421", "\u0441", "\u0422", "\u0442", "\u040b", "\u045b", "\u0423", "\u0443", "\u040e",
 665+ "\u045e", "\u04ee", "\u04ef", "\u04b0", "\u04b1", "\u04ae", "\u04af", "\u0424", "\u0444", "\u0425",
 666+ "\u0445", "\u04b2", "\u04b3", "\u04ba", "\u04bb", "\u0426", "\u0446", "\u0427", "\u0447", "\u04b6",
 667+ "\u04b7", "\u040f", "\u045f", "\u0428", "\u0448", "\u0429", "\u0449", "\u042a", "\u044a", "\u042b",
 668+ "\u044b", "\u042c", "\u044c", "\u042d", "\u044d", "\u042e", "\u044e", "\u042f", "\u044f"
 669+ ]
 670+ },
 671+ 'arabic': {
 672+ 'labelMsg': 'wikieditor-toolbar-characters-page-arabic',
 673+ 'layout': 'characters',
 674+ 'language': 'ar',
 675+ 'direction': 'rtl',
 676+ 'characters': [
 677+ "\u061b", "\u061f", "\u0621", "\u0622", "\u0623", "\u0624", "\u0625", "\u0626", "\u0627", "\u0628",
 678+ "\u0629", "\u062a", "\u062b", "\u062c", "\u062d", "\u062e", "\u062f", "\u0630", "\u0631", "\u0632",
 679+ "\u0633", "\u0634", "\u0635", "\u0636", "\u0637", "\u0638", "\u0639", "\u063a", "\u0641", "\u0642",
 680+ "\u0643", "\u0644", "\u0645", "\u0646", "\u0647", "\u0648", "\u0649", "\u064a", "\u060c", "\u067e",
 681+ "\u0686", "\u0698", "\u06af", "\u06ad"
 682+ ]
 683+ },
 684+ 'hebrew': {
 685+ 'labelMsg': 'wikieditor-toolbar-characters-page-hebrew',
 686+ 'layout': 'characters',
 687+ 'direction': 'rtl',
 688+ 'characters': [
 689+ "\u05d0", "\u05d1", "\u05d2", "\u05d3", "\u05d4", "\u05d5", "\u05d6", "\u05d7", "\u05d8", "\u05d9",
 690+ "\u05db", "\u05da", "\u05dc", "\u05de", "\u05dd", "\u05e0", "\u05df", "\u05e1", "\u05e2", "\u05e4",
 691+ "\u05e3", "\u05e6", "\u05e5", "\u05e7", "\u05e8", "\u05e9", "\u05ea", "\u05f3", "\u05f4", "\u05f0",
 692+ "\u05f1", "\u05f2", "\u05d0", "\u05d3", "\u05d4", "\u05d5", "\u05d6", "\u05d7", "\u05d8", "\u05d9",
 693+ "\u05da", "\u05db", "\u05dc", "\u05dd", "\u05de", "\u05df", "\u05e0", "\u05e1", "\u05e2", "\u05e3",
 694+ "\u05e4", "\u05be", "\u05f3", "\u05f4",
 695+ [ "\u05b0\u25cc", "\u05b0" ], [ "\u05b1\u25cc", "\u05b1" ], [ "\u05b2\u25cc", "\u05b2" ],
 696+ [ "\u05b3\u25cc", "\u05b3" ], [ "\u05b4\u25cc", "\u05b4" ], [ "\u05b5\u25cc", "\u05b5" ],
 697+ [ "\u05b6\u25cc", "\u05b6" ], [ "\u05b7\u25cc", "\u05b7" ], [ "\u05b8\u25cc", "\u05b8" ],
 698+ [ "\u05b9\u25cc", "\u05b9" ], [ "\u05bb\u25cc", "\u05bb" ], [ "\u05bc\u25cc", "\u05bc" ],
 699+ [ "\u05c1\u25cc", "\u05c1" ], [ "\u05c2\u25cc", "\u05c2" ], [ "\u05c7\u25cc", "\u05c7" ],
 700+ [ "\u0591\u25cc", "\u0591" ], [ "\u0592\u25cc", "\u0592" ], [ "\u0593\u25cc", "\u0593" ],
 701+ [ "\u0594\u25cc", "\u0594" ], [ "\u0595\u25cc", "\u0595" ], [ "\u0596\u25cc", "\u0596" ],
 702+ [ "\u0597\u25cc", "\u0597" ], [ "\u0598\u25cc", "\u0598" ], [ "\u0599\u25cc", "\u0599" ],
 703+ [ "\u059a\u25cc", "\u059a" ], [ "\u059b\u25cc", "\u059b" ], [ "\u059c\u25cc", "\u059c" ],
 704+ [ "\u059d\u25cc", "\u059d" ], [ "\u059e\u25cc", "\u059e" ], [ "\u059f\u25cc", "\u059f" ],
 705+ [ "\u05a0\u25cc", "\u05a0" ], [ "\u05a1\u25cc", "\u05a1" ], [ "\u05a2\u25cc", "\u05a2" ],
 706+ [ "\u05a3\u25cc", "\u05a3" ], [ "\u05a4\u25cc", "\u05a4" ], [ "\u05a5\u25cc", "\u05a5" ],
 707+ [ "\u05a6\u25cc", "\u05a6" ], [ "\u05a7\u25cc", "\u05a7" ], [ "\u05a8\u25cc", "\u05a8" ],
 708+ [ "\u05a9\u25cc", "\u05a9" ], [ "\u05aa\u25cc", "\u05aa" ], [ "\u05ab\u25cc", "\u05ab" ],
 709+ [ "\u05ac\u25cc", "\u05ac" ], [ "\u05ad\u25cc", "\u05ad" ], [ "\u05ae\u25cc", "\u05ae" ],
 710+ [ "\u05af\u25cc", "\u05af" ], [ "\u05bf\u25cc", "\u05bf" ], [ "\u05c0\u25cc", "\u05c0" ],
 711+ [ "\u05c3\u25cc", "\u05c3" ]
 712+ ]
 713+ },
 714+ 'bangla': {
 715+ 'labelMsg': 'wikieditor-toolbar-characters-page-bangla',
 716+ 'language': 'bn',
 717+ 'layout': 'characters',
 718+ 'characters': [
 719+ "\u0985", "\u0986", "\u0987", "\u0988", "\u0989", "\u098a", "\u098b", "\u098f", "\u0990", "\u0993",
 720+ "\u0994", "\u09be", "\u09bf", "\u09c0", "\u09c1", "\u09c2", "\u09c3", "\u09c7", "\u09c8", "\u09cb",
 721+ "\u09cc", "\u0995", "\u0996", "\u0997", "\u0998", "\u0999", "\u099a", "\u099b", "\u099c", "\u099d",
 722+ "\u099e", "\u099f", "\u09a0", "\u09a1", "\u09a2", "\u09a3", "\u09a4", "\u09a5", "\u09a6", "\u09a7",
 723+ "\u09a8", "\u09aa", "\u09ab", "\u09ac", "\u09ad", "\u09ae", "\u09af", "\u09b0", "\u09b2", "\u09b6",
 724+ "\u09b7", "\u09b8", "\u09b9", "\u09a1\u09bc", "\u09a2\u09bc", "\u09af\u09bc", "\u09ce", "\u0982",
 725+ "\u0983", "\u0981", "\u09cd", "\u09e7", "\u09e8", "\u09e9", "\u09ea", "\u09eb", "\u09ec", "\u09ed",
 726+ "\u09ee", "\u09ef", "\u09e6"
 727+ ]
 728+ },
 729+ 'telugu': {
 730+ 'labelMsg': 'wikieditor-toolbar-characters-page-telugu',
 731+ 'language': 'te',
 732+ 'layout': 'characters',
 733+ 'characters': [
 734+ "\u0c01", "\u0c02", "\u0c03", "\u0c05", "\u0c06", "\u0c07", "\u0c08", "\u0c09", "\u0c0a", "\u0c0b",
 735+ "\u0c60", "\u0c0c", "\u0c61", "\u0c0e", "\u0c0f", "\u0c10", "\u0c12", "\u0c13", "\u0c14", "\u0c15",
 736+ "\u0c16", "\u0c17", "\u0c18", "\u0c19", "\u0c1a", "\u0c1b", "\u0c1c", "\u0c1d", "\u0c1e", "\u0c1f",
 737+ "\u0c20", "\u0c21", "\u0c22", "\u0c23", "\u0c24", "\u0c25", "\u0c26", "\u0c27", "\u0c28", "\u0c2a",
 738+ "\u0c2b", "\u0c2c", "\u0c2d", "\u0c2e", "\u0c2f", "\u0c30", "\u0c31", "\u0c32", "\u0c33", "\u0c35",
 739+ "\u0c36", "\u0c37", "\u0c38", "\u0c39", "\u0c3e", "\u0c3f", "\u0c40", "\u0c41", "\u0c42", "\u0c43",
 740+ "\u0c44", "\u0c46", "\u0c47", "\u0c48", "\u0c4a", "\u0c4b", "\u0c4c", "\u0c4d", "\u0c62", "\u0c63",
 741+ "\u0c58", "\u0c59", "\u0c66", "\u0c67", "\u0c68", "\u0c69", "\u0c6a", "\u0c6b", "\u0c6c", "\u0c6d",
 742+ "\u0c6e", "\u0c6f", "\u0c3d", "\u0c78", "\u0c79", "\u0c7a", "\u0c7b", "\u0c7c", "\u0c7d", "\u0c7e",
 743+ "\u0c7f"
 744+ ]
 745+ },
 746+ 'sinhala': {
 747+ 'labelMsg': 'wikieditor-toolbar-characters-page-sinhala',
 748+ 'language': 'si',
 749+ 'layout': 'characters',
 750+ 'characters': [
 751+ "\u0d85", "\u0d86", "\u0d87", "\u0d88", "\u0d89", "\u0d8a", "\u0d8b", "\u0d8c", "\u0d8d", "\u0d8e",
 752+ "\u0d8f", "\u0d90", "\u0d91", "\u0d92", "\u0d93", "\u0d94", "\u0d95", "\u0d96", "\u0d9a", "\u0d9b",
 753+ "\u0d9c", "\u0d9d", "\u0d9e", "\u0d9f", "\u0da0", "\u0da1", "\u0da2", "\u0da3", "\u0da4", "\u0da5",
 754+ "\u0da6", "\u0da7", "\u0da8", "\u0da9", "\u0daa", "\u0dab", "\u0dac", "\u0dad", "\u0dae", "\u0daf",
 755+ "\u0db0", "\u0db1", "\u0db3", "\u0db4", "\u0db5", "\u0db6", "\u0db7", "\u0db8", "\u0db9", "\u0dba",
 756+ "\u0dbb", "\u0dbd", "\u0dc0", "\u0dc1", "\u0dc2", "\u0dc3", "\u0dc4", "\u0dc5", "\u0dc6",
 757+ [ "\u25cc\u0dcf", "\u0dcf" ], [ "\u25cc\u0dd0", "\u0dd0" ], [ "\u25cc\u0dd1", "\u0dd1" ],
 758+ [ "\u25cc\u0dd2", "\u0dd2" ], [ "\u25cc\u0dd3", "\u0dd3" ], [ "\u25cc\u0dd4", "\u0dd4" ],
 759+ [ "\u25cc\u0dd6", "\u0dd6" ], [ "\u25cc\u0dd8", "\u0dd8" ], [ "\u25cc\u0df2", "\u0df2" ],
 760+ [ "\u25cc\u0ddf", "\u0ddf" ], [ "\u25cc\u0df3", "\u0df3" ], [ "\u25cc\u0dd9", "\u0dd9" ],
 761+ [ "\u25cc\u0dda", "\u0dda" ], [ "\u25cc\u0ddc", "\u0ddc" ], [ "\u25cc\u0ddd", "\u0ddd" ],
 762+ [ "\u25cc\u0dde", "\u0dde" ], [ "\u25cc\u0dca", "\u0dca" ]
 763+ ]
 764+ },
 765+ 'gujarati': {
 766+ 'labelMsg': 'wikieditor-toolbar-characters-page-gujarati',
 767+ 'language': 'gu',
 768+ 'layout': 'characters',
 769+ 'characters': [
 770+ "\u0ad0", "\u0a85", "\u0a86", "\u0a87", "\u0a88", "\u0a89", "\u0a8a", "\u0a8b", "\u0ae0", "\u0a8c",
 771+ "\u0ae1", "\u0a8d", "\u0a8f", "\u0a90", "\u0a91", "\u0a93", "\u0a94", "\u0a95", "\u0a96", "\u0a97",
 772+ "\u0a98", "\u0a99", "\u0a9a", "\u0a9b", "\u0a9c", "\u0a9d", "\u0a9e", "\u0a9f", "\u0aa0", "\u0aa1",
 773+ "\u0aa2", "\u0aa3", "\u0aa4", "\u0aa5", "\u0aa6", "\u0aa7", "\u0aa8", "\u0aaa", "\u0aab", "\u0aac",
 774+ "\u0aad", "\u0aae", "\u0aaf", "\u0ab0", "\u0ab2", "\u0ab5", "\u0ab6", "\u0ab7", "\u0ab8", "\u0ab9",
 775+ "\u0ab3", "\u0abd", [ "\u25cc\u0abe", "\u0abe" ], [ "\u25cc\u0abf", "\u0abf" ],
 776+ [ "\u25cc\u0ac0", "\u0ac0" ], [ "\u25cc\u0ac1", "\u0ac1" ], [ "\u25cc\u0ac2", "\u0ac2" ],
 777+ [ "\u25cc\u0ac3", "\u0ac3" ], [ "\u25cc\u0ac4", "\u0ac4" ], [ "\u25cc\u0ae2", "\u0ae2" ],
 778+ [ "\u25cc\u0ae3", "\u0ae3" ], [ "\u25cc\u0ac5", "\u0ac5" ], [ "\u25cc\u0ac7", "\u0ac7" ],
 779+ [ "\u25cc\u0ac8", "\u0ac8" ], [ "\u25cc\u0ac9", "\u0ac9" ], [ "\u25cc\u0acb", "\u0acb" ],
 780+ [ "\u25cc\u0acc", "\u0acc" ], [ "\u25cc\u0acd", "\u0acd" ]
 781+ ]
 782+ },
 783+ 'thai': {
 784+ 'labelMsg': 'wikieditor-toolbar-characters-page-thai',
 785+ 'language': 'th',
 786+ 'layout': 'characters',
 787+ 'characters': [
 788+ "\u0e01", "\u0e02", "\u0e03", "\u0e04", "\u0e05", "\u0e06", "\u0e07", "\u0e08", "\u0e09", "\u0e0a",
 789+ "\u0e0b", "\u0e0c", "\u0e0d", "\u0e0e", "\u0e0f", "\u0e10", "\u0e11", "\u0e12", "\u0e13", "\u0e14",
 790+ "\u0e15", "\u0e16", "\u0e17", "\u0e18", "\u0e19", "\u0e1a", "\u0e1b", "\u0e1c", "\u0e1d", "\u0e1e",
 791+ "\u0e1f", "\u0e20", "\u0e21", "\u0e22", "\u0e23", "\u0e24", "\u0e25", "\u0e26", "\u0e27", "\u0e28",
 792+ "\u0e29", "\u0e2a", "\u0e2b", "\u0e2c", "\u0e2d", "\u0e2e", "\u0e30", "\u0e31", "\u0e32", "\u0e45",
 793+ "\u0e33", "\u0e34", "\u0e35", "\u0e36", "\u0e37", "\u0e38", "\u0e39", "\u0e40", "\u0e41", "\u0e42",
 794+ "\u0e43", "\u0e44", "\u0e47", "\u0e48", "\u0e49", "\u0e4a", "\u0e4b", "\u0e4c", "\u0e4d", "\u0e3a",
 795+ "\u0e4e", "\u0e50", "\u0e51", "\u0e52", "\u0e53", "\u0e54", "\u0e55", "\u0e56", "\u0e57", "\u0e58",
 796+ "\u0e59", "\u0e3f", "\u0e46", "\u0e2f", "\u0e5a", "\u0e4f", "\u0e5b"
 797+ ]
 798+ },
 799+ 'lao': {
 800+ 'labelMsg': 'wikieditor-toolbar-characters-page-lao',
 801+ 'language': 'lo',
 802+ 'layout': 'characters',
 803+ 'characters': [
 804+ "\u0e81", "\u0e82", "\u0e84", "\u0e87", "\u0e88", "\u0eaa", "\u0e8a", "\u0e8d", "\u0e94", "\u0e95",
 805+ "\u0e96", "\u0e97", "\u0e99", "\u0e9a", "\u0e9b", "\u0e9c", "\u0e9d", "\u0e9e", "\u0e9f", "\u0ea1",
 806+ "\u0ea2", "\u0ea5", "\u0ea7", "\u0eab", "\u0ead", "\u0eae", "\u0ea3", "\u0edc", "\u0edd", "\u0ebc",
 807+ "\u0ebd", "\u0eb0", "\u0eb1", "\u0eb2", "\u0eb3", "\u0eb4", "\u0eb5", "\u0eb6", "\u0eb7", "\u0eb8",
 808+ "\u0eb9", "\u0ebb", "\u0ec0", "\u0ec1", "\u0ec2", "\u0ec3", "\u0ec4", "\u0ec8", "\u0ec9", "\u0eca",
 809+ "\u0ecb", "\u0ecc", "\u0ecd", "\u0ed0", "\u0ed1", "\u0ed2", "\u0ed3", "\u0ed4", "\u0ed5", "\u0ed6",
 810+ "\u0ed7", "\u0ed8", "\u0ed9", "\u20ad", "\u0ec6", "\u0eaf"
 811+ ]
 812+ },
 813+ 'khmer': {
 814+ 'labelMsg': 'wikieditor-toolbar-characters-page-khmer',
 815+ 'language': 'km',
 816+ 'layout': 'characters',
 817+ 'characters': [
 818+ "\u1780", "\u1781", "\u1782", "\u1783", "\u1784", "\u1785", "\u1786", "\u1787", "\u1788", "\u1789",
 819+ "\u178a", "\u178b", "\u178c", "\u178d", "\u178e", "\u178f", "\u1790", "\u1791", "\u1792", "\u1793",
 820+ "\u1794", "\u1795", "\u1796", "\u1797", "\u1798", "\u1799", "\u179a", "\u179b", "\u179c", "\u179f",
 821+ "\u17a0", "\u17a1", "\u17a2", "\u17a3", "\u17a4", "\u17a5", "\u17a6", "\u17a7", "\u17a8", "\u17a9",
 822+ "\u17aa", "\u17ab", "\u17ac", "\u17ad", "\u17ae", "\u17af", "\u17b0", "\u17b1", "\u17b2", "\u17b3",
 823+ "\u17d2", "\u17b4", "\u17b5", "\u17b6", "\u17b7", "\u17b8", "\u17b9", "\u17ba", "\u17bb", "\u17bc",
 824+ "\u17bd", "\u17be", "\u17bf", "\u17c0", "\u17c1", "\u17c2", "\u17c3", "\u17c4", "\u17c5", "\u17c6",
 825+ "\u17c7", "\u17c8", "\u17c9", "\u17ca", "\u17cb", "\u17cc", "\u17cd", "\u17ce", "\u17cf", "\u17d0",
 826+ "\u17d1", "\u17d3", "\u17dd", "\u17dc", "\u17e0", "\u17e1", "\u17e2", "\u17e3", "\u17e4", "\u17e5",
 827+ "\u17e6", "\u17e7", "\u17e8", "\u17e9", "\u17db", "\u17d4", "\u17d5", "\u17d6", "\u17d7", "\u17d8",
 828+ "\u17d9", "\u17da", "\u17f0", "\u17f1", "\u17f2", "\u17f3", "\u17f4", "\u17f5", "\u17f6", "\u17f7",
 829+ "\u17f8", "\u17f9", "\u19e0", "\u19e1", "\u19e2", "\u19e3", "\u19e4", "\u19e5", "\u19e6", "\u19e7",
 830+ "\u19e8", "\u19e9", "\u19ea", "\u19eb", "\u19ec", "\u19ed", "\u19ee", "\u19ef", "\u19f0", "\u19f1",
 831+ "\u19f2", "\u19f3", "\u19f4", "\u19f5", "\u19f6", "\u19f7", "\u19f8", "\u19f9", "\u19fa", "\u19fb",
 832+ "\u19fc", "\u19fd", "\u19fe", "\u19ff"
 833+ ]
 834+ }
 835+ }
 836+ },
 837+ 'help': {
 838+ labelMsg: 'wikieditor-toolbar-section-help',
 839+ type: 'booklet',
 840+ deferLoad: true,
 841+ pages: {
 842+ 'format': {
 843+ labelMsg: 'wikieditor-toolbar-help-page-format',
 844+ layout: 'table',
 845+ headings: [
 846+ { textMsg: 'wikieditor-toolbar-help-heading-description' },
 847+ { textMsg: 'wikieditor-toolbar-help-heading-syntax' },
 848+ { textMsg: 'wikieditor-toolbar-help-heading-result' }
 849+ ],
 850+ rows: [
 851+ {
 852+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-italic-description' },
 853+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-italic-syntax' },
 854+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-italic-result' }
 855+ },
 856+ {
 857+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-bold-description' },
 858+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-bold-syntax' },
 859+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-bold-result' }
 860+ },
 861+ {
 862+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-bolditalic-description' },
 863+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-bolditalic-syntax' },
 864+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-bolditalic-result' }
 865+ }
 866+ ]
 867+ },
 868+ 'link': {
 869+ labelMsg: 'wikieditor-toolbar-help-page-link',
 870+ layout: 'table',
 871+ headings: [
 872+ { textMsg: 'wikieditor-toolbar-help-heading-description' },
 873+ { textMsg: 'wikieditor-toolbar-help-heading-syntax' },
 874+ { textMsg: 'wikieditor-toolbar-help-heading-result' }
 875+ ],
 876+ rows: [
 877+ {
 878+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-ilink-description' },
 879+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-ilink-syntax' },
 880+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-ilink-result' }
 881+ },
 882+ {
 883+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-xlink-description' },
 884+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-xlink-syntax' },
 885+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-xlink-result' }
 886+ }
 887+ ]
 888+ },
 889+ 'heading': {
 890+ labelMsg: 'wikieditor-toolbar-help-page-heading',
 891+ layout: 'table',
 892+ headings: [
 893+ { textMsg: 'wikieditor-toolbar-help-heading-description' },
 894+ { textMsg: 'wikieditor-toolbar-help-heading-syntax' },
 895+ { textMsg: 'wikieditor-toolbar-help-heading-result' }
 896+ ],
 897+ rows: [
 898+ {
 899+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-heading2-description' },
 900+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-heading2-syntax' },
 901+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-heading2-result' }
 902+ },
 903+ {
 904+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-heading3-description' },
 905+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-heading3-syntax' },
 906+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-heading3-result' }
 907+ },
 908+ {
 909+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-heading4-description' },
 910+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-heading4-syntax' },
 911+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-heading4-result' }
 912+ },
 913+ {
 914+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-heading5-description' },
 915+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-heading5-syntax' },
 916+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-heading5-result' }
 917+ }
 918+ ]
 919+ },
 920+ 'list': {
 921+ labelMsg: 'wikieditor-toolbar-help-page-list',
 922+ layout: 'table',
 923+ headings: [
 924+ { textMsg: 'wikieditor-toolbar-help-heading-description' },
 925+ { textMsg: 'wikieditor-toolbar-help-heading-syntax' },
 926+ { textMsg: 'wikieditor-toolbar-help-heading-result' }
 927+ ],
 928+ rows: [
 929+ {
 930+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-ulist-description' },
 931+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-ulist-syntax' },
 932+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-ulist-result' }
 933+ },
 934+ {
 935+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-olist-description' },
 936+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-olist-syntax' },
 937+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-olist-result' }
 938+ }
 939+ ]
 940+ },
 941+ 'file': {
 942+ labelMsg: 'wikieditor-toolbar-help-page-file',
 943+ layout: 'table',
 944+ headings: [
 945+ { textMsg: 'wikieditor-toolbar-help-heading-description' },
 946+ { textMsg: 'wikieditor-toolbar-help-heading-syntax' },
 947+ { textMsg: 'wikieditor-toolbar-help-heading-result' }
 948+ ],
 949+ rows: [
 950+ {
 951+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-file-description' },
 952+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-file-syntax' },
 953+ 'result': { htmlMsg: [ 'wikieditor-toolbar-help-content-file-result', stylepath ] }
 954+ }
 955+ ]
 956+ },
 957+ 'reference': {
 958+ labelMsg: 'wikieditor-toolbar-help-page-reference',
 959+ layout: 'table',
 960+ headings: [
 961+ { textMsg: 'wikieditor-toolbar-help-heading-description' },
 962+ { textMsg: 'wikieditor-toolbar-help-heading-syntax' },
 963+ { textMsg: 'wikieditor-toolbar-help-heading-result' }
 964+ ],
 965+ rows: [
 966+ {
 967+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-reference-description' },
 968+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-reference-syntax' },
 969+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-reference-result' }
 970+ },
 971+ {
 972+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-rereference-description' },
 973+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-rereference-syntax' },
 974+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-rereference-result' }
 975+ },
 976+ {
 977+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-showreferences-description' },
 978+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-showreferences-syntax' },
 979+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-showreferences-result' }
 980+ }
 981+ ]
 982+ },
 983+ 'discussion': {
 984+ labelMsg: 'wikieditor-toolbar-help-page-discussion',
 985+ layout: 'table',
 986+ headings: [
 987+ { textMsg: 'wikieditor-toolbar-help-heading-description' },
 988+ { textMsg: 'wikieditor-toolbar-help-heading-syntax' },
 989+ { textMsg: 'wikieditor-toolbar-help-heading-result' }
 990+ ],
 991+ rows: [
 992+ {
 993+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-signaturetimestamp-description' },
 994+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-signaturetimestamp-syntax' },
 995+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-signaturetimestamp-result' }
 996+ },
 997+ {
 998+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-signature-description' },
 999+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-signature-syntax' },
 1000+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-signature-result' }
 1001+ },
 1002+ {
 1003+ 'description': { htmlMsg: 'wikieditor-toolbar-help-content-indent-description' },
 1004+ 'syntax': { htmlMsg: 'wikieditor-toolbar-help-content-indent-syntax' },
 1005+ 'result': { htmlMsg: 'wikieditor-toolbar-help-content-indent-result' }
 1006+ }
 1007+ ]
 1008+ }
 1009+ }
 1010+ }
 1011+},
 1012+'dialogs': {
 1013+ 'insert-link': {
 1014+ // For now, apply the old browser and iframe requirements to the link and table dialogs as well
 1015+ // This'll be removed once these dialogs are confirmed stable without the iframe and/or in more browsers
 1016+ /*
 1017+ 'browsers': {
 1018+ // Left-to-right languages
 1019+ 'ltr': {
 1020+ 'msie': [['>=', 7]],
 1021+ 'firefox': [['>=', 3]],
 1022+ 'opera': [['>=', 10]],
 1023+ 'safari': [['>=', 4]],
 1024+ 'chrome': [['>=', 4]]
 1025+ },
 1026+ // Right-to-left languages
 1027+ 'rtl': {
 1028+ 'msie': [['>=', 8]],
 1029+ 'firefox': [['>=', 3]],
 1030+ 'opera': [['>=', 10]],
 1031+ 'safari': [['>=', 4]],
 1032+ 'chrome': [['>=', 4]]
 1033+ }
 1034+ },
 1035+ 'req': [ 'iframe' ],
 1036+ */
 1037+ filters: [ '#wpTextbox1.toolbar-dialogs' ],
 1038+ titleMsg: 'wikieditor-toolbar-tool-link-title',
 1039+ id: 'wikieditor-toolbar-link-dialog',
 1040+ html: '\
 1041+ <fieldset>\
 1042+ <div class="wikieditor-toolbar-field-wrapper">\
 1043+ <label for="wikieditor-toolbar-link-int-target" rel="wikieditor-toolbar-tool-link-int-target" id="wikieditor-toolbar-tool-link-int-target-label"></label>\
 1044+ <div id="wikieditor-toolbar-link-int-target-status"></div>\
 1045+ <input type="text" id="wikieditor-toolbar-link-int-target" />\
 1046+ </div>\
 1047+ <div class="wikieditor-toolbar-field-wrapper">\
 1048+ <label for="wikieditor-toolbar-link-int-text" rel="wikieditor-toolbar-tool-link-int-text"></label>\
 1049+ <input type="text" id="wikieditor-toolbar-link-int-text" />\
 1050+ </div>\
 1051+ <div class="wikieditor-toolbar-field-wrapper">\
 1052+ <div class="wikieditor-toolbar-floated-field-wrapper">\
 1053+ <input type="radio" id="wikieditor-toolbar-link-type-int" name="wikieditor-toolbar-link-type" selected />\
 1054+ <label for="wikieditor-toolbar-link-type-int" rel="wikieditor-toolbar-tool-link-int"></label>\
 1055+ </div>\
 1056+ <div class="wikieditor-toolbar-floated-field-wrapper">\
 1057+ <input type="radio" id="wikieditor-toolbar-link-type-ext" name="wikieditor-toolbar-link-type" />\
 1058+ <label for="wikieditor-toolbar-link-type-ext" rel="wikieditor-toolbar-tool-link-ext"></label>\
 1059+ </div>\
 1060+ </div>\
 1061+ </fieldset>',
 1062+ init: function() {
 1063+ function isExternalLink( s ) {
 1064+ // The following things are considered to be external links:
 1065+ // * Starts a URL protocol
 1066+ // * Starts with www.
 1067+ // All of these are potentially valid titles, and the latter two
 1068+ // categories match about 6300 titles in enwiki's ns0. Out of 6.9M
 1069+ // titles, that's 0.09%
 1070+ if ( typeof arguments.callee.regex == 'undefined' ) {
 1071+ // Cache the regex
 1072+ arguments.callee.regex =
 1073+ new RegExp( "^(" + wgUrlProtocols + "|www\\.)", 'i');
 1074+ }
 1075+ return s.match( arguments.callee.regex );
 1076+ }
 1077+ // Updates the status indicator above the target link
 1078+ function updateWidget( status ) {
 1079+ $j( '#wikieditor-toolbar-link-int-target-status' ).children().hide();
 1080+ $j( '#wikieditor-toolbar-link-int-target' ).parent()
 1081+ .removeClass( 'status-invalid status-external status-notexists status-exists status-loading' );
 1082+ if ( status ) {
 1083+ $j( '#wikieditor-toolbar-link-int-target-status-' + status ).show();
 1084+ $j( '#wikieditor-toolbar-link-int-target' ).parent().addClass( 'status-' + status );
 1085+ }
 1086+ if ( status == 'invalid' ) {
 1087+ $j( '.ui-dialog:visible .ui-dialog-buttonpane button:first' )
 1088+ .attr( 'disabled', true )
 1089+ .addClass( 'disabled' );
 1090+ } else {
 1091+ $j( '.ui-dialog:visible .ui-dialog-buttonpane button:first' )
 1092+ .removeAttr('disabled')
 1093+ .removeClass('disabled');
 1094+ }
 1095+ }
 1096+ // Updates the UI to show if the page title being inputed by the user exists or not
 1097+ // accepts parameter internal for bypassing external link detection
 1098+ function updateExistence( internal ) {
 1099+ // ensure the internal parameter is a boolean
 1100+ if ( internal != true ) internal = false;
 1101+ // Abort previous request
 1102+ var request = $j( '#wikieditor-toolbar-link-int-target-status' ).data( 'request' );
 1103+ if ( request ) {
 1104+ request.abort();
 1105+ }
 1106+ var target = $j( '#wikieditor-toolbar-link-int-target' ).val();
 1107+ var cache = $j( '#wikieditor-toolbar-link-int-target-status' ).data( 'existencecache' );
 1108+ if ( cache[target] ) {
 1109+ updateWidget( cache[target] );
 1110+ return;
 1111+ }
 1112+ if ( target.replace( /^\s+$/,'' ) == '' ) {
 1113+ // Hide the widget when the textbox is empty
 1114+ updateWidget( false );
 1115+ return;
 1116+ }
 1117+ // If the forced internal paremter was not true, check if the target is an external link
 1118+ if ( !internal && isExternalLink( target ) ) {
 1119+ updateWidget( 'external' );
 1120+ return;
 1121+ }
 1122+ if ( target.indexOf( '|' ) != -1 ) {
 1123+ // Title contains | , which means it's invalid
 1124+ // but confuses the API. Show invalid and bypass API
 1125+ updateWidget( 'invalid' );
 1126+ return;
 1127+ }
 1128+ // Show loading spinner while waiting for the API to respond
 1129+ updateWidget( 'loading' );
 1130+ // Call the API to check page status, saving the request object so it can be aborted if necessary
 1131+ $j( '#wikieditor-toolbar-link-int-target-status' ).data(
 1132+ 'request',
 1133+ $j.ajax( {
 1134+ url: wgScriptPath + '/api.php',
 1135+ dataType: 'json',
 1136+ data: {
 1137+ 'action': 'query',
 1138+ 'indexpageids': '',
 1139+ 'titles': target,
 1140+ 'converttitles': '',
 1141+ 'format': 'json'
 1142+ },
 1143+ success: function( data ) {
 1144+ var status;
 1145+ if ( !data || typeof data.query == 'undefined' ) {
 1146+ // This happens in some weird cases
 1147+ status = false;
 1148+ } else {
 1149+ var page = data.query.pages[data.query.pageids[0]];
 1150+ status = 'exists';
 1151+ if ( typeof page.missing != 'undefined' )
 1152+ status = 'notexists';
 1153+ else if ( typeof page.invalid != 'undefined' )
 1154+ status = 'invalid';
 1155+ }
 1156+ // Cache the status of the link target if the force internal parameter was not passed
 1157+ if ( !internal ) cache[target] = status;
 1158+ updateWidget( status );
 1159+ }
 1160+ } )
 1161+ );
 1162+ }
 1163+ $j( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' ).click( function() {
 1164+ if( $j( '#wikieditor-toolbar-link-type-ext' ).is( ':checked' ) ) {
 1165+ // Abort previous request
 1166+ var request = $j( '#wikieditor-toolbar-link-int-target-status' ).data( 'request' );
 1167+ if ( request ) {
 1168+ request.abort();
 1169+ }
 1170+ updateWidget( 'external' );
 1171+ }
 1172+ if( $j( '#wikieditor-toolbar-link-type-int' ).is( ':checked' ) )
 1173+ updateExistence( true );
 1174+ });
 1175+ // Set labels of tabs based on rel values
 1176+ var u = mw.usability;
 1177+ $j(this).find( '[rel]' ).each( function() {
 1178+ $j(this).text( u.getMsg( $j(this).attr( 'rel' ) ) );
 1179+ });
 1180+ // Set tabindexes on form fields
 1181+ $j.wikiEditor.modules.dialogs.fn.setTabindexes( $j(this).find( 'input' ).not( '[tabindex]' ) );
 1182+ // Setup the tooltips in the textboxes
 1183+ $j( '#wikieditor-toolbar-link-int-target' )
 1184+ .data( 'tooltip', u.getMsg( 'wikieditor-toolbar-tool-link-int-target-tooltip' ) );
 1185+ $j( '#wikieditor-toolbar-link-int-text' )
 1186+ .data( 'tooltip', u.getMsg( 'wikieditor-toolbar-tool-link-int-text-tooltip' ) );
 1187+ $j( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' )
 1188+ .each( function() {
 1189+ var tooltip = u.getMsg( $j( this ).attr( 'id' ) + '-tooltip' );
 1190+ if ( $j( this ).val() == '' )
 1191+ $j( this )
 1192+ .addClass( 'wikieditor-toolbar-dialog-hint' )
 1193+ .val( $j( this ).data( 'tooltip' ) )
 1194+ .data( 'tooltip-mode', true );
 1195+ } )
 1196+ .focus( function() {
 1197+ if( $j( this ).val() == $j( this ).data( 'tooltip' ) ) {
 1198+ $j( this )
 1199+ .val( '' )
 1200+ .removeClass( 'wikieditor-toolbar-dialog-hint' )
 1201+ .data( 'tooltip-mode', false );
 1202+ }
 1203+ })
 1204+ .bind( 'change', function() {
 1205+ if ( $j( this ).val() != $j( this ).data( 'tooltip' ) ) {
 1206+ $j( this )
 1207+ .removeClass( 'wikieditor-toolbar-dialog-hint' )
 1208+ .data( 'tooltip-mode', false );
 1209+ }
 1210+ })
 1211+ .bind( 'blur', function() {
 1212+ if ( $j( this ).val() == '' ) {
 1213+ $j( this )
 1214+ .addClass( 'wikieditor-toolbar-dialog-hint' )
 1215+ .val( $j( this ).data( 'tooltip' ) )
 1216+ .data( 'tooltip-mode', true );
 1217+ }
 1218+ });
 1219+
 1220+ // Automatically copy the value of the internal link page title field to the link text field unless the user
 1221+ // has changed the link text field - this is a convenience thing since most link texts are going to be the
 1222+ // the same as the page title
 1223+ // Also change the internal/external radio button accordingly
 1224+ $j( '#wikieditor-toolbar-link-int-target' ).bind( 'change keydown paste cut', function() {
 1225+ // $j(this).val() is the old value, before the keypress
 1226+ // Defer this until $j(this).val() has been updated
 1227+ setTimeout( function() {
 1228+ if ( isExternalLink( $j( '#wikieditor-toolbar-link-int-target' ).val() ) ) {
 1229+ $j( '#wikieditor-toolbar-link-type-ext' ).attr( 'checked', 'checked' );
 1230+ updateWidget( 'external' );
 1231+ } else {
 1232+ $j( '#wikieditor-toolbar-link-type-int' ).attr( 'checked', 'checked' );
 1233+ updateExistence();
 1234+ }
 1235+ if ( $j( '#wikieditor-toolbar-link-int-text' ).data( 'untouched' ) )
 1236+ if ( $j( '#wikieditor-toolbar-link-int-target' ).val() ==
 1237+ $j( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip' ) ) {
 1238+ $j( '#wikieditor-toolbar-link-int-text' )
 1239+ .addClass( 'wikieditor-toolbar-dialog-hint' )
 1240+ .val( $j( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip' ) )
 1241+ .change();
 1242+ } else {
 1243+ $j( '#wikieditor-toolbar-link-int-text' )
 1244+ .val( $j( '#wikieditor-toolbar-link-int-target' ).val() )
 1245+ .change();
 1246+ }
 1247+ }, 0 );
 1248+ });
 1249+ $j( '#wikieditor-toolbar-link-int-text' ).bind( 'change keydown paste cut', function() {
 1250+ var oldVal = $j(this).val();
 1251+ var that = this;
 1252+ setTimeout( function() {
 1253+ if ( $j(that).val() != oldVal )
 1254+ $j(that).data( 'untouched', false );
 1255+ }, 0 );
 1256+ });
 1257+ // Add images to the page existence widget, which will be shown mutually exclusively to communicate if the
 1258+ // page exists, does not exist or the title is invalid (like if it contains a | character)
 1259+ var existsMsg = u.getMsg( 'wikieditor-toolbar-tool-link-int-target-status-exists' );
 1260+ var notexistsMsg = u.getMsg( 'wikieditor-toolbar-tool-link-int-target-status-notexists' );
 1261+ var invalidMsg = u.getMsg( 'wikieditor-toolbar-tool-link-int-target-status-invalid' );
 1262+ var externalMsg = u.getMsg( 'wikieditor-toolbar-tool-link-int-target-status-external' );
 1263+ var loadingMsg = u.getMsg( 'wikieditor-toolbar-tool-link-int-target-status-loading' );
 1264+ $j( '#wikieditor-toolbar-link-int-target-status' )
 1265+ .append( $j( '<div />' )
 1266+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-exists' )
 1267+ .append( existsMsg )
 1268+ )
 1269+ .append( $j( '<div />' )
 1270+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-notexists' )
 1271+ .append( notexistsMsg )
 1272+ )
 1273+ .append( $j( '<div />' )
 1274+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-invalid' )
 1275+ .append( invalidMsg )
 1276+ )
 1277+ .append( $j( '<div />' )
 1278+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-external' )
 1279+ .append( externalMsg )
 1280+ )
 1281+ .append( $j( '<div />' )
 1282+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-loading' )
 1283+ .append( $j( '<img />' ).attr( {
 1284+ 'src': $j.wikiEditor.imgPath + 'dialogs/' + 'loading.gif',
 1285+ 'alt': loadingMsg,
 1286+ 'title': loadingMsg
 1287+ } ) )
 1288+ )
 1289+ .data( 'existencecache', {} )
 1290+ .children().hide();
 1291+
 1292+ $j( '#wikieditor-toolbar-link-int-target' )
 1293+ .bind( 'keyup paste cut', function() {
 1294+ // Cancel the running timer if applicable
 1295+ if ( typeof $j(this).data( 'timerID' ) != 'undefined' ) {
 1296+ clearTimeout( $j(this).data( 'timerID' ) );
 1297+ }
 1298+ // Delay fetch for a while
 1299+ // FIXME: Make 120 configurable elsewhere
 1300+ var timerID = setTimeout( updateExistence, 120 );
 1301+ $j(this).data( 'timerID', timerID );
 1302+ } )
 1303+ .change( function() {
 1304+ // Cancel the running timer if applicable
 1305+ if ( typeof $j(this).data( 'timerID' ) != 'undefined' ) {
 1306+ clearTimeout( $j(this).data( 'timerID' ) );
 1307+ }
 1308+ // Fetch right now
 1309+ updateExistence();
 1310+ } );
 1311+
 1312+ // Title suggestions
 1313+ $j( '#wikieditor-toolbar-link-int-target' ).data( 'suggcache', {} ).suggestions( {
 1314+ fetch: function( query ) {
 1315+ var that = this;
 1316+ var title = $j(this).val();
 1317+
 1318+ if ( isExternalLink( title ) || title.indexOf( '|' ) != -1 || title == '') {
 1319+ $j(this).suggestions( 'suggestions', [] );
 1320+ return;
 1321+ }
 1322+
 1323+ var cache = $j(this).data( 'suggcache' );
 1324+ if ( typeof cache[title] != 'undefined' ) {
 1325+ $j(this).suggestions( 'suggestions', cache[title] );
 1326+ return;
 1327+ }
 1328+
 1329+ var request = $j.ajax( {
 1330+ url: wgScriptPath + '/api.php',
 1331+ data: {
 1332+ 'action': 'opensearch',
 1333+ 'search': title,
 1334+ 'namespace': 0,
 1335+ 'suggest': '',
 1336+ 'format': 'json'
 1337+ },
 1338+ dataType: 'json',
 1339+ success: function( data ) {
 1340+ cache[title] = data[1];
 1341+ $j(that).suggestions( 'suggestions', data[1] );
 1342+ }
 1343+ });
 1344+ $j(this).data( 'request', request );
 1345+ },
 1346+ cancel: function() {
 1347+ var request = $j(this).data( 'request' );
 1348+ if ( request )
 1349+ request.abort();
 1350+ }
 1351+ });
 1352+ },
 1353+ dialog: {
 1354+ width: 500,
 1355+ dialogClass: 'wikiEditor-toolbar-dialog',
 1356+ buttons: {
 1357+ 'wikieditor-toolbar-tool-link-insert': function() {
 1358+ function escapeInternalText( s ) {
 1359+ // FIXME: Should this escape [[ too? Seems to work without that
 1360+ return s.replace( /(]{2,})/g, '<nowiki>$1</nowiki>' );
 1361+ }
 1362+ function escapeExternalTarget( s ) {
 1363+ return s.replace( / /g, '%20' )
 1364+ .replace( /\[/g, '%5B' )
 1365+ .replace( /]/g, '%5D' );
 1366+ }
 1367+ function escapeExternalText( s ) {
 1368+ // FIXME: Should this escape [ too? Seems to work without that
 1369+ return s.replace( /(]+)/g, '<nowiki>$1</nowiki>' );
 1370+ }
 1371+ var insertText = '';
 1372+ var whitespace = $j( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace' );
 1373+ var target = $j( '#wikieditor-toolbar-link-int-target' ).val();
 1374+ var text = $j( '#wikieditor-toolbar-link-int-text' ).val();
 1375+ // check if the tooltips were passed as target or text
 1376+ if ( $j( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip-mode' ) )
 1377+ target = "";
 1378+ if ( $j( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip-mode' ) )
 1379+ text = "";
 1380+ var u = mw.usability;
 1381+ if ( target == '' ) {
 1382+ alert( u.getMsg( 'wikieditor-toolbar-tool-link-empty' ) );
 1383+ return;
 1384+ }
 1385+ if ( $j.trim( text ) == '' ) {
 1386+ // [[Foo| ]] creates an invisible link
 1387+ // Instead, generate [[Foo|]]
 1388+ text = '';
 1389+ }
 1390+ if ( $j( '#wikieditor-toolbar-link-type-int' ).is( ':checked' ) ) {
 1391+ // FIXME: Exactly how fragile is this?
 1392+ if ( $j( '#wikieditor-toolbar-link-int-target-status-invalid' ).is( ':visible' ) ) {
 1393+ // Refuse to add links to invalid titles
 1394+ alert( u.getMsg( 'wikieditor-toolbar-tool-link-int-invalid' ) );
 1395+ return;
 1396+ }
 1397+
 1398+ if ( target == text || !text.length )
 1399+ insertText = '[[' + target + ']]';
 1400+ else
 1401+ insertText = '[[' + target + '|' + escapeInternalText( text ) + ']]';
 1402+ } else {
 1403+ // Prepend http:// if there is no protocol
 1404+ if ( !target.match( /^[a-z]+:\/\/./ ) )
 1405+ target = 'http://' + target;
 1406+
 1407+ // Detect if this is really an internal link in disguise
 1408+ var match = target.match( $j(this).data( 'articlePathRegex' ) );
 1409+ if ( match && !$j(this).data( 'ignoreLooksInternal' ) ) {
 1410+ var buttons = { };
 1411+ var that = this;
 1412+ buttons[ u.getMsg( 'wikieditor-toolbar-tool-link-lookslikeinternal-int' ) ] = function() {
 1413+ $j( '#wikieditor-toolbar-link-int-target' ).val( match[1] ).change();
 1414+ $j(this).dialog( 'close' );
 1415+ };
 1416+ buttons[ u.getMsg( 'wikieditor-toolbar-tool-link-lookslikeinternal-ext' ) ] = function() {
 1417+ $j(that).data( 'ignoreLooksInternal', true );
 1418+ $j(that).closest( '.ui-dialog' ).find( 'button:first' ).click();
 1419+ $j(that).data( 'ignoreLooksInternal', false );
 1420+ $j(this).dialog( 'close' );
 1421+ };
 1422+ $j.wikiEditor.modules.dialogs.quickDialog(
 1423+ u.getMsg( 'wikieditor-toolbar-tool-link-lookslikeinternal', match[1] ),
 1424+ { buttons: buttons }
 1425+ );
 1426+ return;
 1427+ }
 1428+
 1429+ var escTarget = escapeExternalTarget( target );
 1430+ var escText = escapeExternalText( text );
 1431+
 1432+ if ( escTarget == escText )
 1433+ insertText = escTarget;
 1434+ else if ( text == '' )
 1435+ insertText = '[' + escTarget + ']';
 1436+ else
 1437+ insertText = '[' + escTarget + ' ' + escText + ']';
 1438+ }
 1439+ // Preserve whitespace in selection when replacing
 1440+ if ( whitespace ) insertText = whitespace[0] + insertText + whitespace[1];
 1441+ $j(this).dialog( 'close' );
 1442+ $j.wikiEditor.modules.toolbar.fn.doAction( $j(this).data( 'context' ), {
 1443+ type: 'replace',
 1444+ options: {
 1445+ pre: insertText
 1446+ }
 1447+ }, $j(this) );
 1448+
 1449+ // Blank form
 1450+ $j( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' ).val( '' );
 1451+ $j( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' ).attr( 'checked', '' );
 1452+ },
 1453+ 'wikieditor-toolbar-tool-link-cancel': function() {
 1454+ // Clear any saved selection state
 1455+ var context = $j(this).data( 'context' );
 1456+ context.fn.restoreStuffForIE();
 1457+ $j(this).dialog( 'close' );
 1458+ }
 1459+ },
 1460+ open: function() {
 1461+ // Cache the articlepath regex
 1462+ $j(this).data( 'articlePathRegex', new RegExp(
 1463+ '^' + RegExp.escape( wgServer + wgArticlePath )
 1464+ .replace( /\\\$1/g, '(.*)' ) + '$'
 1465+ ) );
 1466+ // Pre-fill the text fields based on the current selection
 1467+ var context = $j(this).data( 'context' );
 1468+ // Restore and immediately save selection state, needed for inserting stuff later
 1469+ context.fn.restoreStuffForIE();
 1470+ context.fn.saveStuffForIE();
 1471+ var selection = context.$textarea.textSelection( 'getSelection' );
 1472+ $j( '#wikieditor-toolbar-link-int-target' ).focus();
 1473+ // Trigger the change event, so the link status indicator is up to date
 1474+ $j( '#wikieditor-toolbar-link-int-target' ).change();
 1475+ $j( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ '', '' ] );
 1476+ if ( selection != '' ) {
 1477+ var target, text, type;
 1478+ var matches;
 1479+ if ( ( matches = selection.match( /^(\s*)\[\[([^\]\|]+)(\|([^\]\|]*))?\]\](\s*)$/ ) ) ) {
 1480+ // [[foo|bar]] or [[foo]]
 1481+ target = matches[2];
 1482+ text = ( matches[4] ? matches[4] : matches[2] );
 1483+ type = 'int';
 1484+ // Preserve whitespace when replacing
 1485+ $j( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[1], matches[5] ] );
 1486+ } else if ( ( matches = selection.match( /^(\s*)\[([^\] ]+)( ([^\]]+))?\](\s*)$/ ) ) ) {
 1487+ // [http://www.example.com foo] or [http://www.example.com]
 1488+ target = matches[2];
 1489+ text = ( matches[4] ? matches[4] : '' );
 1490+ type = 'ext';
 1491+ // Preserve whitespace when replacing
 1492+ $j( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[1], matches[5] ] );
 1493+ } else {
 1494+ // Trim any leading and trailing whitespace from the selection,
 1495+ // but preserve it when replacing
 1496+ target = text = $j.trim( selection );
 1497+ if ( target.length < selection.length ) {
 1498+ $j( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [
 1499+ selection.substr( 0, selection.indexOf( target.charAt( 0 ) ) ),
 1500+ selection.substr(
 1501+ selection.lastIndexOf( target.charAt( target.length - 1 ) ) + 1
 1502+ ) ]
 1503+ );
 1504+ }
 1505+ }
 1506+
 1507+ // Change the value by calling val() doesn't trigger the change event, so let's do that ourselves
 1508+ if ( typeof text != 'undefined' )
 1509+ $j( '#wikieditor-toolbar-link-int-text' ).val( text ).change();
 1510+ if ( typeof target != 'undefined' )
 1511+ $j( '#wikieditor-toolbar-link-int-target' ).val( target ).change();
 1512+ if ( typeof type != 'undefined' )
 1513+ $j( '#wikieditor-toolbar-link-' + type ).attr( 'checked', 'checked' );
 1514+ }
 1515+ $j( '#wikieditor-toolbar-link-int-text' ).data( 'untouched',
 1516+ $j( '#wikieditor-toolbar-link-int-text' ).val() ==
 1517+ $j( '#wikieditor-toolbar-link-int-target' ).val() ||
 1518+ $j( '#wikieditor-toolbar-link-int-text' ).hasClass( 'wikieditor-toolbar-dialog-hint' )
 1519+ );
 1520+ $j( '#wikieditor-toolbar-link-int-target' ).suggestions();
 1521+
 1522+ //don't overwrite user's text
 1523+ if( selection != '' ){
 1524+ $j( '#wikieditor-toolbar-link-int-text' ).data( 'untouched', false );
 1525+ }
 1526+
 1527+ $j( '#wikieditor-toolbar-link-int-text, #wikiedit-toolbar-link-int-target' )
 1528+ .each( function() {
 1529+ if ( $j(this).val() == '' )
 1530+ $j(this).parent().find( 'label' ).show();
 1531+ });
 1532+
 1533+ if ( !( $j(this).data( 'dialogkeypressset' ) ) ) {
 1534+ $j(this).data( 'dialogkeypressset', true );
 1535+ // Execute the action associated with the first button
 1536+ // when the user presses Enter
 1537+ $j(this).closest( '.ui-dialog' ).keypress( function( e ) {
 1538+ if ( ( e.keyCode || e.which ) == 13 ) {
 1539+ var button = $j(this).data( 'dialogaction' ) || $j(this).find( 'button:first' );
 1540+ button.click();
 1541+ e.preventDefault();
 1542+ }
 1543+ });
 1544+
 1545+ // Make tabbing to a button and pressing
 1546+ // Enter do what people expect
 1547+ $j(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
 1548+ $j(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
 1549+ });
 1550+ }
 1551+ }
 1552+ }
 1553+ },
 1554+ 'insert-reference': {
 1555+ filters: [ '#wpTextbox1.toolbar-dialogs' ],
 1556+ titleMsg: 'wikieditor-toolbar-tool-reference-title',
 1557+ id: 'wikieditor-toolbar-reference-dialog',
 1558+ html: '\
 1559+ <div class="wikieditor-toolbar-dialog-wrapper">\
 1560+ <fieldset><div class="wikieditor-toolbar-table-form">\
 1561+ <div class="wikieditor-toolbar-field-wrapper">\
 1562+ <label for="wikieditor-toolbar-reference-text"\
 1563+ rel="wikieditor-toolbar-tool-reference-text"></label>\
 1564+ <input type="text" id="wikieditor-toolbar-reference-text" />\
 1565+ </div>\
 1566+ </div></fieldset>\
 1567+ </div>',
 1568+ init: function() {
 1569+ // Insert translated strings into labels
 1570+ $j( this ).find( '[rel]' ).each( function() {
 1571+ $j( this ).text( mw.usability.getMsg( $j( this ).attr( 'rel' ) ) );
 1572+ } );
 1573+
 1574+ },
 1575+ dialog: {
 1576+ dialogClass: 'wikiEditor-toolbar-dialog',
 1577+ width: 590,
 1578+ buttons: {
 1579+ 'wikieditor-toolbar-tool-reference-insert': function() {
 1580+ var insertText = $j( '#wikieditor-toolbar-reference-text' ).val();
 1581+ var whitespace = $j( '#wikieditor-toolbar-reference-dialog' ).data( 'whitespace' );
 1582+ var attributes = $j( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes' );
 1583+ // Close the dialog
 1584+ $j( this ).dialog( 'close' );
 1585+ $j.wikiEditor.modules.toolbar.fn.doAction(
 1586+ $j( this ).data( 'context' ),
 1587+ {
 1588+ type: 'replace',
 1589+ options: {
 1590+ pre: whitespace[0] + '<ref' + attributes + '>',
 1591+ peri: insertText,
 1592+ post: '</ref>' + whitespace[1]
 1593+ }
 1594+ },
 1595+ $j( this )
 1596+ );
 1597+ // Restore form state
 1598+ $j( '#wikieditor-toolbar-reference-text' ).val( "" );
 1599+ },
 1600+ 'wikieditor-toolbar-tool-reference-cancel': function() {
 1601+ // Clear any saved selection state
 1602+ var context = $j( this ).data( 'context' );
 1603+ context.fn.restoreStuffForIE();
 1604+ $j( this ).dialog( 'close' );
 1605+ }
 1606+ },
 1607+ open: function() {
 1608+ // Pre-fill the text fields based on the current selection
 1609+ var context = $j(this).data( 'context' );
 1610+ // Restore and immediately save selection state, needed for inserting stuff later
 1611+ context.fn.restoreStuffForIE();
 1612+ context.fn.saveStuffForIE();
 1613+ var selection = context.$textarea.textSelection( 'getSelection' );
 1614+ // set focus
 1615+ $j( '#wikieditor-toolbar-reference-text' ).focus();
 1616+ $j( '#wikieditor-toolbar-reference-dialog' )
 1617+ .data( 'whitespace', [ '', '' ] )
 1618+ .data( 'attributes', '' );
 1619+ if ( selection != '' ) {
 1620+ var matches, text;
 1621+ if ( ( matches = selection.match( /^(\s*)<ref([^\>]*)>([^\<]*)<\/ref\>(\s*)$/ ) ) ) {
 1622+ text = matches[3];
 1623+ // Preserve whitespace when replacing
 1624+ $j( '#wikieditor-toolbar-reference-dialog' ).data( 'whitespace', [ matches[1], matches[4] ] );
 1625+ $j( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes', matches[2] );
 1626+ } else {
 1627+ text = selection;
 1628+ }
 1629+ $j( '#wikieditor-toolbar-reference-text' ).val( text );
 1630+ }
 1631+ if ( !( $j( this ).data( 'dialogkeypressset' ) ) ) {
 1632+ $j( this ).data( 'dialogkeypressset', true );
 1633+ // Execute the action associated with the first button
 1634+ // when the user presses Enter
 1635+ $j( this ).closest( '.ui-dialog' ).keypress( function( e ) {
 1636+ if ( ( e.keyCode || e.which ) == 13 ) {
 1637+ var button = $j( this ).data( 'dialogaction' ) || $j( this ).find( 'button:first' );
 1638+ button.click();
 1639+ e.preventDefault();
 1640+ }
 1641+ } );
 1642+ // Make tabbing to a button and pressing
 1643+ // Enter do what people expect
 1644+ $j( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
 1645+ $j( this ).closest( '.ui-dialog' ).data( 'dialogaction', this );
 1646+ } );
 1647+ }
 1648+ }
 1649+ }
 1650+ },
 1651+ 'insert-table': {
 1652+ // For now, apply the old browser and iframe requirements to the link and table dialogs as well
 1653+ // This'll be removed once these dialogs are confirmed stable without the iframe and/or in more browsers
 1654+ /*
 1655+ 'browsers': {
 1656+ // Left-to-right languages
 1657+ 'ltr': {
 1658+ 'msie': [['>=', 7]],
 1659+ 'firefox': [['>=', 3]],
 1660+ 'opera': [['>=', 10]],
 1661+ 'safari': [['>=', 4]],
 1662+ 'chrome': [['>=', 4]]
 1663+ },
 1664+ // Right-to-left languages
 1665+ 'rtl': {
 1666+ 'msie': [['>=', 8]],
 1667+ 'firefox': [['>=', 3]],
 1668+ 'opera': [['>=', 10]],
 1669+ 'safari': [['>=', 4]],
 1670+ 'chrome': [['>=', 4]]
 1671+ }
 1672+ },
 1673+ 'req': [ 'iframe' ],
 1674+ */
 1675+ filters: [ '#wpTextbox1.toolbar-dialogs' ],
 1676+ titleMsg: 'wikieditor-toolbar-tool-table-title',
 1677+ id: 'wikieditor-toolbar-table-dialog',
 1678+ // FIXME: Localize 'x'?
 1679+ html: '\
 1680+ <div class="wikieditor-toolbar-dialog-wrapper">\
 1681+ <fieldset><div class="wikieditor-toolbar-table-form">\
 1682+ <div class="wikieditor-toolbar-field-wrapper">\
 1683+ <input type="checkbox" id="wikieditor-toolbar-table-dimensions-header" checked />\
 1684+ <label for="wikieditor-toolbar-table-dimensions-header"\
 1685+ rel="wikieditor-toolbar-tool-table-dimensions-header"></label>\
 1686+ </div>\
 1687+ <div class="wikieditor-toolbar-field-wrapper">\
 1688+ <input type="checkbox" id="wikieditor-toolbar-table-wikitable" checked />\
 1689+ <label for="wikieditor-toolbar-table-wikitable" rel="wikieditor-toolbar-tool-table-wikitable"></label>\
 1690+ </div>\
 1691+ <div class="wikieditor-toolbar-field-wrapper">\
 1692+ <input type="checkbox" id="wikieditor-toolbar-table-sortable" />\
 1693+ <label for="wikieditor-toolbar-table-sortable" rel="wikieditor-toolbar-tool-table-sortable"></label>\
 1694+ </div>\
 1695+ <div class="wikieditor-toolbar-table-dimension-fields">\
 1696+ <div class="wikieditor-toolbar-field-wrapper">\
 1697+ <label for="wikieditor-toolbar-table-dimensions-rows"\
 1698+ rel="wikieditor-toolbar-tool-table-dimensions-rows"></label><br />\
 1699+ <input type="text" id="wikieditor-toolbar-table-dimensions-rows" size="4" />\
 1700+ </div>\
 1701+ <div class="wikieditor-toolbar-field-wrapper">\
 1702+ <label for="wikieditor-toolbar-table-dimensions-columns"\
 1703+ rel="wikieditor-toolbar-tool-table-dimensions-columns"></label><br />\
 1704+ <input type="text" id="wikieditor-toolbar-table-dimensions-columns" size="4" />\
 1705+ </div>\
 1706+ </div>\
 1707+ </div></fieldset>\
 1708+ <div class="wikieditor-toolbar-table-preview-wrapper" >\
 1709+ <span rel="wikieditor-toolbar-tool-table-example"></span>\
 1710+ <div class="wikieditor-toolbar-table-preview-content">\
 1711+ <table id="wikieditor-toolbar-table-preview" class="wikieditor-toolbar-table-preview wikitable">\
 1712+ <tr class="wikieditor-toolbar-table-preview-header">\
 1713+ <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
 1714+ <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
 1715+ <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
 1716+ </tr><tr class="wikieditor-toolbar-table-preview-hidden" style="display: none;">\
 1717+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1718+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1719+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1720+ </tr><tr>\
 1721+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1722+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1723+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1724+ </tr><tr>\
 1725+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1726+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1727+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1728+ </tr><tr>\
 1729+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1730+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1731+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 1732+ </tr>\
 1733+ </table>\
 1734+ </div>\
 1735+ </div></div>',
 1736+ init: function() {
 1737+ $j(this).find( '[rel]' ).each( function() {
 1738+ $j(this).text( mw.usability.getMsg( $j(this).attr( 'rel' ) ) );
 1739+ });
 1740+ // Set tabindexes on form fields
 1741+ $j.wikiEditor.modules.dialogs.fn.setTabindexes( $j(this).find( 'input' ).not( '[tabindex]' ) );
 1742+
 1743+ $j( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 );
 1744+ $j( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 );
 1745+ $j( '#wikieditor-toolbar-table-wikitable' ).click( function() {
 1746+ $j( '.wikieditor-toolbar-table-preview' ).toggleClass( 'wikitable' );
 1747+ });
 1748+
 1749+ // Hack for sortable preview: dynamically adding
 1750+ // sortable class doesn't work, so we use a clone
 1751+ // FIXME: Relies on sortable table internals
 1752+ $j( '#wikieditor-toolbar-table-preview' )
 1753+ .clone()
 1754+ .attr( 'id', 'wikieditor-toolbar-table-preview2' )
 1755+ .addClass( 'sortable' )
 1756+ .insertAfter( $j( '#wikieditor-toolbar-table-preview' ) )
 1757+ .hide();
 1758+ if ( typeof ts_makeSortable == 'function' )
 1759+ ts_makeSortable( $j( '#wikieditor-toolbar-table-preview2' ).get( 0 ) );
 1760+ $j( '#wikieditor-toolbar-table-sortable' ).click( function() {
 1761+ // Swap the currently shown one clone with the other one
 1762+ $j( '#wikieditor-toolbar-table-preview' )
 1763+ .hide()
 1764+ .attr( 'id', 'wikieditor-toolbar-table-preview3' );
 1765+ $j( '#wikieditor-toolbar-table-preview2' )
 1766+ .attr( 'id', 'wikieditor-toolbar-table-preview' )
 1767+ .show();
 1768+ $j( '#wikieditor-toolbar-table-preview3' ).attr( 'id', 'wikieditor-toolbar-table-preview2' );
 1769+ });
 1770+
 1771+ $j( '#wikieditor-toolbar-table-dimensions-header' ).click( function() {
 1772+ // Instead of show/hiding, switch the HTML around
 1773+ // We do this because the sortable tables script styles the first row,
 1774+ // visible or not
 1775+ var headerHTML = $j( '.wikieditor-toolbar-table-preview-header' ).html();
 1776+ var hiddenHTML = $j( '.wikieditor-toolbar-table-preview-hidden' ).html();
 1777+ $j( '.wikieditor-toolbar-table-preview-header' ).html( hiddenHTML );
 1778+ $j( '.wikieditor-toolbar-table-preview-hidden' ).html( headerHTML );
 1779+ if ( typeof ts_makeSortable == 'function' )
 1780+ ts_makeSortable(
 1781+ $j( '#wikieditor-toolbar-table-preview, #wikieditor-toolbar-table-preview2' )
 1782+ .filter( '.sortable' )
 1783+ .get( 0 )
 1784+ );
 1785+ });
 1786+
 1787+ },
 1788+ dialog: {
 1789+ resizable: false,
 1790+ dialogClass: 'wikiEditor-toolbar-dialog',
 1791+ width: 590,
 1792+ buttons: {
 1793+ 'wikieditor-toolbar-tool-table-insert': function() {
 1794+ var rowsVal = $j( '#wikieditor-toolbar-table-dimensions-rows' ).val();
 1795+ var colsVal = $j( '#wikieditor-toolbar-table-dimensions-columns' ).val();
 1796+ var rows = parseInt( rowsVal, 10 );
 1797+ var cols = parseInt( colsVal, 10 );
 1798+ var header = $j( '#wikieditor-toolbar-table-dimensions-header' ).is( ':checked' ) ? 1 : 0;
 1799+ var u = mw.usability;
 1800+ if ( isNaN( rows ) || isNaN( cols ) || rows != rowsVal || cols != colsVal ) {
 1801+ alert( u.getMsg( 'wikieditor-toolbar-tool-table-invalidnumber' ) );
 1802+ return;
 1803+ }
 1804+ if ( rows + header == 0 || cols == 0 ) {
 1805+ alert( u.getMsg( 'wikieditor-toolbar-tool-table-zero' ) );
 1806+ return;
 1807+ }
 1808+ if ( rows * cols > 1000 ) {
 1809+ alert( u.getMsg( 'wikieditor-toolbar-tool-table-toomany', 1000 ) );
 1810+ return;
 1811+ }
 1812+ var headerText = u.getMsg( 'wikieditor-toolbar-tool-table-example-header' );
 1813+ var normalText = u.getMsg( 'wikieditor-toolbar-tool-table-example' );
 1814+ var table = "";
 1815+ for ( var r = 0; r < rows + header; r++ ) {
 1816+ table += "|-\n";
 1817+ for ( var c = 0; c < cols; c++ ) {
 1818+ var isHeader = ( header && r == 0 );
 1819+ var delim = isHeader ? '!' : '|';
 1820+ if ( c > 0 ) {
 1821+ delim += delim;
 1822+ }
 1823+ table += delim + ' ' + ( isHeader ? headerText : normalText ) + ' ';
 1824+ }
 1825+ // Replace trailing space by newline
 1826+ // table[table.length - 1] is read-only
 1827+ table = table.substr( 0, table.length - 1 ) + "\n";
 1828+ }
 1829+ var classes = [];
 1830+ if ( $j( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) )
 1831+ classes.push( 'wikitable' );
 1832+ if ( $j( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) )
 1833+ classes.push( 'sortable' );
 1834+ var classStr = classes.length > 0 ? ' class="' + classes.join( ' ' ) + '"' : '';
 1835+ $j(this).dialog( 'close' );
 1836+ $j.wikiEditor.modules.toolbar.fn.doAction(
 1837+ $j(this).data( 'context' ),
 1838+ {
 1839+ type: 'replace',
 1840+ options: {
 1841+ pre: '{|' + classStr + "\n",
 1842+ peri: table,
 1843+ post: '|}',
 1844+ ownline: true
 1845+ }
 1846+ },
 1847+ $j(this)
 1848+ );
 1849+
 1850+ // Restore form state
 1851+ $j( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 );
 1852+ $j( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 );
 1853+ // Simulate clicks instead of setting values, so the according
 1854+ // actions are performed
 1855+ if ( !$j( '#wikieditor-toolbar-table-dimensions-header' ).is( ':checked' ) )
 1856+ $j( '#wikieditor-toolbar-table-dimensions-header' ).click();
 1857+ if ( !$j( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) )
 1858+ $j( '#wikieditor-toolbar-table-wikitable' ).click();
 1859+ if ( $j( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) )
 1860+ $j( '#wikieditor-toolbar-table-sortable' ).click();
 1861+ },
 1862+ 'wikieditor-toolbar-tool-table-cancel': function() {
 1863+ $j(this).dialog( 'close' );
 1864+ }
 1865+ },
 1866+ open: function() {
 1867+ $j( '#wikieditor-toolbar-table-dimensions-rows' ).focus();
 1868+ if ( !( $j(this).data( 'dialogkeypressset' ) ) ) {
 1869+ $j(this).data( 'dialogkeypressset', true );
 1870+ // Execute the action associated with the first button
 1871+ // when the user presses Enter
 1872+ $j(this).closest( '.ui-dialog' ).keypress( function( e ) {
 1873+ if ( ( e.keyCode || e.which ) == 13 ) {
 1874+ var button = $j(this).data( 'dialogaction' ) || $j(this).find( 'button:first' );
 1875+ button.click();
 1876+ e.preventDefault();
 1877+ }
 1878+ });
 1879+
 1880+ // Make tabbing to a button and pressing
 1881+ // Enter do what people expect
 1882+ $j(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
 1883+ $j(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
 1884+ });
 1885+ }
 1886+ }
 1887+ }
 1888+ },
 1889+ 'search-and-replace': {
 1890+ 'browsers': {
 1891+ // Left-to-right languages
 1892+ 'ltr': {
 1893+ 'msie': false,
 1894+ 'firefox': [['>=', 2]],
 1895+ 'opera': false,
 1896+ 'safari': [['>=', 3]],
 1897+ 'chrome': [['>=', 3]]
 1898+ },
 1899+ // Right-to-left languages
 1900+ 'rtl': {
 1901+ 'msie': false,
 1902+ 'firefox': [['>=', 2]],
 1903+ 'opera': false,
 1904+ 'safari': [['>=', 3]],
 1905+ 'chrome': [['>=', 3]]
 1906+ }
 1907+ },
 1908+ filters: [ '#wpTextbox1.toolbar-dialogs' ],
 1909+ titleMsg: 'wikieditor-toolbar-tool-replace-title',
 1910+ id: 'wikieditor-toolbar-replace-dialog',
 1911+ html: '\
 1912+ <div id="wikieditor-toolbar-replace-message">\
 1913+ <div id="wikieditor-toolbar-replace-nomatch" rel="wikieditor-toolbar-tool-replace-nomatch"></div>\
 1914+ <div id="wikieditor-toolbar-replace-success"></div>\
 1915+ <div id="wikieditor-toolbar-replace-emptysearch" rel="wikieditor-toolbar-tool-replace-emptysearch"></div>\
 1916+ <div id="wikieditor-toolbar-replace-invalidregex"></div>\
 1917+ </div>\
 1918+ <fieldset>\
 1919+ <div class="wikieditor-toolbar-field-wrapper">\
 1920+ <label for="wikieditor-toolbar-replace-search" rel="wikieditor-toolbar-tool-replace-search"></label>\
 1921+ <input type="text" id="wikieditor-toolbar-replace-search" style="width: 100%;" />\
 1922+ </div>\
 1923+ <div class="wikieditor-toolbar-field-wrapper">\
 1924+ <label for="wikieditor-toolbar-replace-replace" rel="wikieditor-toolbar-tool-replace-replace"></label>\
 1925+ <input type="text" id="wikieditor-toolbar-replace-replace" style="width: 100%;" />\
 1926+ </div>\
 1927+ <div class="wikieditor-toolbar-field-wrapper">\
 1928+ <input type="checkbox" id="wikieditor-toolbar-replace-case" />\
 1929+ <label for="wikieditor-toolbar-replace-case" rel="wikieditor-toolbar-tool-replace-case"></label>\
 1930+ </div>\
 1931+ <div class="wikieditor-toolbar-field-wrapper">\
 1932+ <input type="checkbox" id="wikieditor-toolbar-replace-regex" />\
 1933+ <label for="wikieditor-toolbar-replace-regex" rel="wikieditor-toolbar-tool-replace-regex"></label>\
 1934+ </div>\
 1935+ </fieldset>',
 1936+ init: function() {
 1937+ var u = mw.usability;
 1938+ $j(this).find( '[rel]' ).each( function() {
 1939+ $j(this).text( u.getMsg( $j(this).attr( 'rel' ) ) );
 1940+ });
 1941+ // Set tabindexes on form fields
 1942+ $j.wikiEditor.modules.dialogs.fn.setTabindexes( $j(this).find( 'input' ).not( '[tabindex]' ) );
 1943+
 1944+ // TODO: Find a cleaner way to share this function
 1945+ $j(this).data( 'replaceCallback', function( mode ) {
 1946+ $j( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
 1947+ var searchStr = $j( '#wikieditor-toolbar-replace-search' ).val();
 1948+ if ( searchStr == '' ) {
 1949+ $j( '#wikieditor-toolbar-replace-emptysearch' ).show();
 1950+ return;
 1951+ }
 1952+ var replaceStr = $j( '#wikieditor-toolbar-replace-replace' ).val();
 1953+ var flags = 'm';
 1954+ var matchCase = $j( '#wikieditor-toolbar-replace-case' ).is( ':checked' );
 1955+ var isRegex = $j( '#wikieditor-toolbar-replace-regex' ).is( ':checked' );
 1956+ if ( !matchCase ) {
 1957+ flags += 'i';
 1958+ }
 1959+ if ( mode == 'replaceAll' ) {
 1960+ flags += 'g';
 1961+ }
 1962+ if ( !isRegex ) {
 1963+ searchStr = RegExp.escape( searchStr );
 1964+ }
 1965+ try {
 1966+ var regex = new RegExp( searchStr, flags );
 1967+ } catch( e ) {
 1968+ $j( '#wikieditor-toolbar-replace-invalidregex' )
 1969+ .text( u.getMsg( 'wikieditor-toolbar-tool-replace-invalidregex',
 1970+ e.message ) )
 1971+ .show();
 1972+ return;
 1973+ }
 1974+ var $textarea = $j(this).data( 'context' ).$textarea;
 1975+ var text = $textarea.textSelection( 'getContents' );
 1976+ var match = false;
 1977+ var offset, s;
 1978+ if ( mode != 'replaceAll' ) {
 1979+ offset = $j(this).data( 'offset' );
 1980+ s = text.substr( offset );
 1981+ match = s.match( regex );
 1982+ }
 1983+ if ( !match ) {
 1984+ // Search hit BOTTOM, continuing at TOP
 1985+ offset = 0;
 1986+ s = text;
 1987+ match = s.match( regex );
 1988+ }
 1989+
 1990+ if ( !match ) {
 1991+ $j( '#wikieditor-toolbar-replace-nomatch' ).show();
 1992+ } else if ( mode == 'replaceAll' ) {
 1993+ // Instead of using repetitive .match() calls, we use one .match() call with /g
 1994+ // and indexOf() followed by substr() to find the offsets. This is actually
 1995+ // faster because our indexOf+substr loop is faster than a match loop, and the
 1996+ // /g match is so ridiculously fast that it's negligible.
 1997+ // FIXME: Repetitively calling encapsulateSelection() is probably the best strategy
 1998+ // in Firefox/Webkit, but in IE replacing the entire content once is better.
 1999+ var index;
 2000+ for ( var i = 0; i < match.length; i++ ) {
 2001+ index = s.indexOf( match[i] );
 2002+ if ( index == -1 ) {
 2003+ // This shouldn't happen
 2004+ break;
 2005+ }
 2006+ var matchedText = s.substr( index, match[i].length );
 2007+ s = s.substr( index + match[i].length );
 2008+
 2009+ var start = index + offset;
 2010+ var end = start + match[i].length;
 2011+ // Make regex placeholder substitution ($1) work
 2012+ var replace = isRegex ? matchedText.replace( regex, replaceStr ) : replaceStr;
 2013+ var newEnd = start + replace.length;
 2014+ $textarea
 2015+ .textSelection( 'setSelection', { 'start': start, 'end': end } )
 2016+ .textSelection( 'encapsulateSelection', {
 2017+ 'peri': replace,
 2018+ 'replace': true } )
 2019+ .textSelection( 'setSelection', { 'start': start, 'end': newEnd } );
 2020+ offset = newEnd;
 2021+ }
 2022+ $j( '#wikieditor-toolbar-replace-success' )
 2023+ .text( u.getMsg( 'wikieditor-toolbar-tool-replace-success', match.length ) )
 2024+ .show();
 2025+ $j(this).data( 'offset', 0 );
 2026+ } else {
 2027+ // Make regex placeholder substitution ($1) work
 2028+ var replace = isRegex ? match[0].replace( regex, replaceStr ): replaceStr;
 2029+ var start = match.index + offset;
 2030+ var end = start + match[0].length;
 2031+ var newEnd = start + replace.length;
 2032+ var context = $j( this ).data( 'context' );
 2033+ $textarea.textSelection( 'setSelection', { 'start': start,
 2034+ 'end': end } );
 2035+ if ( mode == 'replace' ) {
 2036+ $textarea
 2037+ .textSelection( 'encapsulateSelection', {
 2038+ 'peri': replace,
 2039+ 'replace': true } )
 2040+ .textSelection( 'setSelection', {
 2041+ 'start': start,
 2042+ 'end': newEnd } );
 2043+ }
 2044+ $textarea.textSelection( 'scrollToCaretPosition' );
 2045+ $textarea.textSelection( 'setSelection', { 'start': start,
 2046+ 'end': mode == 'replace' ? newEnd : end } );
 2047+ $j( this ).data( 'offset', mode == 'replace' ? newEnd : end );
 2048+ var textbox = typeof context.$iframe != 'undefined' ? context.$iframe[0].contentWindow : $textarea[0];
 2049+ textbox.focus();
 2050+ }
 2051+ });
 2052+ },
 2053+ dialog: {
 2054+ width: 500,
 2055+ dialogClass: 'wikiEditor-toolbar-dialog',
 2056+ buttons: {
 2057+ 'wikieditor-toolbar-tool-replace-button-findnext': function( e ) {
 2058+ $j(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
 2059+ $j(this).data( 'replaceCallback' ).call( this, 'find' );
 2060+ },
 2061+ 'wikieditor-toolbar-tool-replace-button-replacenext': function( e ) {
 2062+ $j(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
 2063+ $j(this).data( 'replaceCallback' ).call( this, 'replace' );
 2064+ },
 2065+ 'wikieditor-toolbar-tool-replace-button-replaceall': function( e ) {
 2066+ $j(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
 2067+ $j(this).data( 'replaceCallback' ).call( this, 'replaceAll' );
 2068+ },
 2069+ 'wikieditor-toolbar-tool-replace-close': function() {
 2070+ $j(this).dialog( 'close' );
 2071+ }
 2072+ },
 2073+ open: function() {
 2074+ $j(this).data( 'offset', 0 );
 2075+ $j( '#wikieditor-toolbar-replace-search' ).focus();
 2076+ $j( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
 2077+ if ( !( $j(this).data( 'onetimeonlystuff' ) ) ) {
 2078+ $j(this).data( 'onetimeonlystuff', true );
 2079+ // Execute the action associated with the first button
 2080+ // when the user presses Enter
 2081+ $j(this).closest( '.ui-dialog' ).keypress( function( e ) {
 2082+ if ( ( e.keyCode || e.which ) == 13 ) {
 2083+ var button = $j(this).data( 'dialogaction' ) || $j(this).find( 'button:first' );
 2084+ button.click();
 2085+ e.preventDefault();
 2086+ }
 2087+ });
 2088+ // Make tabbing to a button and pressing
 2089+ // Enter do what people expect
 2090+ $j(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
 2091+ $j(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
 2092+ });
 2093+ }
 2094+ var dialog = $j(this).closest( '.ui-dialog' );
 2095+ var that = this;
 2096+ var context = $j(this).data( 'context' );
 2097+ var textbox = typeof context.$iframe != 'undefined' ?
 2098+ context.$iframe[0].contentWindow.document : context.$textarea;
 2099+
 2100+ $j( textbox )
 2101+ .bind( 'keypress.srdialog', function( e ) {
 2102+ if ( ( e.keyCode || e.which ) == 13 ) {
 2103+ // Enter
 2104+ var button = dialog.data( 'dialogaction' ) || dialog.find( 'button:first' );
 2105+ button.click();
 2106+ e.preventDefault();
 2107+ } else if ( ( e.keyCode || e.which ) == 27 ) {
 2108+ // Escape
 2109+ $j(that).dialog( 'close' );
 2110+ }
 2111+ });
 2112+ },
 2113+ close: function() {
 2114+ var context = $j(this).data( 'context' );
 2115+ var textbox = typeof context.$iframe != 'undefined' ?
 2116+ context.$iframe[0].contentWindow.document : context.$textarea;
 2117+ $j( textbox ).unbind( 'keypress.srdialog' );
 2118+ $j(this).closest( '.ui-dialog' ).data( 'dialogaction', false );
 2119+ }
 2120+ }
 2121+ }
 2122+} } );
 2123+
 2124+} } );
 2125+
Property changes on: trunk/extensions/WikiEditor/modules/Toolbar.js
___________________________________________________________________
Added: svn:eol-style
12126 + native

Status & tagging log