Add ability to use mibew in an iframe

Fixes #63
This commit is contained in:
Dmitriy Simushev 2015-01-20 13:48:28 +00:00
parent 120abce028
commit 7fde2eb9cd
20 changed files with 778 additions and 164 deletions

View File

@ -72,6 +72,14 @@ chat_user_mail_send:
token: \d{1,10}
methods: [POST]
chat_user_popup_style:
path: /chat/style/popup/{style}
defaults:
_controller: Mibew\Controller\Chat\StyleController::loadPopupStyleAction
style: ""
requirements:
style: "[0-9A-Za-z_]*"
chat_user_start:
path: /chat
defaults:

View File

@ -59,7 +59,7 @@
}],
function(args){
if (args.closed) {
window.close();
Mibew.Utils.closeChatPopup();
} else {
// Something went wrong. Display error message
Mibew.Objects.Models.Status.message.setMessage(

View File

@ -188,6 +188,15 @@
models.soundManager = new Mibew.Models.ChatSoundManager();
// If the chat is ran inside an iframe we need to tell the parent page
// that the chat is started. This is needed to reopen chat when the user
// navigates to another page.
if (!models.user.get('isAgent') && options.links.chat) {
if (window.parent && window.parent.postMessage && (window.parent !== window)) {
window.parent.postMessage('mibew-chat-started:' + window.name + ':' + options.links.chat, '*');
}
}
// TODO: May be move it somewhere else
// Periodically call update function at the server side
periodicallyCalled.push(

View File

@ -0,0 +1,43 @@
/*!
* This file is a part of Mibew Messenger.
*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function(Mibew){
/**
* @namespace Holds utility functions
*/
Mibew.Utils = Mibew.Utils || {};
/**
* Closes chat window.
*
* The method helps to close chat popup no mater how it's implemented
* (iframe or window).
*/
Mibew.Utils.closeChatPopup = function() {
if (window.parent && (window.parent !== window) && window.parent.postMessage) {
// It seems that the chat is working in an iframe. Tell the parent
// window that the chat is stoped so it could close the iframe.
window.parent.postMessage('mibew-chat-closed:' + window.name, '*');
} else {
// Just close the window.
window.close();
}
}
})(Mibew);

View File

@ -0,0 +1,460 @@
/*!
* This file is a part of Mibew Messenger.
*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @namespace Holds all Mibew functionality
*/
var Mibew = Mibew || {};
(function(Mibew) {
if (Mibew.ChatPopup) {
// It seems that this file was already loaded. We do not need to do the
// job twice.
return;
}
/**
* @namespace Holds all functionality related with chat popups
*/
Mibew.ChatPopup = {};
/**
* @namespace Holds objects instances
*/
Mibew.Objects = Mibew.Objects || {};
/**
* @namespace Holds all popups instances.
*/
Mibew.Objects.ChatPopups = {};
/**
* @namespace Holds utility functions
*/
Mibew.Utils = {};
/**
* Create a cookie for the second level domain with path equals to '/'.
*
* @param {String} name Cookie name
* @param {String} value Cookie value
* @param {Date} expires Indicates when the cookie expires. If the value is
* omitted a session cookie will be created.
*/
Mibew.Utils.createCookie = function(name, value, expires) {
var domain = /([^\.]+\.[^\.]+)$/.exec(document.location.hostname);
document.cookie = "" + name + "=" + value + "; "
+ "path=/; "
+ (domain ? ("domain=" + domain[1] + "; ") : '')
+ (expires ? ('expires=' + expires.toUTCString() + '; ') : '');
}
/**
* Try to read cookie.
*
* @param {String} name Cookie name
* @returns {String|Boolean} Cookie value or boolean false if cookie with
* specified name does not exist
*/
Mibew.Utils.readCookie = function(name) {
var cookies = document.cookie.split('; ');
var nameForSearch = name + '=';
var value = false;
for (var i = 0; i < cookies.length; i++) {
if (cookies[i].indexOf(nameForSearch) != -1) {
value = cookies[i].substr(nameForSearch.length);
break;
}
}
return value;
}
/**
* Deletes cookie.
*
* @param {String} name Name of the cookie that should be deleted.
*/
Mibew.Utils.deleteCookie = function(name) {
Mibew.Utils.createCookie(name, '', (new Date(0)));
}
/**
* Sets correct prototypes chain.
*
* This function is based on the logic used in Object.create method.
* Unfortunately not all target browsers support this function thus it
* should be implemented here.
*
* Warning: this methods completely rewrites prototype of "ctor" argument.
*
* @type {Function}
* @param {Function} ctor An object constructor which prototype should be
* updated.
* @param {Function} superCtor An object constructor which prototype should
* be used.
*/
Mibew.Utils.inherits = (function() {
// Tmp function is defined in closure because in such case only one
// temporary function will be kept in memory regardless of inherits
// function calls number.
var Tmp = function() {};
return function(ctor, superCtor) {
Tmp.prototype = superCtor.prototype;
ctor.prototype = new Tmp();
Tmp.prototype = null;
ctor.prototype.constructor = ctor;
}
})();
/**
* Attaches an event listener to the target object's event.
*
* This method uses native "addEventListener" in modern browsers and a
* workaround for IE < 9.
*
* @param {Object} target The object which fires the event.
* @param {String} eventName Name of the event.
* @param {Function} listener The function that should be triggered.
*/
Mibew.Utils.addEventListener = function(target, eventName, listener) {
if (target.addEventListener) {
// A regular browser is used
target.addEventListener(eventName, listener, false);
} else {
if (target.attachEvent) {
// This is needed for IE < 9
target.attachEvent(
'on' + eventName,
// The closure is used to use valid this reference in the
// listener.
function (event) {
listener.call(target, event);
});
}
}
};
/**
* Loads CSS file and attach it to DOM.
*
* @param {String} url URL of the CSS that should be loaded.
* @param {String} [id] ID of the DOM element that will be created. Can be
* omitted.
* @returns {Element} Appended DOM item.
*/
Mibew.Utils.loadStyleSheet = function(url, id) {
var styleSheet = document.createElement('link');
styleSheet.setAttribute('rel', 'stylesheet');
styleSheet.setAttribute('type', 'text/css');
styleSheet.setAttribute('href', url);
if (id) {
styleSheet.setAttribute('id', id);
}
document.getElementsByTagName('head')[0].appendChild(styleSheet);
return styleSheet;
}
/**
* Loads JavaScript file and attach it to DOM.
*
* @param {String} url URL of the JavaScript file that should be loaded.
* @param {String} [id] ID of the DOM element that will be created. Can be
* omitted.
* @returns {Element} Appended DOM item.
*/
Mibew.Utils.loadScript = function(url, id) {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', url);
if (id) {
script.setAttribute('id', id);
}
document.getElementsByTagName('head')[0].appendChild(script);
return script;
}
/**
* Initialize a proper chat popup.
*
* This is a helper function which choose which popup (iframe or window)
* should be created, create it and store into Mibew.Objects.ChatPopups
* hash.
*
* @param {Object} options List of popup options.
*/
Mibew.ChatPopup.init = function(options) {
var canUseIFrame = (window.postMessage && options.preferIFrame),
Popup = canUseIFrame ? Mibew.ChatPopup.IFrame : Mibew.ChatPopup.Window;
Mibew.Objects.ChatPopups[options.id] = new Popup(options);
}
/**
* A constructor for base (abstract) popup object.
*
* @constructor
* @param {Object} options A list of popup options.
*/
var BasePopup = function(options) {
/**
* Unique ID of the popup.
* @type {String}
*/
this.id = options.id;
/**
* Chat initialization URL.
* @type {String}
*/
this.url = options.url;
/**
* Width of the popup in pixels.
* @type {Number}
*/
this.width = options.width;
/**
* Height of the popup in pixels.
* @type {Number}
*/
this.height = options.height;
/**
* Indicats if the popup should be resizable.
*
* It can be appliedonly for window popup.
*
* @type {Boolean}
*/
this.resizable = options.resizable || false;
/**
* Contains URL of JavaScript file that loads css file for IFrame popup.
*
* @type {String}
*/
this.styleLoader = options.styleLoader;
/**
* Indicates if special actions should be done to fix problems with
* mod_security.
* @type {Boolean}
*/
this.modSecurity = options.modSecurity || false;
/**
* Indicates if the popup is opened.
* @type {Boolean}
*/
this.isOpened = false;
}
/**
* Builds an URL that initializes a chat.
*
* @returns {String} Chat URL.
*/
BasePopup.prototype.buildChatUrl = function() {
var href = document.location.href,
referrer = document.referrer;
if (this.modSecurity) {
href = href.replace('http://','').replace('https://','');
referrer = referrer.replace('http://','').replace('https://','');
}
return this.url
+ ((this.url.indexOf('?') === false) ? '?' : '&') + 'url=' + escape(href)
+ '&referrer=' + escape(referrer);
}
/**
* Constructs IFrame popup.
*
* @constructor
* @extends BasePopup
* @param {Object} options List of popup options.
*/
Mibew.ChatPopup.IFrame = function(options) {
// Call parent constructor.
BasePopup.call(this, options);
this.iframe = null;
// Load extra style sheets.
Mibew.Utils.loadScript(this.styleLoader);
var openedChatUrl = Mibew.Utils.readCookie('mibew-chat-frame-' + this.id);
if (openedChatUrl) {
// The chat was not closed so the popup should be reopened when a
// new page is visited.
this.open(openedChatUrl);
}
}
// Set correct prototype chain for IFrame popup.
Mibew.Utils.inherits(Mibew.ChatPopup.IFrame, BasePopup);
/**
* Opens the popup.
*
* @param {String} [url] The URL that should be opened in the popup. If the
* value is omitted, the chat initialization URL will be loaded.
*/
Mibew.ChatPopup.IFrame.prototype.open = function(url) {
if (this.isOpened) {
// Do not open the popup twice.
return;
}
if (!this.iframe) {
// Create new iframe.
// There is a bug in IE <= 7 that make "name" attribute unchangeble
// for elements that already exist. Thus a temporary div is used
// here as a workaround.
var tmpDiv = document.createElement('div');
tmpDiv.innerHTML = '<iframe name="mibewChat' + this.id + '"></iframe>';
this.iframe = tmpDiv.getElementsByTagName('iframe')[0];
this.iframe.setAttribute('id', 'mibew-chat-frame-' + this.id);
this.iframe.className = 'mibew-chat-frame';
this.iframe.setAttribute('frameBorder', 0);
this.iframe.style.display = 'none';
document.getElementsByTagName('body')[0].appendChild(this.iframe);
}
this.iframe.style.display = 'block';
this.iframe.src = url || this.buildChatUrl();
this.isOpened = true;
}
/**
* Closes the popup.
*/
Mibew.ChatPopup.IFrame.prototype.close = function() {
if (!this.isOpened) {
// A popup that was not opened thus it cannot be closed.
return;
}
this.iframe.style.display = 'none';
this.iframe.src = '';
this.isOpened = false;
Mibew.Utils.deleteCookie('mibew-chat-frame-' + this.id);
}
/**
* Constructs Window popup.
*
* @constructor
* @extends BasePopup
* @param {Object} options List of popup options.
*/
Mibew.ChatPopup.Window = function(options) {
BasePopup.call(this, options);
this.window = null;
}
// Set correct prototype chain for Window popup.
Mibew.Utils.inherits(Mibew.ChatPopup.Window, BasePopup);
/**
* Opens the popup.
*
* @param {String} [url] The URL that should be opened in the popup. If the
* value is omitted, the chat initialization URL will be loaded.
*/
Mibew.ChatPopup.Window.prototype.open = function(url) {
if (this.isOpened) {
// Do not open the popup twice.
return;
}
this.window = window.open(
url || this.buildChatUrl(),
'mibewChat' + this.id,
this.getWindowParams()
);
this.window.focus();
this.window.opener = window;
this.isOpened = true;
}
/**
* Closes the popup.
*/
Mibew.ChatPopup.Window.prototype.close = function() {
if (!this.isOpened) {
// The window was not opened thus it should not be closed.
return;
}
this.window.close();
this.window = null;
this.isOpened = false;
}
/**
* Builds window params string.
*
* Generated params string can be used in window.open method as the third
* argument.
*
* @protected
* @returns {String}
*/
Mibew.ChatPopup.Window.prototype.getWindowParams = function() {
return [
'toolbar=0',
'scrollbars=0',
'location=0',
'status=1',
'menubar=0',
'width=' + this.width.toString(),
'height=' + this.height.toString(),
'resizable=' + (this.resizable ? '1' : '0')
].join(',');
}
// Attach a listener to window's "message" event to get the url of the chat
// which is opened in iframe popup.
Mibew.Utils.addEventListener(window, 'message', function(event) {
var matches = /^mibew-chat-started\:mibewChat([0-9A-Za-z]+)\:(.*)$/.exec(event.data);
if (matches) {
Mibew.Utils.createCookie('mibew-chat-frame-' + matches[1], matches[2]);
}
});
// Attach a listener to window's "message" event to close the iframe when
// the chat is closed.
Mibew.Utils.addEventListener(window, 'message', function(event) {
var popups = Mibew.Objects.ChatPopups,
matches = /^mibew-chat-closed\:mibewChat([0-9A-Za-z]+)$/.exec(event.data);
if (matches && popups[matches[1]]) {
popups[matches[1]].close();
}
});
})(Mibew);

View File

@ -19,14 +19,14 @@
/**
* @namespace Holds all Mibew functionality
*/
var Mibew = {};
var Mibew = Mibew || {};
(function(Mibew){
/**
* @namespace Holds objects instances
*/
Mibew.Objects = {};
Mibew.Objects = Mibew.Objects || {};
/**
* Create new widget
@ -93,11 +93,7 @@ var Mibew = {};
this.dataToSend = {};
// Load additional styles
var styleSheet = document.createElement('link');
styleSheet.setAttribute('rel', 'stylesheet');
styleSheet.setAttribute('type', 'text/css');
styleSheet.setAttribute('href', options.inviteStyle);
document.getElementsByTagName('head')[0].appendChild(styleSheet);
Mibew.Utils.loadStyleSheet(options.inviteStyle);
}
/**
@ -121,7 +117,7 @@ var Mibew = {};
}
}
this.doLoadScript(
Mibew.Utils.loadScript(
this.requestURL
+ '?' + this.getQuery(),
'mibew-response-script'
@ -260,7 +256,7 @@ var Mibew = {};
// Store context
var context = this;
// Load script by adding script tag to DOM
var script = this.doLoadScript(this.requestedScripts[id].url, id);
var script = Mibew.Utils.loadScript(this.requestedScripts[id].url, id);
// Check if script loaded
script.onload = function(){
@ -274,21 +270,6 @@ var Mibew = {};
}
}
/**
* Dynamically add script tag to DOM
* @param {String} url URL of the script to load
* @param {String} id Identifier of the script to load
* @private
*/
Mibew.Widget.prototype.doLoadScript = function(url, id) {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', url);
script.setAttribute('id', id);
document.getElementsByTagName('head')[0].appendChild(script);
return script;
}
/**
* Event listener for script onLoad event. Run handlers which have no
* unload dependencies.
@ -330,57 +311,6 @@ var Mibew = {};
Mibew.Objects.widget.makeRequest();
}
/**
* @namespace Holds utility functions
*/
Mibew.Utils = {};
/**
* Create a cookie for the second level domain with path equals to '/'.
*
* @param {String} name Cookie name
* @param {String} value Cookie value
* @param {Date} expires Indicates when the cookie expires. If the value is
* omitted a session cookie will be created.
*/
Mibew.Utils.createCookie = function(name, value, expires) {
var domain = /([^\.]+\.[^\.]+)$/.exec(document.location.hostname);
document.cookie = "" + name + "=" + value + "; "
+ "path=/; "
+ (domain ? ("domain=" + domain[1] + "; ") : '')
+ (expires ? ('expires=' + expires.toUTCString() + '; ') : '');
}
/**
* Try to read cookie.
*
* @param {String} name Cookie name
* @returns {String|Boolean} Cookie value or boolean false if cookie with
* specified name does not exist
*/
Mibew.Utils.readCookie = function(name) {
var cookies = document.cookie.split('; ');
var nameForSearch = name + '=';
var value = false;
for (var i = 0; i < cookies.length; i++) {
if (cookies[i].indexOf(nameForSearch) != -1) {
value = cookies[i].substr(nameForSearch.length);
break;
}
}
return value;
}
/**
* Deletes cookie.
*
* @param {String} name Name of the cookie that should be deleted.
*/
Mibew.Utils.deleteCookie = function(name) {
Mibew.Utils.createCookie(name, '', (new Date(0)));
}
/**
* @namespace Holds invitation stuff
*/

View File

@ -416,6 +416,16 @@ function setup_chatview_for_user(
);
}
// Set chat link
$data['chat']['links']['chat'] = $url_generator->generate(
'chat_user',
array(
'thread_id' => $thread->id,
'token' => $thread->lastToken,
),
UrlGeneratorInterface::ABSOLUTE_URL
);
// Set default operator's avatar
$operator = operator_by_id($thread->agentId);
$data['chat']['avatar'] = ($operator['vcavatar'] ? $operator['vcavatar'] : '');
@ -465,6 +475,16 @@ function setup_chatview_for_operator(
);
}
// Set chat link
$data['chat']['links']['chat'] = $url_generator->generate(
'chat_operator',
array(
'thread_id' => $thread->id,
'token' => $thread->lastToken,
),
UrlGeneratorInterface::ABSOLUTE_URL
);
// Set history window params
$data['chat']['links']['history'] = $url_generator->generate(
'history_user',

View File

@ -19,6 +19,8 @@
namespace Mibew\Button\Generator;
use Canteen\HTML5;
use Mibew\Asset\Generator\UrlGeneratorInterface as AssetUrlGeneratorInterface;
use Mibew\EventDispatcher\EventDispatcher;
use Mibew\EventDispatcher\Events;
use Mibew\Routing\Generator\SecureUrlGeneratorInterface as RouteUrlGeneratorInterface;
@ -36,6 +38,13 @@ abstract class AbstractGenerator implements GeneratorInterface
*/
protected $routeUrlGenerator = null;
/**
* An assets URL generator.
*
* @var AssetUrlGeneratorInterface|null
*/
protected $assetUrlGenerator = null;
/**
* List of the generator's options.
*
@ -53,10 +62,14 @@ abstract class AbstractGenerator implements GeneratorInterface
*/
public function __construct(
RouteUrlGeneratorInterface $routeUrlGenerator,
AssetUrlGeneratorInterface $assetUrlGenerator,
$options = array()
) {
$this->routeUrlGenerator = $routeUrlGenerator;
$this->options = $options;
$this->assetUrlGenerator = $assetUrlGenerator;
$this->options = $options + array(
'unique_id' => uniqid() . dechex(rand(0, pow(2, 12))),
);
}
/**
@ -117,6 +130,25 @@ abstract class AbstractGenerator implements GeneratorInterface
: $generator->generate($route, $parameters, RouteUrlGeneratorInterface::ABSOLUTE_URL);
}
/**
* 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);
}
/**
* Gets the URL of the chat start point.
*
@ -161,26 +193,76 @@ abstract class AbstractGenerator implements GeneratorInterface
}
/**
* Gets the options string for the chat popup window.
* Gets the style options string for the chat popup.
*
* @return string
* @return array
*/
protected function getPopupOptions()
protected function getPopupStyle()
{
$style_name = $this->getOption('chat_style');
$defaults = array(
'width' => 640,
'height' => 480,
'resizable' => true,
);
$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";
return $defaults + array(
'styleLoader' => $this->generateUrl('chat_user_popup_style'),
);
}
$chat_style = new ChatStyle($style_name);
$chat_configurations = $chat_style->getConfigurations();
$chat_configs = $chat_style->getConfigurations();
return sprintf(
"toolbar=0,scrollbars=0,location=0,status=1,menubar=0,width=%u,height=%u,resizable=%u",
$chat_configurations['chat']['window']['width'] ?: 640,
$chat_configurations['chat']['window']['height'] ?: 480,
$chat_configurations['chat']['window']['resizable'] ? '1' : '0'
// Intersection is used to limit style options to keys from the defaults
// array.
return array_intersect_key(
$chat_configs['chat']['window'] + $defaults,
$defaults
) + array(
'styleLoader' => $this->generateUrl(
'chat_user_popup_style',
array('style' => $style_name)
),
);
}
/**
* Builds options list for a chat popup.
*
* @return array
*/
protected function getPopupOptions()
{
return array(
'id' => $this->getOption('unique_id'),
'url' => str_replace('&', '&amp;', $this->getChatUrl()),
'preferIFrame' => $this->getOption('prefer_iframe'),
'modSecurity' => $this->getOption('mod_security'),
) + $this->getPopupStyle();
}
/**
* Builds markup with chat popup initialization code.
*
* @return \Canteen\HTML5\Fragment
*/
protected function getPopup()
{
$fragment = HTML5\html('fragment');
$fragment->addChild(
HTML5\html('script')->setAttributes(array(
'type' => 'text/javascript',
'src' => $this->generateAssetUrl('js/compiled/chat_popup.js'),
))
);
$fragment->addChild(
HTML5\html('script')
->setAttribute('type', 'text/javascript')
->addChild('Mibew.ChatPopup.init(' . json_encode($this->getPopupOptions()) . ');')
);
return $fragment;
}
}

View File

@ -20,8 +20,6 @@
namespace Mibew\Button\Generator;
use Canteen\HTML5;
use Mibew\Asset\Generator\UrlGeneratorInterface as AssetUrlGeneratorInterface;
use Mibew\Routing\Generator\SecureUrlGeneratorInterface as RouteUrlGeneratorInterface;
use Mibew\Settings;
use Mibew\Style\InvitationStyle;
@ -30,32 +28,6 @@ use Mibew\Style\InvitationStyle;
*/
class ImageGenerator extends TextGenerator
{
/**
* An assets URL generator.
*
* @var AssetUrlGeneratorInterface|null
*/
protected $assetUrlGenerator = null;
/**
* Class contructor.
*
* @param RouteUrlGeneratorInterface $routeUrlGenerator A routes URL
* generator.
* @param AssetUrlGeneratorInterface $assetUrlGenerator An assets URL
* generator.
* @param array $options Associative array with generator's initial options.
* The set of options can vary for the child classes.
*/
public function __construct(
RouteUrlGeneratorInterface $routeUrlGenerator,
AssetUrlGeneratorInterface $assetUrlGenerator,
$options = array()
) {
parent::__construct($routeUrlGenerator, $options);
$this->assetUrlGenerator = $assetUrlGenerator;
}
/**
* {@inheritdoc}
*/
@ -84,7 +56,7 @@ class ImageGenerator extends TextGenerator
$button = HTML5\html('fragment');
$button->addChild(HTML5\html('comment', 'mibew button'));
$button->addChild($this->getPopup($image));
$button->addChild($this->getPopupLink($image));
if (Settings::get('enabletracking')) {
$button->addChild($this->getWidgetCode());
}
@ -93,25 +65,6 @@ class ImageGenerator extends TextGenerator
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 markup for Mibew widget.
*

View File

@ -35,15 +35,12 @@ class OperatorCodeGenerator extends AbstractGenerator
$form->setAttributes(array(
'action' => '',
'onsubmit' => sprintf(
("if(navigator.userAgent.toLowerCase().indexOf('opera') != -1 "
. "&amp;&amp; window.event.preventDefault) window.event.preventDefault();"
. "this.newWindow = window.open(%s + '&amp;operator_code=' "
. "+ document.getElementById('mibew-operator-code-field').value, 'mibew', '%s');"
. "this.newWindow.focus();"
. "this.newWindow.opener=window;"
. "return false;"),
$this->getChatUrlForJs(),
$this->getPopupOptions()
("var popup = Mibew.Objects.ChatPopups['%s'];"
. "popup.open(popup.buildChatUrl() "
. "+ '&amp;operator_code=' "
. "+ document.getElementById('mibew-operator-code-field').value);"
. "return false;"),
$this->getOption('unique_id')
),
'id' => 'mibew-operator-code-form',
));
@ -58,6 +55,7 @@ class OperatorCodeGenerator extends AbstractGenerator
$button = HTML5\html('fragment');
$button->addChild(HTML5\html('comment', 'mibew operator code field'));
$button->addChild($form);
$button->addChild($this->getPopup());
$button->addChild(HTML5\html('comment', '/ mibew operator code field'));
return $button;

View File

@ -33,35 +33,28 @@ class TextGenerator extends AbstractGenerator
{
$button = HTML5\html('fragment');
$button->addChild(HTML5\html('comment', 'mibew text link'));
$button->addChild($this->getPopup($this->getOption('caption')));
$button->addChild($this->getPopupLink($this->getOption('caption')));
$button->addChild(HTML5\html('comment', '/ mibew text link'));
return $button;
}
/**
* Generates a markup for opening popup window with the chat.
* Generates a markup for link that opens chat popup.
*
* @param string|\Canteen\HTML5\Node $caption A string or an HTML node that
* is used as popup link caption.
* @return string HTML markup.
*/
protected function getPopup($message)
protected function getPopupLink($caption)
{
$link = HTML5\html('a', $message);
$link = HTML5\html('a', $caption);
$link->setAttributes(array(
'id' => 'mibew-agent-button',
'href' => str_replace('&', '&amp;', $this->getChatUrl()),
'target' => '_blank',
'onclick' =>sprintf(
("if(navigator.userAgent.toLowerCase().indexOf('opera') != -1 "
. "&amp;&amp; window.event.preventDefault) window.event.preventDefault();"
. "this.newWindow = window.open(%s, 'mibew', '%s');"
. "this.newWindow.focus();"
. "this.newWindow.opener=window;"
. "return false;"),
$this->getChatUrlForJs(),
$this->getPopupOptions()
),
'onclick' => ("Mibew.Objects.ChatPopups['" . $this->getOption('unique_id') . "'].open();"
. "return false;"),
));
$title = $this->getOption('title');
@ -69,6 +62,10 @@ class TextGenerator extends AbstractGenerator
$link->setAttribute('title', $title);
}
return $link;
$fragment = HTML5\html('fragment');
$fragment->addChild($link);
$fragment->addChild($this->getPopup());
return $fragment;
}
}

View File

@ -82,6 +82,7 @@ class ButtonCodeController extends AbstractController
$show_host = $request->query->get('hostname') == 'on';
$force_secure = $request->query->get('secure') == 'on';
$mod_security = $request->query->get('modsecurity') == 'on';
$force_windows = $request->query->get('forcewindows') == 'on';
$code_type = $request->query->get('codetype', 'button');
if (!in_array($code_type, array('button', 'operator_code', 'text_link'))) {
@ -101,11 +102,13 @@ class ButtonCodeController extends AbstractController
'show_host' => $show_host,
'force_secure' => $force_secure,
'mod_security' => $mod_security,
'prefer_iframe' => !$force_windows,
);
if ($operator_code) {
$button_generator = new OperatorCodeFieldGenerator(
$this->getRouter(),
$this->getAssetManager()->getUrlGenerator(),
$button_generator_options
);
} elseif ($generate_button) {
@ -134,6 +137,7 @@ class ButtonCodeController extends AbstractController
$button_generator = new TextButtonGenerator(
$this->getRouter(),
$this->getAssetManager()->getUrlGenerator(),
$button_generator_options
);
@ -166,6 +170,7 @@ class ButtonCodeController extends AbstractController
$page['formsecure'] = $force_secure;
$page['formmodsecurity'] = $mod_security;
$page['formcodetype'] = $code_type;
$page['formforcewindows'] = $force_windows;
$page['enabletracking'] = Settings::get('enabletracking');
$page['operator_code'] = $operator_code;

View File

@ -0,0 +1,61 @@
<?php
/*
* This file is a part of Mibew Messenger.
*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Mibew\Controller\Chat;
use Mibew\Asset\Generator\UrlGeneratorInterface;
use Mibew\Style\ChatStyle;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
/**
* Contains all actions which are related with dynamic styles loading.
*/
class StyleController extends AbstractController
{
/**
* Generates a JavaScript file with popup CSS file loader.
*
* @param Request $request
* @return JsonResponse
*/
public function loadPopupStyleAction(Request $request)
{
$style_name = $request->attributes->get('style');
if (!$style_name) {
$style_name = ChatStyle::getDefaultStyle();
}
$style = new ChatStyle($style_name);
$configs = $style->getConfigurations();
$response = new JsonResponse();
if ($configs['chat']['iframe']['css']) {
$generator = $this->getAssetManager()->getUrlGenerator();
$css = $generator->generate(
$style->getFilesPath() . '/' . $configs['chat']['iframe']['css'],
UrlGeneratorInterface::ABSOLUTE_URL
);
$response->setData($css);
$response->setCallback('Mibew.Utils.loadStyleSheet');
}
return $response;
}
}

View File

@ -142,6 +142,7 @@ class ChatStyle extends AbstractHandlebarsPoweredStyle implements StyleInterface
return array(
'chat' => array(
'window' => array(),
'iframe' => array(),
),
'mail' => array(
'window' => array(),

View File

@ -6,6 +6,9 @@ chat:
height: 480
width: 640
resizable: true
# These params are used to customize chat popup.
iframe:
css: iframe.css
mail:
# These params are used for send mail windows opened via JavaScript's window.open.

View File

@ -0,0 +1,37 @@
/*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
iframe.mibew-chat-frame {
background-color: #f1f2f2 !important;
margin: 0 !important;
padding: 0px !important;
border: 0 !important;
outline: 0 !important;
border: 1px solid #aaa !important;
position: fixed !important;
bottom: 5px !important;
right: 5px !important;
width: 510px !important;
height: 480px !important;
overflow: hidden !important;
z-index: 100 !important;
border-radius: 10px !important;
-webkit-border-radius: 10px !important;
-moz-border-radius: 10px !important;
box-shadow: 0 1px 1px 1px #ddd !important;
-moz-box-shadow: 0 1px 1px 1px #ddd !important;
-webkit-box-shadow: 0 1px 1px 1px #ddd !important;
}

View File

@ -1,5 +1,5 @@
<div class="buttons">
<a href="javascript:window.close();" title="{{l10n "Close"}}">
<a href="javascript:Mibew.Utils.closeChatPopup();" title="{{l10n "Close"}}">
<img class="tpl-image image-close-window" src="{{page.stylePath}}/images/free.gif" alt="{{l10n "Close"}}" />
</a>
</div>

View File

@ -1,5 +1,5 @@
<div class="buttons">
<a href="javascript:window.close();" title="{{l10n "Close..."}}">
<a href="javascript:Mibew.Utils.closeChatPopup();" title="{{l10n "Close..."}}">
<img class="tpl-image image-close-window" src="{{page.stylePath}}/images/free.gif" alt="{{l10n "Close..."}}" />
</a>
</div>

View File

@ -5,7 +5,7 @@
<div id="headers">
<div class="headers-inwards-wrapper-common"><div class="headers-inwards-wrapper-left"><div class="headers-inwards-wrapper-right"><div class="headers-inwards-wrapper-top"><div class="headers-inwards-wrapper-top-left"><div class="headers-inwards-wrapper-top-right"><div class="headers-inwards-wrapper-bottom-left"><div class="headers-inwards-wrapper-bottom-right">
<div class="buttons">
<a href="javascript:window.close();" title="{{l10n "Close"}}"><img class="tpl-image image-close-window" src="{{page.stylePath}}/images/free.gif" alt="{{l10n "Close"}}" /></a>
<a href="javascript:Mibew.Utils.closeChatPopup();" title="{{l10n "Close"}}"><img class="tpl-image image-close-window" src="{{page.stylePath}}/images/free.gif" alt="{{l10n "Close"}}" /></a>
</div>
<div class="info-message">{{l10n "Thank you for contacting us. Please fill out the form below and click the Start Chat button."}}</div>
</div></div></div></div></div></div></div></div>

View File

@ -50,6 +50,13 @@
</select>
</div>
</div>
<div class="field-in-row">
<label for="force-windows" class="field-label">{{l10n "Use windows (even for modern browsers)"}}</label>
<div class="field-value-no-description">
<input id="force-windows" type="checkbox" name="forcewindows" value="on"{{#if formforcewindows}} checked="checked"{{/if}} onchange="this.form.submit();"/>
</div>
</div>
<br clear="all"/>
{{#if generateButton}}