diff --git a/src/mibew/libs/classes/Mibew/Button/Generator/AbstractGenerator.php b/src/mibew/libs/classes/Mibew/Button/Generator/AbstractGenerator.php
new file mode 100644
index 00000000..843cfcc0
--- /dev/null
+++ b/src/mibew/libs/classes/Mibew/Button/Generator/AbstractGenerator.php
@@ -0,0 +1,158 @@
+routeUrlGenerator = $routeUrlGenerator;
+        $this->options = $options;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOption($name, $value)
+    {
+        $this->options[$name] = $value;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOption($name, $default = false)
+    {
+        return isset($this->options[$name]) ? $this->options[$name] : $default;
+    }
+
+    /**
+     * Generates URL for the specified route.
+     *
+     * @param stirng $route The name of the route.
+     * @param array $parameters List of parameters that will be used for URL
+     *   generating.
+     * @return string The URL for the specified route.
+     */
+    protected function generateUrl($route, $parameters = array())
+    {
+        $generator = $this->routeUrlGenerator;
+
+        if (!$this->getOption('show_host')) {
+            return $generator->generate($route, $parameters);
+        }
+
+        return $this->getOption('force_secure')
+            ? $generator->generateSecure($route, $parameters, RouteUrlGeneratorInterface::ABSOLUTE_URL)
+            : $generator->generate($route, $parameters, RouteUrlGeneratorInterface::ABSOLUTE_URL);
+    }
+
+    /**
+     * Gets the URL of the chat start point.
+     *
+     * @return string
+     */
+    protected function getChatUrl()
+    {
+        $link_params = array();
+
+        if ($this->getOption('locale')) {
+            $link_params['locale'] = $this->getOption('locale');
+        }
+        if ($this->getOption('chat_style')) {
+            $link_params['style'] = $this->getOption('chat_style');
+        }
+        if ($this->getOption('group_id')) {
+            $link_params['group'] = $this->getOption('group_id');
+        }
+
+        return $this->generateUrl('chat_user_start', $link_params);
+    }
+
+    /**
+     * Gets the URL of the chat start point.
+     *
+     * The result is a JavaScript String with several additional dynamic
+     * parameters. It can be use only as a JS String.
+     *
+     * @return string
+     */
+    protected function getChatUrlForJs()
+    {
+        $url = str_replace('&', '&', $this->getChatUrl());
+        $modsecfix = $this->getOption('mod_security')
+            ? ".replace('http://','').replace('https://','')"
+            : '';
+
+        return "'" . $url
+            . ((strpos($url, '?') === false) ? '?' : '&')
+            . "url='+escape(document.location.href$modsecfix)+'&"
+            . "referrer='+escape(document.referrer$modsecfix)";
+    }
+
+    /**
+     * Gets the options string for the chat popup window.
+     *
+     * @return string
+     */
+    protected function getPopupOptions()
+    {
+        $style_name = $this->getOption('chat_style');
+
+        if (!$style_name) {
+            return "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,width=640,height=480,resizable=1";
+        }
+
+        $chat_style = new ChatStyle($style_name);
+        $chat_configurations = $chat_style->getConfigurations();
+
+        return $chat_configurations['chat']['window_params'];
+    }
+}
diff --git a/src/mibew/libs/classes/Mibew/Button/Generator/GeneratorInterface.php b/src/mibew/libs/classes/Mibew/Button/Generator/GeneratorInterface.php
new file mode 100644
index 00000000..0181cafe
--- /dev/null
+++ b/src/mibew/libs/classes/Mibew/Button/Generator/GeneratorInterface.php
@@ -0,0 +1,49 @@
+assetUrlGenerator = $assetUrlGenerator;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function generate()
+    {
+        $image_link_args = array(
+            'i' => $this->getOption('image'),
+            'lang' => $this->getOption('locale'),
+        );
+
+        if ($this->getOption('group_id')) {
+            $image_link_args['group'] = $this->getOption('group_id');
+        }
+
+        $image_url = str_replace(
+            '&',
+            '&',
+            $this->generateUrl('button', $image_link_args)
+        );
+        $message = "
";
+
+        $button = ""
+            . $this->getPopup($message)
+            . $this->getWidgetCode()
+            . "";
+
+        return $button;
+    }
+
+    /**
+     * Generates URL for the specified asset.
+     *
+     * @param stirng $asset The relative path of the asset.
+     * @return string The URL for the specified asset.
+     */
+    protected function generateAssetUrl($asset)
+    {
+        $generator = $this->assetUrlGenerator;
+
+        if (!$this->getOption('show_host')) {
+            return $generator->generate($asset);
+        }
+
+        return $this->getOption('force_secure')
+            ? $generator->generateSecure($asset, RouteUrlGeneratorInterface::ABSOLUTE_URL)
+            : $generator->generate($asset, RouteUrlGeneratorInterface::ABSOLUTE_URL);
+    }
+
+    /**
+     * Generates HTML coed for Mibew widget.
+     *
+     * @return string
+     */
+    protected function getWidgetCode()
+    {
+        if (!Settings::get('enabletracking')) {
+            return '';
+        }
+
+        $widget_data = array();
+
+        // Get actual invitation style instance
+        $style_name = $this->getOption('invitation_style')
+            ? $this->getOption('invitation_style')
+            : InvitationStyle::getCurrentStyle();
+        $style = new InvitationStyle($style_name);
+
+        // URL of file with additional CSS rules for invitation popup
+        $widget_data['inviteStyle'] = $this->generateAssetUrl(
+            $style->getFilesPath() . '/invite.css'
+        );
+
+        // Time between requests to the server in milliseconds
+        $widget_data['requestTimeout'] = Settings::get('updatefrequency_tracking') * 1000;
+
+        // URL for requests
+        $widget_data['requestURL'] = $this->generateUrl('widget_gateway');
+
+        // Locale for invitation
+        $widget_data['locale'] = $this->getOption('locale');
+
+        // Name of the cookie to track user. It is used if third-party cookie
+        // blocked
+        $widget_data['visitorCookieName'] = VISITOR_COOKIE_NAME;
+
+        // Build additional button code
+        return '
'
+            . ''
+            . '';
+    }
+}
diff --git a/src/mibew/libs/classes/Mibew/Button/Generator/OperatorCodeGenerator.php b/src/mibew/libs/classes/Mibew/Button/Generator/OperatorCodeGenerator.php
new file mode 100644
index 00000000..29c83be6
--- /dev/null
+++ b/src/mibew/libs/classes/Mibew/Button/Generator/OperatorCodeGenerator.php
@@ -0,0 +1,47 @@
+getChatUrlForJs();
+        $popup_options = $this->getPopupOptions();
+
+        $form_on_submit = "if(navigator.userAgent.toLowerCase().indexOf('opera') != -1 "
+            . "&& window.event.preventDefault) window.event.preventDefault();"
+            . "this.newWindow = window.open({$js_link} + '&operator_code=' "
+            . "+ document.getElementById('mibewOperatorCodeField').value, 'mibew', '{$popup_options}');"
+            . "this.newWindow.focus();this.newWindow.opener=window;return false;";
+
+        $temp = '';
+
+        return "" . $temp . "";
+    }
+}
diff --git a/src/mibew/libs/classes/Mibew/Button/Generator/TextGenerator.php b/src/mibew/libs/classes/Mibew/Button/Generator/TextGenerator.php
new file mode 100644
index 00000000..d8fb9faf
--- /dev/null
+++ b/src/mibew/libs/classes/Mibew/Button/Generator/TextGenerator.php
@@ -0,0 +1,56 @@
+"
+            . $this->getPopup($this->getOption('caption'))
+            . "";
+    }
+
+    /**
+     * Generates a markup for opening popup window with the chat.
+     *
+     * @return string HTML markup.
+     */
+    protected function getPopup($message)
+    {
+        $url = str_replace('&', '&', $this->getChatUrl());
+        $js_url = $this->getChatUrlForJs();
+        $options = $this->getPopupOptions();
+        $title = $this->getOption('title');
+
+        return "$message";
+    }
+}
diff --git a/src/mibew/libs/classes/Mibew/Controller/ButtonCodeController.php b/src/mibew/libs/classes/Mibew/Controller/ButtonCodeController.php
index c3c651cf..5269ccfa 100644
--- a/src/mibew/libs/classes/Mibew/Controller/ButtonCodeController.php
+++ b/src/mibew/libs/classes/Mibew/Controller/ButtonCodeController.php
@@ -19,13 +19,15 @@
 
 namespace Mibew\Controller;
 
