Index: trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.i18n.php |
— | — | @@ -15,6 +15,13 @@ |
16 | 16 | 'edittoolbar' => 'Editing Toolbar', |
17 | 17 | 'edittoolbar-desc' => 'Edit page toolbar with enhanced usability', |
18 | 18 | 'edittoolbar-preference' => 'Enable enhanced editing toolbar', |
| 19 | + /* Sections */ |
| 20 | + 'edittoolbar-section-format' => 'Format', |
| 21 | + 'edittoolbar-section-insert' => 'Insert', |
| 22 | + 'edittoolbar-section-characters' => 'Special Characters', |
| 23 | + 'edittoolbar-section-help' => 'Help', |
| 24 | + 'edittoolbar-section-insert' => 'Insert', |
| 25 | + /* Main Section */ |
19 | 26 | 'edittoolbar-format-bold' => 'Bold', |
20 | 27 | 'edittoolbar-format-bold-example' => 'Bold text', |
21 | 28 | 'edittoolbar-format-italic' => 'Italic', |
— | — | @@ -23,8 +30,29 @@ |
24 | 31 | 'edittoolbar-insert-ilink-example' => 'Link title', |
25 | 32 | 'edittoolbar-insert-xlink' => 'External link (remember http:// prefix)', |
26 | 33 | 'edittoolbar-insert-xlink-example' => 'http://www.example.com link title', |
27 | | - 'edittoolbar-insert-image' => 'Embedded file', |
28 | | - 'edittoolbar-insert-image-example' => 'File:Example.jpg', |
| 34 | + 'edittoolbar-insert-file' => 'Embedded file', |
| 35 | + 'edittoolbar-insert-file-pre' => '$1File:', |
| 36 | + 'edittoolbar-insert-file-example' => 'Example.jpg', |
29 | 37 | 'edittoolbar-insert-reference' => 'Insert a reference', |
30 | 38 | 'edittoolbar-insert-reference-example' => 'Insert footnote text here', |
| 39 | + /* Formatting Section */ |
| 40 | + 'edittoolbar-format-ulist' => 'Bulleted list', |
| 41 | + 'edittoolbar-format-ulist-example' => 'Bulleted list item', |
| 42 | + 'edittoolbar-format-olist' => 'Numbered list', |
| 43 | + 'edittoolbar-format-olist-example' => 'Numbered list item', |
| 44 | + 'edittoolbar-format-heading' => 'Heading', |
| 45 | + 'edittoolbar-format-heading-1' => 'Level 1', |
| 46 | + 'edittoolbar-format-heading-2' => 'Level 2', |
| 47 | + 'edittoolbar-format-heading-3' => 'Level 3', |
| 48 | + 'edittoolbar-format-heading-4' => 'Level 4', |
| 49 | + 'edittoolbar-format-heading-5' => 'Level 5', |
| 50 | + 'edittoolbar-format-heading-example' => 'Heading text', |
| 51 | + 'edittoolbar-format-superscript' => 'Superscript', |
| 52 | + 'edittoolbar-format-superscript-example' => 'Superscript text', |
| 53 | + 'edittoolbar-format-subscript' => 'Subscript', |
| 54 | + 'edittoolbar-format-subscript-example' => 'Subscript text', |
| 55 | + 'edittoolbar-format-big' => 'Big', |
| 56 | + 'edittoolbar-format-big-example' => 'Big text', |
| 57 | + 'edittoolbar-format-small' => 'Small', |
| 58 | + 'edittoolbar-format-small-example' => 'Small text', |
31 | 59 | ); |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.css |
— | — | @@ -2,26 +2,65 @@ |
3 | 3 | |
4 | 4 | div#edittoolbar { |
5 | 5 | width: 100%; |
6 | | - height: 32px; |
7 | 6 | background-color: #EEEEEE; |
8 | 7 | } |
9 | | - |
10 | | -div#edittoolbar > div.group { |
| 8 | +div#edittoolbar div.tabs, |
| 9 | +div#edittoolbar div.group { |
11 | 10 | float: left; |
12 | 11 | height: 26px; |
13 | 12 | margin: 3px; |
14 | 13 | padding-left: 3px; |
15 | 14 | border-left: solid 1px #DDDDDD; |
16 | 15 | } |
17 | | -div#edittoolbar > div.group:first-child { |
| 16 | +div#edittoolbar div.group:first-child { |
18 | 17 | border-left: none; |
19 | 18 | } |
20 | | - |
21 | | -div#edittoolbar > div.group > img { |
| 19 | +div#edittoolbar div.sections { |
22 | 20 | float: left; |
| 21 | + width: 100%; |
| 22 | + clear: both; |
| 23 | +} |
| 24 | +div#edittoolbar div.sections > div.section { |
| 25 | + display: none; |
| 26 | + float: left; |
| 27 | + width: 100%; |
| 28 | + border-top: solid 1px #DDDDDD; |
| 29 | +} |
| 30 | +div#edittoolbar div.group > img { |
| 31 | + float: left; |
23 | 32 | border: 0px; |
24 | 33 | height: 22px; |
25 | 34 | width: 22px; |
26 | 35 | margin: 2px; |
27 | 36 | cursor: pointer; |
| 37 | +} |
| 38 | +div#edittoolbar div.group > select { |
| 39 | + float: left; |
| 40 | + margin: 2px; |
| 41 | + height: 22px; |
| 42 | + cursor: pointer; |
| 43 | +} |
| 44 | +div#edittoolbar div.tabs > div.tab { |
| 45 | + float: left; |
| 46 | + line-height: 26px; |
| 47 | +} |
| 48 | +div#edittoolbar div.tabs > div.tab > a, |
| 49 | +div#edittoolbar div.tabs > div.tab > a:visited { |
| 50 | + padding-left: 18px; |
| 51 | + padding-right: 12px; |
| 52 | + display: block; |
| 53 | + height: 26px; |
| 54 | + cursor: pointer; |
| 55 | + color: #00528C; |
| 56 | + background-image: url(images/arrow-right.png); |
| 57 | + background-position: left center; |
| 58 | + background-repeat: no-repeat; |
| 59 | +} |
| 60 | +div#edittoolbar div.tabs > div.tab > a.current, |
| 61 | +div#edittoolbar div.tabs > div.tab > a.current:visited { |
| 62 | + color: #333333; |
| 63 | + background-image: url(images/arrow-down.png); |
| 64 | +} |
| 65 | +div#edittoolbar div.tabs > div.tab > a.current:hover { |
| 66 | + text-decoration: none; |
28 | 67 | } |
\ No newline at end of file |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.hooks.php |
— | — | @@ -24,43 +24,67 @@ |
25 | 25 | if ( $wgUser->getOption( 'usebetatoolbar' ) ) { |
26 | 26 | $toolbar = '<div id="edittoolbar"></div>'; |
27 | 27 | } |
| 28 | + |
28 | 29 | // List of messages to be sent to the client for use in the toolbar |
29 | | - // all of which will get a prefix of "toolbar" |
30 | 30 | $messages = array( |
31 | | - 'format-bold', |
32 | | - 'format-bold-example', |
33 | | - 'format-italic', |
34 | | - 'format-italic-example', |
35 | | - 'insert-ilink', |
36 | | - 'insert-ilink-example', |
37 | | - 'insert-xlink', |
38 | | - 'insert-xlink-example', |
39 | | - 'insert-image', |
40 | | - 'insert-image-example', |
41 | | - 'insert-reference', |
42 | | - 'insert-reference-example', |
| 31 | + /* Sections */ |
| 32 | + 'edittoolbar-section-format', |
| 33 | + 'edittoolbar-section-insert', |
| 34 | + 'edittoolbar-section-characters', |
| 35 | + 'edittoolbar-section-help', |
| 36 | + /* Main Section */ |
| 37 | + 'edittoolbar-format-bold', |
| 38 | + 'edittoolbar-format-bold-example', |
| 39 | + 'edittoolbar-format-italic', |
| 40 | + 'edittoolbar-format-italic-example', |
| 41 | + 'edittoolbar-insert-ilink', |
| 42 | + 'edittoolbar-insert-ilink-example', |
| 43 | + 'edittoolbar-insert-xlink', |
| 44 | + 'edittoolbar-insert-xlink-example', |
| 45 | + 'edittoolbar-insert-file', |
| 46 | + 'edittoolbar-insert-file-pre', |
| 47 | + 'edittoolbar-insert-file-example', |
| 48 | + 'edittoolbar-insert-reference', |
| 49 | + 'edittoolbar-insert-reference-example', |
| 50 | + /* Formatting Section */ |
| 51 | + 'edittoolbar-format-ulist', |
| 52 | + 'edittoolbar-format-ulist-example', |
| 53 | + 'edittoolbar-format-olist', |
| 54 | + 'edittoolbar-format-olist-example', |
| 55 | + 'edittoolbar-format-heading', |
| 56 | + 'edittoolbar-format-heading-1', |
| 57 | + 'edittoolbar-format-heading-2', |
| 58 | + 'edittoolbar-format-heading-3', |
| 59 | + 'edittoolbar-format-heading-4', |
| 60 | + 'edittoolbar-format-heading-5', |
| 61 | + 'edittoolbar-format-heading-example', |
| 62 | + 'edittoolbar-format-superscript', |
| 63 | + 'edittoolbar-format-superscript-example', |
| 64 | + 'edittoolbar-format-subscript', |
| 65 | + 'edittoolbar-format-subscript-example', |
| 66 | + 'edittoolbar-format-big', |
| 67 | + 'edittoolbar-format-big-example', |
| 68 | + 'edittoolbar-format-small', |
| 69 | + 'edittoolbar-format-small-example', |
43 | 70 | ); |
44 | 71 | // Transforms messages into javascript object members |
45 | 72 | foreach ( $messages as $i => $message ) { |
46 | | - $escapedMessageValue = Xml::escapeJsString( |
47 | | - wfMsg( 'edittoolbar-' . $message ) |
48 | | - ); |
| 73 | + $escapedMessageValue = Xml::escapeJsString( wfMsg( $message ) ); |
49 | 74 | $escapedMessageKey = Xml::escapeJsString( $message ); |
50 | 75 | $messages[$i] = "'{$escapedMessageKey}':'{$escapedMessageValue}'"; |
51 | 76 | } |
52 | 77 | // Converts array of object members to a comma delimited list |
53 | 78 | $messagesList = implode( ',', $messages ); |
54 | 79 | // Encapsulates list in javascript code to set them durring load |
55 | | - $messagesJs = "editToolbar.setMessages({{$messagesList}});"; |
| 80 | + $messagesJs = "loadGM({{$messagesList}});"; |
56 | 81 | // Appends javascript message setting code |
57 | 82 | $toolbar .= Xml::element( |
58 | 83 | 'script', array( 'type' => $wgJsMimeType ), $messagesJs |
59 | 84 | ); |
60 | | - |
61 | 85 | // Continue |
62 | 86 | return true; |
63 | 87 | } |
64 | | - |
| 88 | + |
65 | 89 | /** |
66 | 90 | * GetPreferences hook |
67 | 91 | * Add toolbar related items to the preferences |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.js |
— | — | @@ -1,210 +1,471 @@ |
2 | 2 | /* JavaScript for EditToolbar extension */ |
3 | 3 | |
4 | 4 | /** |
5 | | - * Prototype for global editToolbar object |
6 | | - * @param {String} toolbarSelector jQuery compatible selector of toolbar div |
| 5 | + * This is designed to be directly compatible with (and is essentially taken |
| 6 | + * directly from) the mv_embed code for bringing internationalized messages into |
| 7 | + * the JavaScript space. As such, if we get to the point of merging that stuff |
| 8 | + * into the main branch this code will be uneeded and probably cause issues. |
7 | 9 | */ |
8 | | -function EditToolbar( toolbarSelector ) { |
9 | | - |
10 | | - /* Private Members */ |
11 | | - |
12 | | - // Reference to object's self |
13 | | - var self = this; |
14 | | - // Reference to DIV container object (set on initialize) |
15 | | - var toolbarDiv = null; |
16 | | - // Sections (main and subs), groups and buttons |
17 | | - var tools = { main: {}, subs: {} }; |
18 | | - // Internationalized user interface messages |
19 | | - var messages = {}; |
20 | | - |
21 | | - /* Functions */ |
22 | | - |
23 | | - /** |
24 | | - * Initializes the toolbar user interface |
25 | | - */ |
26 | | - this.initialize = function() { |
27 | | - // Path to images (THIS WILL HAVE TO CHANGE IF YOU MOVE THIS INTO CORE) |
28 | | - var imagePath = wgScriptPath + |
29 | | - '/extensions/UsabilityInitiative/EditToolbar/images/'; |
30 | | - // Gets object handle for container |
31 | | - toolbarDiv = $( toolbarSelector ); |
32 | | - // Checks if the toolbar div existed |
33 | | - if ( toolbarDiv ) { |
| 10 | +// Creates global message object if not already in existence |
| 11 | +if ( !gMsg ) var gMsg = {}; |
| 12 | +/** |
| 13 | + * Caches a list of messages for later retieval |
| 14 | + * @param {Object} msgSet Hash of key:value pairs of messages to cache |
| 15 | + */ |
| 16 | +function loadGM( msgSet ){ |
| 17 | + for ( var i in msgSet ){ |
| 18 | + gMsg[ i ] = msgSet[i]; |
| 19 | + } |
| 20 | +} |
| 21 | +/** |
| 22 | + * Retieves a message from the global message cache, performing on-the-fly |
| 23 | + * replacements using MediaWiki message syntax ($1, $2, etc.) |
| 24 | + * @param {String} key Name of message as it is in MediaWiki |
| 25 | + * @param {Array} args Array of replacment arguments |
| 26 | + */ |
| 27 | +function gM( key, args ) { |
| 28 | + var ms = ''; |
| 29 | + if ( key in gMsg ) { |
| 30 | + ms = gMsg[ key ]; |
| 31 | + if ( typeof args == 'object' || typeof args == 'array' ) { |
| 32 | + for ( var v in args ){ |
| 33 | + var rep = '\$'+ ( parseInt(v) + 1 ); |
| 34 | + ms = ms.replace( rep, args[v]); |
| 35 | + } |
| 36 | + } else if ( typeof args =='string' || typeof args =='number' ) { |
| 37 | + ms = ms.replace( /\$1/, args ); |
| 38 | + } |
| 39 | + return ms; |
| 40 | + } else { |
| 41 | + return '[' + key + ']'; |
| 42 | + } |
| 43 | +} |
| 44 | +/** |
| 45 | + * This is the toolbar plugin, which can be used like |
| 46 | + * $( 'div#edittoolbar' ).toolbar( '#wpTextbox1', tools ); |
| 47 | + * Where tools is an array of objects which describe each tool (see below for |
| 48 | + * specific examples) (THIS NEEDS BETTER DOCUMENTATION WHEN I HAVE TIME) |
| 49 | + */ |
| 50 | +(function($){ |
| 51 | + $.fn.extend({ |
| 52 | + /** |
| 53 | + * |
| 54 | + * @param {Object} textbox |
| 55 | + * @param {Object} tools |
| 56 | + */ |
| 57 | + toolbar: function( textbox, tools ) { |
| 58 | + return this.each(function() { |
| 59 | + // Checks if main section is in the structure |
| 60 | + if ( 'main' in tools ) { |
| 61 | + // Adds main section to toolbar |
| 62 | + $(this).addToolbarSection( tools.main, textbox ); |
| 63 | + } |
| 64 | + // Appends additional section tabs |
| 65 | + var tabDiv = $( '<div />' ) |
| 66 | + .attr( 'class', 'tabs' ) |
| 67 | + .appendTo( $(this) ); |
| 68 | + // Appends additional section |
| 69 | + var sectionsDiv = $( '<div />' ) |
| 70 | + .attr( 'class', 'sections' ) |
| 71 | + .appendTo( $(this) ); |
| 72 | + // Appends float-clearing div |
| 73 | + $(this).append( $( '<div style="clear:both"></div>' ) ); |
| 74 | + // Loops over each section |
| 75 | + for ( section in tools ) { |
| 76 | + // Skips over main (was handled as special case already) |
| 77 | + if ( section == 'main' ) { |
| 78 | + continue; |
| 79 | + } |
| 80 | + // Appends section content |
| 81 | + var sectionDiv = $( '<div />') |
| 82 | + .attr( { 'class': 'section', 'id': $(this).attr( 'id' ) + '-section-' + section } ) |
| 83 | + .appendTo( sectionsDiv ); |
| 84 | + // Appends toolbar to section div |
| 85 | + sectionDiv.addToolbarSection( tools[section], textbox ) |
| 86 | + // Appends section tab |
| 87 | + tabDiv.append( |
| 88 | + $( '<div />' ) |
| 89 | + .attr( 'class', 'tab' ) |
| 90 | + .append( |
| 91 | + $( '<a />' ) |
| 92 | + .text( tools[section].label || gM( tools[section].labelMsg ) ) |
| 93 | + .attr( { 'href': '#', 'rel': section } ) |
| 94 | + .data( 'sectionDiv', sectionDiv ) |
| 95 | + .click( function() { |
| 96 | + $(this).blur(); |
| 97 | + var show = ( $(this).data( 'sectionDiv' ).css( 'display' ) == 'none' ); |
| 98 | + $(this).data( 'sectionDiv' ).parent().children().hide(); |
| 99 | + $(this).parent().parent().find( 'a' ).removeClass( 'current' ); |
| 100 | + if ( show ) { |
| 101 | + $(this).data( 'sectionDiv' ).show(); |
| 102 | + $(this).addClass( 'current' ); |
| 103 | + } |
| 104 | + return false; |
| 105 | + }) |
| 106 | + ) |
| 107 | + ); |
| 108 | + } |
| 109 | + }); |
| 110 | + }, |
| 111 | + /** |
| 112 | + * Adds a toolbar section to a containing div |
| 113 | + * @param {Object} section Section data to build toolbar from |
| 114 | + */ |
| 115 | + addToolbarSection: function( section, textbox ) { |
| 116 | + // Path to images (THIS WILL HAVE TO CHANGE IF YOU MOVE THIS INTO CORE) |
| 117 | + var imagePath = wgScriptPath + |
| 118 | + '/extensions/UsabilityInitiative/EditToolbar/images/'; |
| 119 | + // Check for groups |
| 120 | + if ( !( 'groups' in section ) ) { |
| 121 | + return; |
| 122 | + } |
34 | 123 | // Loops over each main group |
35 | | - for ( group in tools.main ) { |
36 | | - // Creates tool group |
37 | | - var groupDiv = $( '<div class="group"></div>' ); |
38 | | - // Appends group to toolbar |
39 | | - groupDiv.appendTo( toolbarDiv ); |
40 | | - for ( tool in tools.main[group] ) { |
41 | | - // Creates tool |
42 | | - var toolImg = $( '<img />' ); |
43 | | - // Appends tool to group |
44 | | - toolImg.appendTo( groupDiv ); |
45 | | - // Customizes the tool |
46 | | - toolImg.attr({ |
47 | | - src: imagePath + tools.main[group][tool].icon, |
48 | | - alt: messages[group + '-' + tool], |
49 | | - title: messages[group + '-' + tool] |
50 | | - }); |
51 | | - // Sets button action |
52 | | - toolImg.click( tools.main[group][tool].action ); |
| 124 | + for ( group in section.groups ) { |
| 125 | + // Appends group |
| 126 | + var groupDiv = $( '<div />' ) |
| 127 | + .attr( 'class', 'group' ) |
| 128 | + .appendTo( $(this) ); |
| 129 | + // Creates generic action |
| 130 | + var action = function() { |
| 131 | + $(this).useTool( |
| 132 | + $(this).data( 'context' ).tool, |
| 133 | + $(this).data( 'context' ).textbox |
| 134 | + ); |
| 135 | + }; |
| 136 | + // Loops over each tool |
| 137 | + for ( tool in section.groups[group] ) { |
| 138 | + // Creates context for use in action |
| 139 | + var context = { 'tool': section.groups[group][tool], 'textbox': textbox }; |
| 140 | + // Creates the label of the tool |
| 141 | + var label = ( section.groups[group][tool].label || gM( section.groups[group][tool].labelMsg ) ); |
| 142 | + switch ( section.groups[group][tool].type ) { |
| 143 | + case 'button': |
| 144 | + // Appends button |
| 145 | + groupDiv.append( |
| 146 | + $( '<img />' ) |
| 147 | + .attr( { |
| 148 | + src: imagePath + section.groups[group][tool].icon, |
| 149 | + alt: label, |
| 150 | + title: label |
| 151 | + } ) |
| 152 | + .data( 'context', context ) |
| 153 | + .click( action ) |
| 154 | + ); |
| 155 | + break; |
| 156 | + case 'select': |
| 157 | + // Appends select |
| 158 | + var selectDiv = $( '<select />' ) |
| 159 | + .data( 'context', context ) |
| 160 | + .change( action ) |
| 161 | + .append( |
| 162 | + $( '<option />' ) .text( label ) |
| 163 | + ) |
| 164 | + .appendTo( groupDiv ); |
| 165 | + // Appends options |
| 166 | + for ( item in section.groups[group][tool].list ) { |
| 167 | + selectDiv.append( |
| 168 | + $( '<option/>' ) |
| 169 | + .text( ( section.groups[group][tool].list[item].label || gM( section.groups[group][tool].list[item].labelMsg ) ) ) |
| 170 | + .attr( 'value', item ) |
| 171 | + ); |
| 172 | + } |
| 173 | + break; |
| 174 | + default: break; |
| 175 | + } |
53 | 176 | } |
54 | 177 | } |
55 | | - } |
56 | | - } |
57 | | - |
58 | | - /** |
59 | | - * Adds a tool to the toolbar |
60 | | - * @param {String} section ID of section to add tool to |
61 | | - * @param {String} group ID of group to add tool to |
62 | | - * @param {String} tool ID of tool to add |
63 | | - * @param {Object} configuration Object of configuration for tool |
64 | | - */ |
65 | | - this.addTool = function( section, group, tool, configuration ) { |
66 | | - // Checks if the section is valid |
67 | | - if ( section in tools ) { |
68 | | - // Checks if the group doesn't exist in the section |
69 | | - if ( !( group in tools[section] ) ) { |
70 | | - // Adds the group to the section |
71 | | - tools[section][group] = {}; |
| 178 | + }, |
| 179 | + /** |
| 180 | + * Performs action on a textbox using a tool |
| 181 | + * @param {Object} tool |
| 182 | + * @param {Object} textbox |
| 183 | + */ |
| 184 | + useTool: function( tool, textbox ) { |
| 185 | + function performAction( action, textbox ) { |
| 186 | + switch ( action.type) { |
| 187 | + case 'encapsulate': |
| 188 | + var parts = { 'pre': '', 'peri': '', 'post': '' }; |
| 189 | + for ( part in parts ) { |
| 190 | + if ( part + 'Msg' in action.options ) { |
| 191 | + parts[part] = gM( action.options[part + 'Msg'], ( action.options[part] || null ) ); |
| 192 | + } else { |
| 193 | + parts[part] = ( action.options[part] || '' ) |
| 194 | + } |
| 195 | + } |
| 196 | + textbox.encapsulateSelection( parts.pre, parts.peri, parts.post ); |
| 197 | + break; |
| 198 | + default: break; |
| 199 | + } |
72 | 200 | } |
73 | | - // Checks if the tool doesn't exist in the group |
74 | | - if ( !( tool in tools[section][group] ) ) { |
75 | | - // Adds tool and configuration to group |
76 | | - tools[section][group][tool] = configuration; |
| 201 | + switch ( tool.type ) { |
| 202 | + case 'button': |
| 203 | + performAction( tool.action, textbox ); |
| 204 | + break; |
| 205 | + case 'select': |
| 206 | + if ( $(this).val() in tool.list ) { |
| 207 | + performAction( tool.list[$(this).val()].action, textbox ); |
| 208 | + } |
| 209 | + $(this).find(":selected").attr( 'selected', false ); |
| 210 | + $(this).find(":first").attr( 'selected', true ); |
| 211 | + break; |
| 212 | + default: break; |
77 | 213 | } |
78 | 214 | } |
79 | | - } |
80 | | - |
81 | | - /** |
82 | | - * Sets several user interface messages |
83 | | - * @param {Object} messageList List of key/value pairs of messages |
84 | | - */ |
85 | | - this.setMessages = function( messageList ) { |
86 | | - for ( messageItem in messageList ) { |
87 | | - messages[messageItem] = messageList[messageItem]; |
88 | | - } |
89 | | - } |
90 | | - |
91 | | - /** |
92 | | - * Sets a user interface message |
93 | | - * @param {String} key Key of message |
94 | | - * @param {String} value Value of message |
95 | | - */ |
96 | | - this.setMessage = function( key, value ) { |
97 | | - messages[key] = value; |
98 | | - } |
99 | | - |
100 | | - /** |
101 | | - * Gets a user interface message |
102 | | - * @param {String} key Key of message |
103 | | - */ |
104 | | - this.getMessage = function( key ) { |
105 | | - if ( key in messages ) { |
106 | | - return messages[key]; |
107 | | - } else { |
108 | | - return key; |
109 | | - } |
110 | | - } |
111 | | - |
112 | | - /** |
113 | | - * Performs the action associated with a tool |
114 | | - * @param {String} section ID of section of tool to use |
115 | | - * @param {String} group ID of group of tool to use |
116 | | - * @param {String} tool ID of tool to use |
117 | | - */ |
118 | | - this.useTool = function( section, group, tool ) { |
119 | | - // Checks if the tool exists |
120 | | - if ( tool in tools[section][group] ) { |
121 | | - // Adds tool and configuration to group |
122 | | - tools[section][group][tool].action(); |
123 | | - } |
124 | | - } |
125 | | -} |
126 | | - |
127 | | -// Creates global toolbar object |
128 | | -var editToolbar = new EditToolbar( '#edittoolbar' ); |
129 | | -// Executes function when document is ready |
| 215 | + }); |
| 216 | +})(jQuery); |
| 217 | +/** |
| 218 | + * This initializes an edit toolbar on div#edittoolbar and connects it to |
| 219 | + * textarea#wpTextbox1 - which needs to be done after the document is loaded. |
| 220 | + */ |
130 | 221 | $( document ).ready( function() { |
131 | | - // Initializes edit toolbar |
132 | | - editToolbar.initialize(); |
| 222 | + $( 'div#edittoolbar' ).toolbar( $( 'textarea#wpTextbox1' ), editToolbarConfiguration ); |
133 | 223 | }); |
134 | | - |
135 | 224 | /** |
136 | | - * This is a problem for internationalization - so clearly this will be moved |
137 | | - * or restructured at some point. |
| 225 | + * This enormous structure is what makes the toolbar what it is. Customization |
| 226 | + * of this structure prior to the document being ready and thus executing the |
| 227 | + * initialization procedure for the toolbar will result in a custom toolbar. |
138 | 228 | */ |
139 | | -// Adds tools to toolbar |
140 | | -editToolbar.addTool( |
141 | | - 'main', 'format', 'bold', |
142 | | - { |
143 | | - icon: 'format-bold.png', |
144 | | - action: function() { |
145 | | - $( '#wpTextbox1' ).encapsulateSelection( |
146 | | - "'''", null, editToolbar.getMessage( 'format-bold-example' ) |
147 | | - ); |
148 | | - return false; |
| 229 | +var editToolbarConfiguration = { |
| 230 | + // Main section |
| 231 | + 'main': { |
| 232 | + groups: { |
| 233 | + 'format': { |
| 234 | + 'bold': { |
| 235 | + labelMsg: 'edittoolbar-format-bold', |
| 236 | + type: 'button', |
| 237 | + icon: 'format-bold.png', |
| 238 | + action: { |
| 239 | + type: 'encapsulate', |
| 240 | + options: { |
| 241 | + pre: "'''", |
| 242 | + periMsg: 'edittoolbar-format-bold-example', |
| 243 | + post: "'''" |
| 244 | + } |
| 245 | + } |
| 246 | + }, |
| 247 | + 'italic': { |
| 248 | + section: 'main', |
| 249 | + group: 'format', |
| 250 | + id: 'italic', |
| 251 | + labelMsg: 'edittoolbar-format-italic', |
| 252 | + type: 'button', |
| 253 | + icon: 'format-italic.png', |
| 254 | + action: { |
| 255 | + type: 'encapsulate', |
| 256 | + options: { |
| 257 | + pre: "''", |
| 258 | + periMsg: 'edittoolbar-format-italic-example', |
| 259 | + post: "''" |
| 260 | + } |
| 261 | + } |
| 262 | + } |
| 263 | + }, |
| 264 | + 'insert': { |
| 265 | + 'xlink': { |
| 266 | + labelMsg: 'edittoolbar-insert-xlink', |
| 267 | + type: 'button', |
| 268 | + icon: 'insert-xlink.png', |
| 269 | + action: { |
| 270 | + type: 'encapsulate', |
| 271 | + options: { |
| 272 | + pre: "[", |
| 273 | + periMsg: 'edittoolbar-insert-xlink-example', |
| 274 | + post: "]" |
| 275 | + } |
| 276 | + } |
| 277 | + }, |
| 278 | + 'ilink': { |
| 279 | + labelMsg: 'edittoolbar-insert-ilink', |
| 280 | + type: 'button', |
| 281 | + icon: 'insert-ilink.png', |
| 282 | + action: { |
| 283 | + type: 'encapsulate', |
| 284 | + options: { |
| 285 | + pre: "[[", |
| 286 | + periMsg: 'edittoolbar-insert-ilink-example', |
| 287 | + post: "]]" |
| 288 | + } |
| 289 | + } |
| 290 | + }, |
| 291 | + 'file': { |
| 292 | + labelMsg: 'edittoolbar-insert-file', |
| 293 | + type: 'button', |
| 294 | + icon: 'insert-file.png', |
| 295 | + action: { |
| 296 | + type: 'encapsulate', |
| 297 | + options: { |
| 298 | + pre: "[[", |
| 299 | + preMsg: 'edittoolbar-insert-file-pre', |
| 300 | + periMsg: 'edittoolbar-insert-file-example', |
| 301 | + post: "]]" |
| 302 | + } |
| 303 | + } |
| 304 | + }, |
| 305 | + 'reference': { |
| 306 | + labelMsg: 'edittoolbar-insert-reference', |
| 307 | + type: 'button', |
| 308 | + icon: 'insert-reference.png', |
| 309 | + action: { |
| 310 | + type: 'encapsulate', |
| 311 | + options: { |
| 312 | + pre: "<ref>", |
| 313 | + periMsg: 'edittoolbar-insert-reference-example', |
| 314 | + post: "</ref>" |
| 315 | + } |
| 316 | + } |
| 317 | + } |
| 318 | + } |
149 | 319 | } |
150 | | - } |
151 | | -); |
152 | | -editToolbar.addTool( |
153 | | - 'main', 'format', 'italic', |
154 | | - { |
155 | | - icon: 'format-italic.png', |
156 | | - action: function() { |
157 | | - $( '#wpTextbox1' ).encapsulateSelection( |
158 | | - "''", null, editToolbar.getMessage( 'format-italic-example' ) |
159 | | - ); |
160 | | - return false; |
| 320 | + }, |
| 321 | + // Format section |
| 322 | + 'format': { |
| 323 | + labelMsg: 'edittoolbar-section-format', |
| 324 | + groups: { |
| 325 | + 'list': { |
| 326 | + 'ulist': { |
| 327 | + labelMsg: 'edittoolbar-format-ulist', |
| 328 | + type: 'button', |
| 329 | + icon: 'format-ulist.png', |
| 330 | + action: { |
| 331 | + type: 'encapsulate', |
| 332 | + options: { |
| 333 | + pre: "* ", |
| 334 | + periMsg: 'edittoolbar-format-ulist-example', |
| 335 | + post: "" |
| 336 | + } |
| 337 | + } |
| 338 | + }, |
| 339 | + 'olist': { |
| 340 | + labelMsg: 'edittoolbar-format-olist', |
| 341 | + type: 'button', |
| 342 | + icon: 'format-olist.png', |
| 343 | + action: { |
| 344 | + type: 'encapsulate', |
| 345 | + options: { |
| 346 | + pre: "# ", |
| 347 | + periMsg: 'edittoolbar-format-olist-example', |
| 348 | + post: "" |
| 349 | + } |
| 350 | + } |
| 351 | + } |
| 352 | + }, |
| 353 | + 'heading': { |
| 354 | + 'heading': { |
| 355 | + labelMsg: 'edittoolbar-format-heading', |
| 356 | + type: 'select', |
| 357 | + list: { |
| 358 | + 'heading-1' : { |
| 359 | + labelMsg: 'edittoolbar-format-heading-1', |
| 360 | + action: { |
| 361 | + type: 'encapsulate', |
| 362 | + options: { |
| 363 | + pre: "=", |
| 364 | + periMsg: 'edittoolbar-format-heading-example', |
| 365 | + post: "=" |
| 366 | + } |
| 367 | + } |
| 368 | + }, |
| 369 | + 'heading-2' : { |
| 370 | + labelMsg: 'edittoolbar-format-heading-2', |
| 371 | + action: { |
| 372 | + type: 'encapsulate', |
| 373 | + options: { |
| 374 | + pre: "==", |
| 375 | + periMsg: 'edittoolbar-format-heading-example', |
| 376 | + post: "==" |
| 377 | + } |
| 378 | + } |
| 379 | + }, |
| 380 | + 'heading-3' : { |
| 381 | + labelMsg: 'edittoolbar-format-heading-3', |
| 382 | + action: { |
| 383 | + type: 'encapsulate', |
| 384 | + options: { |
| 385 | + pre: "===", |
| 386 | + periMsg: 'edittoolbar-format-heading-example', |
| 387 | + post: "===" |
| 388 | + } |
| 389 | + } |
| 390 | + }, |
| 391 | + 'heading-4' : { |
| 392 | + labelMsg: 'edittoolbar-format-heading-4', |
| 393 | + action: { |
| 394 | + type: 'encapsulate', |
| 395 | + options: { |
| 396 | + pre: "====", |
| 397 | + periMsg: 'edittoolbar-format-heading-example', |
| 398 | + post: "====" |
| 399 | + } |
| 400 | + } |
| 401 | + }, |
| 402 | + 'heading-5' : { |
| 403 | + labelMsg: 'edittoolbar-format-heading-5', |
| 404 | + action: { |
| 405 | + type: 'encapsulate', |
| 406 | + options: { |
| 407 | + pre: "=====", |
| 408 | + periMsg: 'edittoolbar-format-heading-example', |
| 409 | + post: "=====" |
| 410 | + } |
| 411 | + } |
| 412 | + } |
| 413 | + } |
| 414 | + } |
| 415 | + }, |
| 416 | + 'size': { |
| 417 | + 'superscript': { |
| 418 | + labelMsg: 'edittoolbar-format-superscript', |
| 419 | + type: 'button', |
| 420 | + icon: 'format-superscript.png', |
| 421 | + action: { |
| 422 | + type: 'encapsulate', |
| 423 | + options: { |
| 424 | + pre: "<super>", |
| 425 | + periMsg: 'edittoolbar-format-superscript-example', |
| 426 | + post: "</super>" |
| 427 | + } |
| 428 | + } |
| 429 | + }, |
| 430 | + 'subscript': { |
| 431 | + labelMsg: 'edittoolbar-format-subscript', |
| 432 | + type: 'button', |
| 433 | + icon: 'format-subscript.png', |
| 434 | + action: { |
| 435 | + type: 'encapsulate', |
| 436 | + options: { |
| 437 | + pre: "<sub>", |
| 438 | + periMsg: 'edittoolbar-format-subscript-example', |
| 439 | + post: "</sub>" |
| 440 | + } |
| 441 | + } |
| 442 | + }, |
| 443 | + 'big': { |
| 444 | + labelMsg: 'edittoolbar-format-big', |
| 445 | + type: 'button', |
| 446 | + icon: 'format-big.png', |
| 447 | + action: { |
| 448 | + type: 'encapsulate', |
| 449 | + options: { |
| 450 | + pre: "<big>", |
| 451 | + periMsg: 'edittoolbar-format-big-example', |
| 452 | + post: "</big>" |
| 453 | + } |
| 454 | + } |
| 455 | + }, |
| 456 | + 'small': { |
| 457 | + labelMsg: 'edittoolbar-format-small', |
| 458 | + type: 'button', |
| 459 | + icon: 'format-small.png', |
| 460 | + action: { |
| 461 | + type: 'encapsulate', |
| 462 | + options: { |
| 463 | + pre: "<small>", |
| 464 | + periMsg: 'edittoolbar-format-small-example', |
| 465 | + post: "</small>" |
| 466 | + } |
| 467 | + } |
| 468 | + } |
| 469 | + } |
161 | 470 | } |
162 | 471 | } |
163 | | -); |
164 | | -editToolbar.addTool( |
165 | | - 'main', 'insert', 'ilink', |
166 | | - { |
167 | | - icon: 'insert-ilink.png', |
168 | | - action: function() { |
169 | | - $( '#wpTextbox1' ).encapsulateSelection( |
170 | | - '[[', ']]', editToolbar.getMessage( 'insert-ilink-example' ) |
171 | | - ); |
172 | | - return false; |
173 | | - } |
174 | | - } |
175 | | -); |
176 | | -editToolbar.addTool( |
177 | | - 'main', 'insert', 'xlink', |
178 | | - { |
179 | | - icon: 'insert-xlink.png', |
180 | | - action: function() { |
181 | | - $( '#wpTextbox1' ).encapsulateSelection( |
182 | | - '[', ']', editToolbar.getMessage( 'insert-xlink-example' ) |
183 | | - ); |
184 | | - return false; |
185 | | - } |
186 | | - } |
187 | | -); |
188 | | -editToolbar.addTool( |
189 | | - 'main', 'insert', 'image', |
190 | | - { |
191 | | - icon: 'insert-image.png', |
192 | | - action: function() { |
193 | | - $( '#wpTextbox1' ).encapsulateSelection( |
194 | | - '[[File:', ']]', editToolbar.getMessage( 'insert-image-example' ) |
195 | | - ); |
196 | | - return false; |
197 | | - } |
198 | | - } |
199 | | -); |
200 | | -editToolbar.addTool( |
201 | | - 'main', 'insert', 'reference', |
202 | | - { |
203 | | - icon: 'insert-reference.png', |
204 | | - action: function() { |
205 | | - $( '#wpTextbox1' ).encapsulateSelection( |
206 | | - '<ref>', '</ref>', editToolbar.getMessage( 'insert-reference-example' ) |
207 | | - ); |
208 | | - return false; |
209 | | - } |
210 | | - } |
211 | | -); |
\ No newline at end of file |
| 472 | +}; |
\ No newline at end of file |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/images/insert-image.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-superscript.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-superscript.png |
___________________________________________________________________ |
Added: svn:mime-type |
212 | 473 | + application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-small.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-small.png |
___________________________________________________________________ |
Added: svn:mime-type |
213 | 474 | + application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/images/arrow-down.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UsabilityInitiative/EditToolbar/images/arrow-down.png |
___________________________________________________________________ |
Added: svn:mime-type |
214 | 475 | + application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-olist.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-olist.png |
___________________________________________________________________ |
Added: svn:mime-type |
215 | 476 | + application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-subscript.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-subscript.png |
___________________________________________________________________ |
Added: svn:mime-type |
216 | 477 | + application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/images/arrow-right.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UsabilityInitiative/EditToolbar/images/arrow-right.png |
___________________________________________________________________ |
Added: svn:mime-type |
217 | 478 | + application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-ulist.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-ulist.png |
___________________________________________________________________ |
Added: svn:mime-type |
218 | 479 | + application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-big.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UsabilityInitiative/EditToolbar/images/format-big.png |
___________________________________________________________________ |
Added: svn:mime-type |
219 | 480 | + application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/Resources/jquery.textSelection.js |
— | — | @@ -1,54 +1,82 @@ |
2 | 2 | /* |
3 | | - * Functions getSelection and replaceSelection based on: |
4 | | - * (c) 2006 Alex Brem <alex@0xab.cd> - http://blog.0xab.cd |
5 | | - * All other code: |
6 | | - * (c) 2009 Wikimedia Foundation (GPLv2) - http://www.wikimedia.org |
| 3 | + * Ported from skins/common/edit.js by Trevor Parscal |
| 4 | + * (c) 2009 Wikimedia Foundation (GPLv2) - http://www.wikimedia.org |
7 | 5 | */ |
8 | 6 | (function($) { |
9 | 7 | $.fn.extend({ |
10 | | - getSelection: function() { |
| 8 | + encapsulateSelection: function( pre, peri, post ) { |
11 | 9 | var e = this.jquery ? this[0] : this; |
12 | | - if ( 'selectionStart' in e ) { |
13 | | - /* Mozilla / DOM 3.0 */ |
14 | | - var l = e.selectionEnd - e.selectionStart; |
15 | | - return { start: e.selectionStart, end: e.selectionEnd, length: l, text: e.value.substr( e.selectionStart, l ) }; |
16 | | - } else if ( document.selection ) { |
17 | | - /* Internet Explorer */ |
| 10 | + /** |
| 11 | + * CLEAN THIS UP PLEASE! |
| 12 | + */ |
| 13 | + var selText; |
| 14 | + var isSample = false; |
| 15 | + if (document.selection && document.selection.createRange) { // IE/Opera |
| 16 | + |
| 17 | + //save window scroll position |
| 18 | + if (document.documentElement && document.documentElement.scrollTop) |
| 19 | + var winScroll = document.documentElement.scrollTop |
| 20 | + else if (document.body) |
| 21 | + var winScroll = document.body.scrollTop; |
| 22 | + //get current selection |
18 | 23 | e.focus(); |
19 | | - var r = document.selection.createRange(); |
20 | | - if (r == null) { |
21 | | - return { start: 0, end: e.value.length, length: 0, text: null } |
| 24 | + var range = document.selection.createRange(); |
| 25 | + selText = range.text; |
| 26 | + //insert tags |
| 27 | + checkSelectedText(); |
| 28 | + range.text = pre + selText + post; |
| 29 | + //mark sample text as selected |
| 30 | + if (isSample && range.moveStart) { |
| 31 | + if (window.opera) |
| 32 | + post = post.replace(/\n/g,''); |
| 33 | + range.moveStart('character', - post.length - selText.length); |
| 34 | + range.moveEnd('character', - post.length); |
22 | 35 | } |
23 | | - var re = e.createTextRange(); |
24 | | - var rc = re.duplicate(); |
25 | | - re.moveToBookmark( r.getBookmark() ); |
26 | | - rc.setEndPoint( 'EndToStart', re ); |
27 | | - return { start: rc.text.length, end: rc.text.length + r.text.length, length: r.text.length, text: r.text }; |
28 | | - } else { |
29 | | - /* Browser not supported */ |
30 | | - return { start: 0, end: e.value.length, length: 0, text: null }; |
31 | | - } |
32 | | - }, |
33 | | - replaceSelection: function() { |
34 | | - var e = this.jquery ? this[0] : this; |
35 | | - var text = arguments[0] || ''; |
36 | | - if ( 'selectionStart' in e ) { |
37 | | - /* Mozilla / DOM 3.0 */ |
38 | | - e.value = e.value.substr( 0, e.selectionStart ) + text + e.value.substr( e.selectionEnd, e.value.length ); |
39 | | - } else if ( document.selection ) { |
40 | | - /* Internet Explorer */ |
| 36 | + range.select(); |
| 37 | + //restore window scroll position |
| 38 | + if (document.documentElement && document.documentElement.scrollTop) |
| 39 | + document.documentElement.scrollTop = winScroll |
| 40 | + else if (document.body) |
| 41 | + document.body.scrollTop = winScroll; |
| 42 | + |
| 43 | + } else if (e.selectionStart || e.selectionStart == '0') { // Mozilla |
| 44 | + |
| 45 | + //save textarea scroll position |
| 46 | + var textScroll = e.scrollTop; |
| 47 | + //get current selection |
41 | 48 | e.focus(); |
42 | | - document.selection.createRange().text = text; |
43 | | - } else { |
44 | | - /* Browser not supported */ |
45 | | - e.value += text; |
| 49 | + var startPos = e.selectionStart; |
| 50 | + var endPos = e.selectionEnd; |
| 51 | + selText = e.value.substring(startPos, endPos); |
| 52 | + //insert tags |
| 53 | + checkSelectedText(); |
| 54 | + e.value = e.value.substring(0, startPos) |
| 55 | + + pre + selText + post |
| 56 | + + e.value.substring(endPos, e.value.length); |
| 57 | + //set new selection |
| 58 | + if (isSample) { |
| 59 | + e.selectionStart = startPos + pre.length; |
| 60 | + e.selectionEnd = startPos + pre.length + selText.length; |
| 61 | + } else { |
| 62 | + e.selectionStart = startPos + pre.length + selText.length + post.length; |
| 63 | + e.selectionEnd = e.selectionStart; |
| 64 | + } |
| 65 | + //restore textarea scroll position |
| 66 | + e.scrollTop = textScroll; |
46 | 67 | } |
47 | | - return this; |
48 | | - }, |
49 | | - encapsulateSelection: function( pre, post, insert ) { |
50 | | - var obj = $( this ); |
51 | | - obj.replaceSelection( ( pre || '' ) + ( obj.getSelection().text || insert ) + ( post || pre || '' ) ); |
52 | | - return this; |
53 | | - } |
| 68 | + // Checks if the selected text is the same as the insert text |
| 69 | + function checkSelectedText(){ |
| 70 | + if (!selText) { |
| 71 | + selText = peri; |
| 72 | + isSample = true; |
| 73 | + } else if (selText.charAt(selText.length - 1) == ' ') { //exclude ending space char |
| 74 | + selText = selText.substring(0, selText.length - 1); |
| 75 | + post += ' ' |
| 76 | + } |
| 77 | + } |
| 78 | + /** |
| 79 | + * /CLEAN THIS UP PLEASE! |
| 80 | + */ |
| 81 | + } |
54 | 82 | }); |
55 | | -})(jQuery); |
\ No newline at end of file |
| 83 | +})(jQuery); |