mirror of
https://github.com/Mibew/mibew.git
synced 2024-11-15 16:44:11 +03:00
parent
120abce028
commit
7fde2eb9cd
@ -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:
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
43
src/mibew/js/source/chat/utils.js
Normal file
43
src/mibew/js/source/chat/utils.js
Normal 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);
|
460
src/mibew/js/source/chat_popup.js
Normal file
460
src/mibew/js/source/chat_popup.js
Normal 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);
|
@ -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
|
||||
*/
|
||||
|
@ -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',
|
||||
|
@ -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('&', '&', $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;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -35,15 +35,12 @@ class OperatorCodeGenerator extends AbstractGenerator
|
||||
$form->setAttributes(array(
|
||||
'action' => '',
|
||||
'onsubmit' => sprintf(
|
||||
("if(navigator.userAgent.toLowerCase().indexOf('opera') != -1 "
|
||||
. "&& window.event.preventDefault) window.event.preventDefault();"
|
||||
. "this.newWindow = window.open(%s + '&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() "
|
||||
. "+ '&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;
|
||||
|
@ -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('&', '&', $this->getChatUrl()),
|
||||
'target' => '_blank',
|
||||
'onclick' =>sprintf(
|
||||
("if(navigator.userAgent.toLowerCase().indexOf('opera') != -1 "
|
||||
. "&& 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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -142,6 +142,7 @@ class ChatStyle extends AbstractHandlebarsPoweredStyle implements StyleInterface
|
||||
return array(
|
||||
'chat' => array(
|
||||
'window' => array(),
|
||||
'iframe' => array(),
|
||||
),
|
||||
'mail' => array(
|
||||
'window' => array(),
|
||||
|
@ -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.
|
||||
|
37
src/mibew/styles/chats/default/iframe.css
Normal file
37
src/mibew/styles/chats/default/iframe.css
Normal 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;
|
||||
}
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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}}
|
||||
|
Loading…
Reference in New Issue
Block a user