+use Mibew\Button\Generator\ImageGenerator as ImageButtonGenerator;
+use Mibew\Button\Generator\OperatorCodeGenerator as OperatorCodeFieldGenerator;
+use Mibew\Button\Generator\TextGenerator as TextButtonGenerator;
 use Mibew\Http\Exception\BadRequestException;
 use Mibew\Settings;
 use Mibew\Style\ChatStyle;
 use Mibew\Style\InvitationStyle;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
 
 /**
  * Represents actions that are related with button code generation.
@@ -93,61 +95,56 @@ class ButtonCodeController extends AbstractController
 
         $operator_code = ($code_type == 'operator_code');
         $generate_button = ($code_type == 'button');
+        $button_generator_options = array(
+            'chat_style' => $style,
+            'group_id' => $group_id,
+            'show_host' => $show_host,
+            'force_secure' => $force_secure,
+            'mod_security' => $mod_security,
+        );
 
-        if ($generate_button) {
-            $disable_invitation = false;
-
+        if ($operator_code) {
+            $button_generator = new OperatorCodeFieldGenerator(
+                $this->getRouter(),
+                $button_generator_options
+            );
+        } elseif ($generate_button) {
+            // Make sure locale exists
             if (!$lang || !in_array($lang, $image_locales)) {
                 $lang = in_array(get_current_locale(), $image_locales)
                     ? get_current_locale()
                     : $image_locales[0];
             }
 
-            $file = MIBEW_FS_ROOT . "/locales/{$lang}/button/{$image}_on.png";
-            if (!is_readable($file)) {
-                // Fallback to .gif image
-                $file = MIBEW_FS_ROOT . "/locales/{$lang}/button/{$image}_on.gif";
-            }
-            $size = get_image_size($file);
-
-            $image_link_args = array(
-                'i' => $image,
-                'lang' => $lang,
+            $button_generator = new ImageButtonGenerator(
+                $this->getRouter(),
+                $this->getAssetUrlGenerator(),
+                $button_generator_options
             );
-            if ($group_id) {
-                $image_link_args['group'] = $group_id;
-            }
-            $host = ($force_secure ? 'https://' : 'http://') . $request->getHost();
-            $image_href = ($show_host ? $host : '')
-                . $this->generateUrl('button', $image_link_args, UrlGeneratorInterface::ABSOLUTE_PATH);
 
-            $message = get_image(htmlspecialchars($image_href), $size[0], $size[1]);
+            // Set generator-specific options
+            $button_generator->setOption('image', $image);
         } else {
-            $disable_invitation = true;
-
+            // Make sure locale exists
             if (!$lang || !in_array($lang, $locales_list)) {
                 $lang = in_array(get_current_locale(), $locales_list)
                     ? get_current_locale()
                     : $locales_list[0];
             }
 
-            $message = getlocal('Click to chat');
+            $button_generator = new TextButtonGenerator(
+                $this->getRouter(),
+                $button_generator_options
+            );
+
+            // Set generator-specific options
+            $button_generator->setOption('caption', getlocal('Click to chat'));
         }
 
-        $page['buttonCode'] = $this->generateButton(
-            $request,
-            '',
-            $lang,
-            $style,
-            $invitation_style,
-            $group_id,
-            $message,
-            $show_host,
-            $force_secure,
-            $mod_security,
-            $operator_code,
-            $disable_invitation
-        );
+        // Set verified locale code to a button generator
+        $button_generator->setOption('locale', $lang);
+
+        $page['buttonCode'] = $button_generator->generate();
         $page['availableImages'] = array_keys($image_locales_map);
         $page['availableLocales'] = $generate_button ? $image_locales : $locales_list;
         $page['availableChatStyles'] = $style_list;
@@ -182,135 +179,6 @@ class ButtonCodeController extends AbstractController
         return $this->render('button_code', $page);
     }
 
-    /**
-     * Generates button code.
-     *
-     * @param string $request Request incoming request.
-     * @param string $title Page title
-     * @param string $locale RFC 5646 code for language
-     * @param string $style name of available style from styles/dialogs folder
-     * @param string $invitation_style_name name of avalabel style from
-     * styles/invitations folder
-     * @param integer $group chat group id
-     * @param integer $inner chat link message or html code like image code
-     * @param bool $show_host generated link contains protocol and domain or not
-     * @param bool $force_secure force protocol to secure (https) or not
-     * @param bool $mod_security add rule to remove protocol from document location
-     * in generated javascript code
-     * @param bool $operator_code add operator code to generated button code or not
-     * @param bool $disable_invitation forcibly disable invitation regadless of
-     * tracking settings
-     *
-     * @return string Generate chat button code
-     */
-    protected function generateButton(
-        $request,
-        $title,
-        $locale,
-        $style,
-        $invitation_style_name,
-        $group,
-        $inner,
-        $show_host,
-        $force_secure,
-        $mod_security,
-        $operator_code,
-        $disable_invitation
-    ) {
-        $host = ($force_secure ? 'https://' : 'http://') . $request->getHost();
-        $base_url = ($show_host ? $host : '')
-            . $request->getBasePath();
-
-        $url_type = $show_host
-            ? UrlGeneratorInterface::ABSOLUTE_URL
-            : UrlGeneratorInterface::ABSOLUTE_PATH;
-
-        // Build the main link
-        $link_params = array();
-        if ($locale) {
-            $link_params['locale'] = $locale;
-        }
-        if ($style) {
-            $link_params['style'] = $style;
-        }
-        if ($group) {
-            $link_params['group'] = $group;
-        }
-        $link = ($show_host && $force_secure)
-            ? $this->generateSecureUrl('chat_user_start', $link_params, $url_type)
-            : $this->generateUrl('chat_user_start', $link_params, $url_type);
-
-        $modsecfix = $mod_security ? ".replace('http://','').replace('https://','')" : "";
-        $js_link = "'" . $link
-            . (empty($link_params) ? '?' : '&')
-            . "url='+escape(document.location.href$modsecfix)+'&referrer='+escape(document.referrer$modsecfix)";
-
-        // Get popup window configurations
-        if ($style) {
-            $chat_style = new ChatStyle($style);
-            $chat_configurations = $chat_style->getConfigurations();
-            $popup_options = $chat_configurations['chat']['window_params'];
-        } else {
-            $popup_options = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,width=640,height=480,resizable=1";
-        }
-
-        // Generate operator code field
-        if ($operator_code) {
-            $form_on_submit = "if(navigator.userAgent.toLowerCase().indexOf('opera') != -1 "
-                . "&& window.event.preventDefault) window.event.preventDefault();"
-                . "this.newWindow = window.open({$js_link} + '&operator_code=' "
-                . "+ document.getElementById('mibewOperatorCodeField').value, 'mibew', '{$popup_options}');"
-                . "this.newWindow.focus();this.newWindow.opener=window;return false;";
-            $temp = '';
-            return "" . $temp . "";
-        }
-
-        // Generate button
-        $temp = get_popup($link, "$js_link", $inner, $title, "mibew", $popup_options);
-        if (!$disable_invitation && Settings::get('enabletracking')) {
-            $widget_data = array();
-
-            // Get actual invitation style instance
-            if (!$invitation_style_name) {
-                $invitation_style_name = InvitationStyle::getCurrentStyle();
-            }
-            $invitation_style = new InvitationStyle($invitation_style_name);
-
-            // URL of file with additional CSS rules for invitation popup
-            $widget_data['inviteStyle'] = $base_url . '/' .
-                $invitation_style->getFilesPath() .
-                '/invite.css';
-
-            // Time between requests to the server in milliseconds
-            $widget_data['requestTimeout'] = Settings::get('updatefrequency_tracking') * 1000;
-
-            // URL for requests
-            $widget_data['requestURL'] = ($show_host && $force_secure)
-                ? $this->generateSecureUrl('widget_gateway', array())
-                : $this->generateUrl('widget_gateway', array(), $url_type);
-
-            // Locale for invitation
-            $widget_data['locale'] = $locale;
-
-            // Name of the cookie to track user. Use if third-party cookie blocked
-            $widget_data['visitorCookieName'] = VISITOR_COOKIE_NAME;
-
-            // Build additional button code
-            $temp = preg_replace('/^('
-                . ''
-                . '';
-        }
-
-        return "" . $temp . "";
-    }
-
     /**
      * Prepares list of group options.
      *
diff --git a/src/mibew/libs/common/misc.php b/src/mibew/libs/common/misc.php
index 74d2da0e..53a91a85 100644
--- a/src/mibew/libs/common/misc.php
+++ b/src/mibew/libs/common/misc.php
@@ -17,32 +17,6 @@
  * limitations under the License.
  */
 
-function get_image_size($filename)
-{
-    $ext = pathinfo($filename, PATHINFO_EXTENSION);
-
-    if (function_exists('gd_info') && ($ext == 'gif' || $ext == 'png')) {
-        $info = gd_info();
-
-        $img = false;
-        if ($ext == 'gif' && !empty($info['GIF Read Support'])) {
-            $img = @imagecreatefromgif($filename);
-        } elseif ($ext == 'png' && !empty($info['PNG Support'])) {
-            $img = @imagecreatefrompng($filename);
-        }
-
-        if ($img) {
-            $height = imagesy($img);
-            $width = imagesx($img);
-            imagedestroy($img);
-
-            return array($width, $height);
-        }
-    }
-
-    return array(0, 0);
-}
-
 function div($a, $b)
 {
     return ($a - ($a % $b)) / $b;
diff --git a/src/mibew/libs/common/response.php b/src/mibew/libs/common/response.php
index ec760867..d68ad4de 100644
--- a/src/mibew/libs/common/response.php
+++ b/src/mibew/libs/common/response.php
@@ -33,15 +33,6 @@ function get_popup($href, $js_href, $message, $title, $wnd_name, $options)
         . "this.newWindow.focus();this.newWindow.opener=window;return false;\">$message";
 }
 
-function get_image($href, $width, $height)
-{
-    if ($width != 0 && $height != 0) {
-        return "
";
-    }
-
-    return "
";
-}
-
 /**
  * Load additional CSS files, required by plugins, and build HTML code to
  * include them