Added experimental integration of message options in editor containers
authorAlexander Ebert <ebert@woltlab.com>
Sat, 5 Jul 2014 17:41:07 +0000 (19:41 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Sat, 5 Jul 2014 17:41:07 +0000 (19:41 +0200)
13 files changed:
com.woltlab.wcf/coreObject.xml
com.woltlab.wcf/templates/__messageFormSmilies.tpl
com.woltlab.wcf/templates/messageFormAttachments.tpl
com.woltlab.wcf/templates/messageFormSmilies.tpl
com.woltlab.wcf/templates/messageFormTabs.tpl
com.woltlab.wcf/templates/wysiwyg.tpl
com.woltlab.wcf/templates/wysiwygToolbar.tpl
wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js
wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js
wcfsetup/install/files/js/3rdParty/redactor/plugins/woptions.js
wcfsetup/install/files/js/3rdParty/redactor/plugins/wupload.js
wcfsetup/install/files/js/WCF.Message.js
wcfsetup/install/files/lib/data/smiley/category/SmileyCategory.class.php

index 634629004e1b36dd2c90258ff54e511397845465..fec7b8fbd2f7ffef7b556065699dd04852cfe433 100644 (file)
@@ -57,5 +57,9 @@
                <coreobject>
                        <objectname><![CDATA[wcf\system\ad\AdHandler]]></objectname>
                </coreobject>
+               
+               <coreobject>
+                       <objectname><![CDATA[wcf\data\smiley\SmileyCache]]></objectname>
+               </coreobject>
        </import>
 </data>
index a4204abebad638b61234debc7f0714339637f597..4a9ac6af4b04e5994534c19d18915e6cea7dd4a4 100644 (file)
@@ -1,5 +1,5 @@
 <ul class="smileyList">
        {foreach from=$smilies item=smiley}
-               <li><a title="{lang}{$smiley->smileyTitle}{/lang}" class="jsTooltip jsSmiley" data-smiley-code="{$smiley->smileyCode}" data-smiley-path="{$smiley->smileyPath}"><img src="{$smiley->getURL()}" alt="{$smiley->smileyCode}" /></a></li>
+               <li><a title="{lang}{$smiley->smileyTitle}{/lang}" class="jsTooltip jsSmiley" data-smiley-code="{$smiley->smileyCode}" data-smiley-path="{$smiley->getURL()}"><img src="{$smiley->getURL()}" alt="{$smiley->smileyCode}" /></a></li>
        {/foreach}
 </ul>
\ No newline at end of file
index 7e95eea05756fe23e802d0e2d184f4397cbd38cd..07e5105a4a7b49d442906aff3b04b52b988bd278 100644 (file)
@@ -1,4 +1,4 @@
-<div id="attachments" class="jsOnly formAttachmentContent tabMenuContent container containerPadding">
+<div class="jsOnly formAttachmentContent container containerPadding" id="attachments_{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if}">
        <ul class="formAttachmentList clearfix"{if !$attachmentHandler->getAttachmentList()|count} style="display: none"{/if}>
                {foreach from=$attachmentHandler->getAttachmentList() item=$attachment}
                        <li class="box48" data-object-id="{@$attachment->attachmentID}">
@@ -49,7 +49,7 @@
                        'wcf.attachment.delete.sure': '{lang}wcf.attachment.delete.sure{/lang}'
                });
                
-               new WCF.Attachment.Upload($('#attachments > dl > dd > div'), $('#attachments > ul'), '{@$attachmentObjectType}', '{@$attachmentObjectID}', '{$tmpHash|encodeJS}', '{@$attachmentParentObjectID}', {@$attachmentHandler->getMaxCount()}, '{@$wysiwygContainerID}');
+               new WCF.Attachment.Upload($('#attachments_{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if} > dl > dd > div'), $('#attachments_{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if} > ul'), '{@$attachmentObjectType}', '{@$attachmentObjectID}', '{$tmpHash|encodeJS}', '{@$attachmentParentObjectID}', {@$attachmentHandler->getMaxCount()}, '{@$wysiwygContainerID}');
                new WCF.Action.Delete('wcf\\data\\attachment\\AttachmentAction', '.formAttachmentList > li');
        });
        //]]>
index b7b3d30293d821f0225dfbba245d2e28db9eb866..f3abb1229d81c53bf1da911edb8de624de82b4c9 100644 (file)
@@ -3,17 +3,17 @@
        {foreach from=$smileyCategories item=smileyCategory}
                {assign var=__tabCount value=$__tabCount + 1}
                {assign var='__smileyAnchor' value='smilies-'|concat:$smileyCategory->categoryID}
-               <li><a href="{$__wcf->getAnchor($__smileyAnchor)}" data-smiley-category-id="{@$smileyCategory->categoryID}">{$smileyCategory->title|language}</a></li>
+               <li data-name="smilies-{@$smileyCategory->categoryID}" data-smiley-category-id="{@$smileyCategory->categoryID}"><a>{$smileyCategory->title|language}</a></li>
        {/foreach}
 {/capture}
 
-<div id="smilies" class="jsOnly smiliesContent tabMenuContent container containerPadding{if $__tabCount} tabMenuContainer{/if}">
+<div class="container containerPadding{if $__tabCount} messageTabMenu{/if}" data-preselect="true" data-collapsible="false" id="smilies-{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if}">
        {capture assign=__defaultSmilies}
                {include file='__messageFormSmilies' smilies=$defaultSmilies}
        {/capture}
        
        {if $__tabCount > 1}
-               <nav class="menu">
+               <nav class="jsOnly">
                        <ul>
                                {@$__categoryTabs}
                        </ul>
@@ -21,7 +21,7 @@
                
                {foreach from=$smileyCategories item=smileyCategory}
                        {if !$smileyCategory->isDisabled}
-                               <div id="smilies-{@$smileyCategory->categoryID}" class="hidden">
+                               <div id="smilies-{if $wysiwygSelector|isset}{$wysiwygSelector|encodeJS}{else}text{/if}-{@$smileyCategory->categoryID}">
                                        {if !$smileyCategory->categoryID}{@$__defaultSmilies}{/if}
                                </div>
                        {/if}
@@ -30,7 +30,7 @@
                <script data-relocate="true">
                        //<![CDATA[
                        $(function() {
-                               new WCF.Message.SmileyCategories();
+                               new WCF.Message.SmileyCategories('{if $wysiwygSelector|isset}{$wysiwygSelector|encodeJS}{else}text{/if}');
                        });
                        //]]>
                </script>
index be4e5f058912a984b66fd8c4a9f76edeab4f17d6..b09fef638a639f81a4cc256e799ba68dc94d6391 100644 (file)
@@ -1,10 +1,10 @@
-<div class="tabMenuContainer" data-active="{$activeTabMenuItem}" data-store="activeTabMenuItem">
-       <nav class="tabMenu jsOnly">
+<div class="messageTabMenu">
+       <nav class="messageTabMenuNavigation jsOnly">
                <ul>
-                       {if MODULE_SMILEY && $__wcf->getSession()->getPermission($permissionCanUseSmilies) && $smileyCategories|count}<li id="smiliesTab"><a href="{@$__wcf->getAnchor('smilies')}" title="{lang}wcf.message.smilies{/lang}">{lang}wcf.message.smilies{/lang}</a></li>{/if}
-                       {if MODULE_ATTACHMENT && $attachmentHandler !== null && $attachmentHandler->canUpload()}<li id="attachmentsTab"><a href="{@$__wcf->getAnchor('attachments')}" title="{lang}wcf.attachment.attachments{/lang}">{lang}wcf.attachment.attachments{/lang}</a></li>{/if}
-                       <li><a href="{@$__wcf->getAnchor('settings')}" title="{lang}wcf.message.settings{/lang}">{lang}wcf.message.settings{/lang}</a></li>
-                       {if $__showPoll|isset && $__showPoll}<li><a href="{@$__wcf->getAnchor('poll')}" title="{lang}wcf.poll.management{/lang}">{lang}wcf.poll.management{/lang}</a></li>{/if}
+                       {if MODULE_SMILEY && $__wcf->getSession()->getPermission($permissionCanUseSmilies) && $smileyCategories|count}<li data-name="smilies"><a>{lang}wcf.message.smilies{/lang}</a></li>{/if}
+                       {if MODULE_ATTACHMENT && $attachmentHandler !== null && $attachmentHandler->canUpload()}<li data-name="attachments"><a>{lang}wcf.attachment.attachments{/lang}</a></li>{/if}
+                       <li data-name="settings"><a>{lang}wcf.message.settings{/lang}</a></li>
+                       {if $__showPoll|isset && $__showPoll}<li data-name="poll"><a>{lang}wcf.poll.management{/lang}</a></li>{/if}
                        {event name='tabMenuTabs'}
                </ul>
        </nav>
        //<![CDATA[
        $(function() {
                if (!$.browser.redactor) $('#smiliesTab, #smilies').remove();
+               
+               $('.redactorMessageOptions > nav > ul > li > a').removeAttr('title');
+               
+               $('.messageTabMenu').messageTabMenu();
+               
+               /*$('.redactorMessageOptions > nav > ul > li > a').click(function() {
+                       var $a = $(this);
+                       var $p = $a.parent();
+                       var $h = $p.hasClass('active');
+                       
+                       $('.redactorMessageOptions > nav > ul > li').removeClass('active');
+                       $('.redactorMessageOptions > div, .redactorMessageOptions > fieldset').hide();
+                       
+                       if (!$h) {
+                               $p.addClass('active');
+                               $('#' + $a.prop('href').replace(/[^#]+#/, '')).show();
+                       }
+                       
+                       return false;
+               });
+               
+               $('.redactorMessageOptions > nav > ul > li > a:eq(0)').trigger('click');
+               
+               $('.redactorMessageOptions > div:eq(0) > nav > ul > li:eq(0)').addClass('active');*/
        });
        //]]>
 </script>
\ No newline at end of file
index 3444e3e1a9e993aaac2f6ac3ff78421f25382f36..a109ef820251042eb3f6a853c12ca4418eb14671 100644 (file)
@@ -29,6 +29,7 @@ $(function() {
        WCF.System.Dependency.Manager.setup($callbackIdentifier, function() {
                var $textarea = $('#' + $editorName);
                var $buttons = [ ];
+               var __wysiwygMessageOptions = (typeof $wysiwygMessageOptions === 'undefined') ? [ ] : $wysiwygMessageOptions;
                
                {include file='wysiwygToolbar'}
                
@@ -42,16 +43,22 @@ $(function() {
                        lang: '{@$__wcf->getLanguage()->getFixedLanguageCode()}',
                        minHeight: 200,
                        imageResizable: false,
-                       plugins: [ 'wutil',  'wmonkeypatch', 'wbutton', 'wbbcode',  'wfontcolor', 'wfontfamily', 'wfontsize' ],
+                       plugins: [ 'wutil',  'wmonkeypatch', 'wbutton', 'wbbcode',  'wfontcolor', 'wfontfamily', 'wfontsize', 'woptions' ],
                        wautosave: {
                                active: ($autosave) ? true : false,
                                key: ($autosave) ? '{@$__wcf->getAutosavePrefix()}_' + $autosave : '',
                                saveOnInit: {if !$errorField|empty}true{else}false{/if}
-                       }
+                       },
+                       wMessageOptions: [ ]
                };
                
                {if $wysiwygEnableUpload}
                        $config.plugins.push('wupload');
+                       $config.wMessageOptions.push({
+                               containerID: 'attachments',
+                               title: '{lang}wcf.attachment.attachments{/lang}',
+                               items: [ ]
+                       });
                        $config.wattachment = {
                                attachments: [ ],
                                maxCount: {@$attachmentHandler->getMaxCount()},
@@ -76,6 +83,22 @@ $(function() {
                
                {event name='javascriptInit'}
                
+               if (__wysiwygMessageOptions.length) {
+                       $config.wMessageOptions.push({
+                               containerID: 'settings',
+                               title: '{lang}wcf.message.settings{/lang}',
+                               items: __wysiwygMessageOptions
+                       });
+               }
+               
+               if (false && $.getLength(__REDACTOR_SMILIES)) {
+                       $config.wMessageOptions.push({
+                               containerID: 'smilies',
+                               title: '{lang}wcf.message.smilies{/lang}',
+                               items: [ ]
+                       });
+               }
+               
                $textarea.redactor($config);
        });
        
@@ -92,7 +115,8 @@ $(function() {
                        '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wfontsize.js?v={@$__wcfVersion}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wmonkeypatch.js?v={@$__wcfVersion}',
                        '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wutil.js?v={@$__wcfVersion}',
-                       '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wupload.js?v={@$__wcfVersion}'
+                       '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wupload.js?v={@$__wcfVersion}',
+                       '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/woptions.js?v={@$__wcfVersion}'
                {/if}
                {event name='javascriptFiles'}
        ], function() {
index 787729a2a2f85babddc3480eb4300fdd782f4317..5e9de08d8daccd79bf0a44001a2ce5b5bd348edb 100644 (file)
@@ -67,14 +67,6 @@ if ($buttons.length && $buttons[$buttons.length -1] != 'separator') {
        $buttons.push('table');
 {/if}
 
-{if $wysiwygEnableUpload}
-       $buttons.push('upload');
-{/if}
-
-{if MODULE_SMILEY && (!$permissionCanUseSmilies|isset || $__wcf->getSession()->getPermission($permissionCanUseSmilies)) && $defaultSmilies|isset && $defaultSmilies|count}
-       $buttons.push('smiley');
-{/if}
-
 if ($buttons.length && $buttons[$buttons.length -1] != 'separator') {
        $buttons.push('separator');
 }
index 4745e72e20adeeb2bb8d588154b962920be8d6f4..bfdf05cfe8fc7278d6114d1169eb3d9f28e7cfbf 100644 (file)
@@ -12,13 +12,13 @@ RedactorPlugins.wbbcode = {
         * Initializes the RedactorPlugins.wbbcode plugin.
         */
        init: function() {
-               var $dropdown = this._createSmileyDropdown();
+               /*var $dropdown = this._createSmileyDropdown();
                
                this.buttonReplace('smiley', 'wsmiley', 'Smiley', $.proxy(function(btnName, $button, btnObject, e) {
                        this.dropdownShow(e, btnName);
                }, this));
                this.buttonGet('wsmiley').data('dropdown', $dropdown);
-               this.buttonAwesome('wsmiley', 'fa-smile-o');
+               this.buttonAwesome('wsmiley', 'fa-smile-o');*/
                
                this.opts.initCallback = $.proxy(function() {
                        if (this.$source.val().length) {
index a0dc2820631e9e62936b6e5cdfcab56d5b4671df..a5e406f3a014be4c8932a5457acceb02a9165513 100644 (file)
@@ -58,6 +58,13 @@ RedactorPlugins.wmonkeypatch = {
                        $(document.body).css('overflow', false);
                };
                
+               var $mpDestroy = this.destroy;
+               this.destroy = function() {
+                       self.callback('destroy', false, { });
+                       
+                       $mpDestroy.call(self);
+               };
+               
                // handle indent/outdent
                var $mpButtonActiveObserver = this.buttonActiveObserver;
                this.buttonActiveObserver = function(e, btnName) {
index 6dc75715a44a5cf0316e07bf0f0511fe077341ce..bb2d8e53456c9cf5829ae9cd7db83f884a409453 100644 (file)
@@ -9,22 +9,22 @@ if (!RedactorPlugins) var RedactorPlugins = {};
  */
 RedactorPlugins.woptions = {
        /**
-        * list of message option elements
+        * list of message option elements grouped by instance
         * @var object<object>
         */
        _messageOptions: { },
        
        /**
-        * message option container
-        * @var jQuery
+        * message option container per instance
+        * @var object<jQuery>
         */
-       _messageOptionContainer: null,
+       _messageOptionContainer: { },
        
        /**
-        * navigation container
-        * @var jQuery
+        * navigation container per instance
+        * @var object<jQuery>
         */
-       _messageOptionNavigation: null,
+       _messageOptionNavigation: { },
        
        /**
         * Initializes the RedactorPlugins.woptions plugin.
@@ -35,22 +35,24 @@ RedactorPlugins.woptions = {
                        return;
                }
                
-               this._messageOptionContainer = $('<div id="redactorMessageOptions" class="redactorMessageOptions" />').appendTo(this.$box);
-               this._messageOptionNavigation = $('<nav><ul /></nav>').appendTo(this._messageOptionContainer).children('ul');
+               var $instanceID = this.$source.wcfIdentify();
+               this.$box.wrap('<div class="redactorContainer" />')
+               this._messageOptionContainer[$instanceID] = $('<div id="redactorMessageOptions" class="redactorMessageOptions" />').insertAfter(this.$box);
+               this._messageOptionNavigation[$instanceID] = $('<nav><ul /></nav>').appendTo(this._messageOptionContainer[$instanceID]).children('ul');
+               this._messageOptions[$instanceID] = { };
                
                for (var $i = 0; $i < $options.length; $i++) {
                        var $container = $options[$i];
                        
-                       var $listItem = $('<li><a>' + $container.title + '</a></li>').appendTo(this._messageOptionNavigation);
+                       var $listItem = $('<li><a>' + $container.title + '</a></li>').appendTo(this._messageOptionNavigation[$instanceID]);
                        $listItem.data('containerID', $container.containerID).click($.proxy(this._showMessageOptionContainer, this));
                        
-                       var $tabContainer = $('<div class="redactorMessageOptionContainer" id="redactorMessageOptions_' + $container.containerID + '" />').hide().appendTo(this._messageOptionContainer);
-                       
+                       var $tabContainer = $('<div class="redactorMessageOptionContainer redactorMessageOptions_' + $container.containerID + '" />').hide().appendTo(this._messageOptionContainer[$instanceID]);
                        for (var $j = 0; $j < $container.items.length; $j++) {
                                $($container.items[$j]).appendTo($tabContainer);
                        }
                        
-                       this._messageOptions[$container.containerID] = {
+                       this._messageOptions[$instanceID][$container.containerID] = {
                                container: $tabContainer,
                                listItem: $listItem
                        };
@@ -69,14 +71,16 @@ RedactorPlugins.woptions = {
         */
        _showMessageOptionContainer: function(event, containerID) {
                var $containerID = (event === null) ? containerID : $(event.currentTarget).data('containerID');
-               if (this._messageOptions[$containerID].listItem.hasClass('active')) {
-                       this._messageOptions[$containerID].listItem.removeClass('active');
-                       this._messageOptions[$containerID].container.hide();
+               var $instanceID = this.$source.wcfIdentify();
+               
+               if (this._messageOptions[$instanceID][$containerID].listItem.hasClass('active')) {
+                       this._messageOptions[$instanceID][$containerID].listItem.removeClass('active');
+                       this._messageOptions[$instanceID][$containerID].container.hide();
                        
                        return;
                }
                
-               $.each(this._messageOptions, function(containerID, elements) {
+               $.each(this._messageOptions[$instanceID], function(containerID, elements) {
                        if (containerID == $containerID) {
                                elements.listItem.addClass('active');
                                elements.container.show();
index c4b0c88d64bac05f00e98352a1e535da4f3f873b..209162dcc7d2eaf56374f143f1024e653c7a2713 100644 (file)
@@ -31,7 +31,7 @@ RedactorPlugins.wupload = {
         * Initializes the attachments user interface.
         */
        _initAttachments: function() {
-               this._attachmentsContainer = $('#redactorMessageOptions_attachments');
+               this._attachmentsContainer = this._messageOptions[this.$source.wcfIdentify()].attachments.container;
                var $attachmentList = $('<ul class="formAttachmentList clearfix" />').hide().appendTo(this._attachmentsContainer);
                $('<dl class="wide"><dt></dt><dd><div data-max-size="{@$attachmentHandler->getMaxSize()}"></div><small>' + WCF.String.unescapeHTML(WCF.Language.get('wcf.attachment.upload.limits')) + '</small></dd></dl>').appendTo(this._attachmentsContainer);
                
index d4de8dd23b62bce264ef031fe2c2c580d0a13ed5..ab69c071f134c420b458243335c4005111e387a3 100644 (file)
@@ -448,43 +448,65 @@ WCF.Message.SmileyCategories = Class.extend({
         */
        _proxy: null,
        
+       /**
+        * wysiwyg editor selector
+        * @var string
+        */
+       _wysiwygSelector: '',
+       
        /**
         * Initializes the smiley loader.
+        * 
+        * @param       string          wysiwygSelector
         */
-       init: function() {
-               this._cache = [ ];
+       init: function(wysiwygSelector) {
                this._proxy = new WCF.Action.Proxy({
                        success: $.proxy(this._success, this)
                });
+               this._wysiwygSelector = wysiwygSelector;
                
-               $('#smilies').on('wcftabsbeforeactivate', $.proxy(this._click, this));
+               $('#smilies-' + this._wysiwygSelector).on('messagetabmenushow', $.proxy(this._click, this));
                
                // handle onload
-               var self = this;
+               /*var self = this;
                new WCF.PeriodicalExecuter(function(pe) {
                        pe.stop();
                        
                        self._click({ }, { newTab: $('#smilies > .menu li.ui-state-active') });
-               }, 100);
+               }, 100);*/
        },
        
        /**
         * Handles tab menu clicks.
         * 
         * @param       object          event
-        * @param       object          ui
+        * @param       object          data
         */
-       _click: function(event, ui) {
-               var $categoryID = parseInt($(ui.newTab).children('a').data('smileyCategoryID'));
+       _click: function(event, data, c) {
+               var $categoryID = parseInt(data.activeTab.tab.data('smileyCategoryID'));
                
-               if ($categoryID && !WCF.inArray($categoryID, this._cache)) {
-                       this._proxy.setOption('data', {
-                               actionName: 'getSmilies',
-                               className: 'wcf\\data\\smiley\\category\\SmileyCategoryAction',
-                               objectIDs: [ $categoryID ]
-                       });
-                       this._proxy.sendRequest();
+               // ignore global category, will always be pre-loaded
+               if (!$categoryID) {
+                       return;
+               }
+               
+               // smilies have already been loaded for this tab, ignore
+               if (data.activeTab.container.children('ul.smileyList').length) {
+                       return;
                }
+               
+               // cache exists
+               if (this._cache[$categoryID] !== undefined) {
+                       data.activeTab.container.html(this._cache[$categoryID]);
+               }
+               
+               // load content
+               this._proxy.setOption('data', {
+                       actionName: 'getSmilies',
+                       className: 'wcf\\data\\smiley\\category\\SmileyCategoryAction',
+                       objectIDs: [ $categoryID ]
+               });
+               this._proxy.sendRequest();
        },
        
        /**
@@ -496,9 +518,9 @@ WCF.Message.SmileyCategories = Class.extend({
         */
        _success: function(data, textStatus, jqXHR) {
                var $categoryID = parseInt(data.returnValues.smileyCategoryID);
-               this._cache.push($categoryID);
+               this._cache[$categoryID] = data.returnValues.template;
                
-               $('#smilies-' + $categoryID).html(data.returnValues.template);
+               $('#smilies-' + this._wysiwygSelector + '-' + $categoryID).html(data.returnValues.template);
        }
 });
 
@@ -3375,3 +3397,188 @@ WCF.Message.UserMention = Class.extend({
                }
        }
 });
+
+/**
+ * Provides a specialized tab menu used for message options, integrates better into the editor.
+ */
+$.widget('wcf.messageTabMenu', {
+       /**
+        * list of existing tabs and their containers
+        * @var array<object>
+        */
+       _tabs: [ ],
+       
+       /**
+        * list of tab names and their corresponding index
+        * @var object<string>
+        */
+       _tabsByName: { },
+       
+       /**
+        * widget options
+        * @var object<mixed>
+        */
+       options: {
+               collapsible: true
+       },
+       
+       /**
+        * Creates the message tab menu.
+        */
+       _create: function() {
+               var $tabs = this.element.find('> nav > ul > li');
+               var $tabContainers = this.element.find('> div, > fieldset');
+               
+               if ($tabs.length != $tabContainers.length) {
+                       console.debug("[wcf.messageTabMenu] Amount of tabs does not equal amount of tab containers, aborting.");
+                       return;
+               }
+               
+               var $preselect = this.element.data('preselect');
+               this._tabs = [ ];
+               this._tabsByName = { };
+               for (var $i = 0; $i < $tabs.length; $i++) {
+                       var $tab = $($tabs[$i]);
+                       var $tabContainer = $($tabContainers[$i]);
+                       
+                       var $name = $tab.data('name');
+                       if ($name === undefined) {
+                               var $href = $tab.children('a').prop('href');
+                               if ($href !== undefined) {
+                                       if ($href.match(/#([a-zA-Z_-]+)$/)) {
+                                               $name = RegExp.$1;
+                                       }
+                               }
+                               
+                               if ($name === undefined) {
+                                       $name = $tab.wcfIdentify();
+                                       console.debug("[wcf.messageTabMenu] Missing name attribute, assuming generic ID '" + $name + "'");
+                               }
+                       }
+                       
+                       this._tabs.push({
+                               container: $tabContainer,
+                               name: $name,
+                               tab: $tab
+                       });
+                       this._tabsByName[$name] = $i;
+                       
+                       var $anchor = $tab.children('a').data('index', $i).click($.proxy(this._showTab, this));
+                       if ($preselect == $name) {
+                               $anchor.trigger('click');
+                       }
+               }
+               
+               if ($preselect === true) {
+                       // pick the first available tab
+                       this._tabs[0].tab.children('a').trigger('click');
+               }
+               
+               var $collapsible = this.element.data('collapsible');
+               if ($collapsible !== undefined) {
+                       this.options.collapsible = $collapsible;
+               }
+       },
+       
+       /**
+        * Destroys the message tab menu.
+        */
+       destroy: function() {
+               $.Widget.prototype.destroy.apply(this, arguments);
+               
+               this.element.remove();
+       },
+       
+       /**
+        * Shows a tab or collapses it if already open.
+        * 
+        * @param       object          event
+        * @param       integer         index
+        * @param       boolean         forceOpen
+        */
+       _showTab: function(event, index, forceOpen) {
+               var $index = (event === null) ? index : $(event.currentTarget).data('index');
+               forceOpen = (!this.options.collapsible || forceOpen === true) ? true : false;
+               
+               var $target = null;
+               for (var $i = 0; $i < this._tabs.length; $i++) {
+                       var $current = this._tabs[$i];
+                       
+                       if ($i == $index) {
+                               if (!$current.tab.hasClass('active')) {
+                                       $current.tab.addClass('active');
+                                       $current.container.addClass('active');
+                                       $target = $current;
+                                       
+                                       continue;
+                               }
+                               else if (forceOpen === true) {
+                                       continue;
+                               }
+                       }
+                       
+                       $current.tab.removeClass('active');
+                       $current.container.removeClass('active');
+               }
+               
+               if (event !== null) {
+                       event.preventDefault();
+                       event.stopPropagation();
+               }
+               
+               if ($target !== null) {
+                       this._trigger('show', { }, {
+                               activeTab: $target
+                       });
+               }
+       },
+       
+       /**
+        * Toggle a specific tab by either index or name property.
+        * 
+        * @param       mixed           index
+        * @param       boolean         forceOpen
+        */
+       showTab: function(index, forceOpen) {
+               if (!$.isNumeric(index)) {
+                       if (this._tabsByName[index] !== undefined) {
+                               index = this._tabsByName[index];
+                       }
+               }
+               
+               if (this._tabs[index] === undefined) {
+                       console.debug("[wcf.messageTabMenu] Cannot locate tab identified by '" + index + "'");
+                       return;
+               }
+               
+               this._showTab(null, index, forceOpen);
+       },
+       
+       /**
+        * Returns a tab by it's unique name.
+        * 
+        * @param       string          name
+        * @return      jQuery
+        */
+       getTab: function(name) {
+               if (this._tabsByName[name] !== undefined) {
+                       return this._tabs[this._tabsByName[name]].tab;
+               }
+               
+               return null;
+       },
+       
+       /**
+        * Returns a tab container by it's tab's unique name.
+        * 
+        * @param       string          name
+        * @return      jQuery
+        */
+       getContainer: function(name) {
+               if (this._tabsByName[name] !== undefined) {
+                       return this._tabs[this._tabsByName[name]].container;
+               }
+               
+               return null;
+       }
+});
index 173359fbffa3deae38784001a8bc76b0f9f43ce0..46f3cf474abd938a2bb6ed79a2081067fb10a684 100644 (file)
@@ -4,6 +4,7 @@ use wcf\data\category\AbstractDecoratedCategory;
 use wcf\data\smiley\SmileyCache;
 use wcf\data\ITraversableObject;
 use wcf\system\exception\SystemException;
+use wcf\system\WCF;
 
 /**
  * Represents a smiley category.
@@ -124,4 +125,13 @@ class SmileyCategory extends AbstractDecoratedCategory implements \Countable, IT
                        return null;
                }
        }
+       
+       /**
+        * Returns the category's name.
+        * 
+        * @return      string
+        */
+       public function __toString() {
+               return WCF::getLanguage()->get($this->title);
+       }
 }