From c64c1a441dc7649794ea84d2eb9faf6967f2c8ca Mon Sep 17 00:00:00 2001 From: Dmitriy Simushev Date: Fri, 17 May 2013 13:59:15 +0000 Subject: [PATCH] Refactor JavaScript invitation and tracking code --- src/messenger/webim/js/compiled/invite.js | 10 - src/messenger/webim/js/compiled/request.js | 13 - src/messenger/webim/js/compiled/widget.js | 16 + src/messenger/webim/js/source/invite.js | 58 --- src/messenger/webim/js/source/request.js | 155 -------- src/messenger/webim/js/source/widget.js | 374 ++++++++++++++++++ src/messenger/webim/libs/common/response.php | 2 +- src/messenger/webim/libs/getcode.php | 48 ++- .../webim/{request.php => widget.php} | 11 +- 9 files changed, 429 insertions(+), 258 deletions(-) delete mode 100644 src/messenger/webim/js/compiled/invite.js delete mode 100644 src/messenger/webim/js/compiled/request.js create mode 100644 src/messenger/webim/js/compiled/widget.js delete mode 100644 src/messenger/webim/js/source/invite.js delete mode 100644 src/messenger/webim/js/source/request.js create mode 100644 src/messenger/webim/js/source/widget.js rename src/messenger/webim/{request.php => widget.php} (85%) diff --git a/src/messenger/webim/js/compiled/invite.js b/src/messenger/webim/js/compiled/invite.js deleted file mode 100644 index ddf2b627..00000000 --- a/src/messenger/webim/js/compiled/invite.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - Copyright 2005-2013 the original author or authors. - - Licensed under the Apache License, Version 2.0 (the "License"). - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -*/ -var style=document.createElement("style");document.getElementsByTagName("head")[0].appendChild(style);window.createPopup||(style.appendChild(document.createTextNode("")),style.setAttribute("type","text/css"));var sheet=document.styleSheets[document.styleSheets.length-1];if(window.createPopup)sheet.cssText=mibewInviteStyle;else{var node=document.createTextNode(mibewInviteStyle);style.appendChild(node)} -function mibewInviteOnResponse(a){var c=a.invitation.message,b=a.invitation.operator,d=a.invitation.avatar;a='
';b&&(a+='

'+b+"

");d&&(a+=''+b+'');a=a+('

'+c+"

")+'
'; -if(c=document.getElementById("mibewinvitation"))c.innerHTML=a}function mibewHideInvitation(){document.getElementById("mibewinvitationpopup")&&(document.getElementById("mibewinvitationpopup").style.display="none")}function mibewOpenAgent(){document.getElementById("mibewAgentButton")&&(document.getElementById("mibewAgentButton").onclick(),mibewHideInvitation())}; diff --git a/src/messenger/webim/js/compiled/request.js b/src/messenger/webim/js/compiled/request.js deleted file mode 100644 index 0a7b5f95..00000000 --- a/src/messenger/webim/js/compiled/request.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - Copyright 2005-2013 the original author or authors. - - Licensed under the Apache License, Version 2.0 (the "License"). - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -*/ -var mibewRequestedScripts=[],mibewHandlers=[],mibewHandlersDependences=[];function mibewMakeRequest(){var a=mibewReadCookie(mibewVisitorCookieName);mibewDoLoadScript(mibewRequestUrl+"&rnd="+Math.random()+(!1!==a?"&user_id="+a:""),"responseScript")} -function mibewOnResponse(a){var b=a.load,c=a.handlers,d=a.data;a=a.dependences;for(id in b)b[id]in mibewRequestedScripts||(mibewRequestedScripts[id]=[],mibewRequestedScripts[id].url=b[id],mibewRequestedScripts[id].status="loading",mibewLoadScript(id));for(handler in a)handler in mibewHandlersDependences||(mibewHandlersDependences[handler]=a[handler]);for(b=0;b'; +d&&(a+='

'+d+"

");e&&(a+=''+d+'');a=a+('

'+b+"

")+'
';if(b=document.getElementById("mibewinvitation"))b.innerHTML=a};b.Invitation={};b.Invitation.hide=function(){var a=document.getElementById("mibewinvitationpopup");a&&(a.style.display="none")};b.Invitation.accept=function(){document.getElementById("mibewAgentButton")&& +(document.getElementById("mibewAgentButton").onclick(),b.Invitation.hide())}})(Mibew); diff --git a/src/messenger/webim/js/source/invite.js b/src/messenger/webim/js/source/invite.js deleted file mode 100644 index b9b5d925..00000000 --- a/src/messenger/webim/js/source/invite.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @preserve Copyright 2005-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - */ - -var style = document.createElement('style'); -document.getElementsByTagName('head')[0].appendChild(style); - -if (!window.createPopup) { - style.appendChild(document.createTextNode('')); - style.setAttribute("type", "text/css"); -} - -var sheet = document.styleSheets[document.styleSheets.length - 1]; -if (!window.createPopup) { - var node = document.createTextNode(mibewInviteStyle); - style.appendChild(node); -} else { - sheet.cssText = mibewInviteStyle; -} - -function mibewInviteOnResponse(response) -{ - var message = response.invitation.message; - var operator = response.invitation.operator; - var avatar = response.invitation.avatar; - - var popuptext = '
'; - popuptext += ''; - if (operator) { - popuptext += '

' + operator + '

'; - } - if (avatar) { - popuptext += '' + operator + ''; - } - popuptext += '

' + message + '

'; - popuptext += '
'; - var invitationdiv = document.getElementById("mibewinvitation"); - if (invitationdiv) { - invitationdiv.innerHTML = popuptext; - } -} - -function mibewHideInvitation() { - if (document.getElementById('mibewinvitationpopup')) { - document.getElementById('mibewinvitationpopup').style.display='none'; - } -} - -function mibewOpenAgent() { - if (document.getElementById('mibewAgentButton')) { - document.getElementById('mibewAgentButton').onclick(); - mibewHideInvitation(); - } -} \ No newline at end of file diff --git a/src/messenger/webim/js/source/request.js b/src/messenger/webim/js/source/request.js deleted file mode 100644 index edc36451..00000000 --- a/src/messenger/webim/js/source/request.js +++ /dev/null @@ -1,155 +0,0 @@ -/** - * @preserve Copyright 2005-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - */ - -var mibewRequestedScripts = new Array(); -var mibewHandlers = new Array(); -var mibewHandlersDependences = new Array(); - -function mibewMakeRequest() -{ - // Try to get user id from local cookie - var userId = mibewReadCookie(mibewVisitorCookieName); - - mibewDoLoadScript( - mibewRequestUrl + '&rnd=' + Math.random() - + ((userId !== false) ? '&user_id=' + userId : ''), - 'responseScript' - ); -} - -function mibewOnResponse(response) -{ - var load = response.load; - var handlers = response.handlers; - var data = response.data; - var dependences = response.dependences; - - for(id in load){ - if(! (load[id] in mibewRequestedScripts)){ - mibewRequestedScripts[id] = new Array(); - mibewRequestedScripts[id]['url'] = load[id]; - mibewRequestedScripts[id]['status'] = 'loading'; - mibewLoadScript(id); - } - } - - for(handler in dependences){ - if(! (handler in mibewHandlersDependences)){ - mibewHandlersDependences[handler] = dependences[handler]; - } - } - - for(var i = 0; i < handlers.length; i++){ - var handlerName = handlers[i]; - if(mibewCanRunHandler(handlers[i])){ - window[handlerName](data); - }else{ - if(! (handlers[i] in mibewHandlers)){ - mibewHandlers[handlerName] = function(){ - window[handlerName](data); - }; - } - } - } - - mibewCleanUpAfterRequest(); - - window.setTimeout(mibewMakeRequest,mibewRequestTimeout); -} - -function mibewCleanUpAfterRequest() -{ - document.getElementsByTagName('head')[0].removeChild(document.getElementById('responseScript')); -} - -function mibewDoLoadScript(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; -} - -function mibewLoadScript(id) -{ - var script = mibewDoLoadScript(mibewRequestedScripts[id]['url'], id); - script.onload = function(){ - mibewScriptReady(id); - } - script.onreadystatechange = function(){ - if (this.readyState == 'complete' || this.readyState == 'loaded') { - mibewScriptReady(id); - } - } -} - -function mibewScriptReady(id) -{ - mibewRequestedScripts[id]['status'] = 'ready'; - for(handlerName in mibewHandlers){ - if(mibewCanRunHandler(handlerName)){ - mibewHandlers[handlerName](); - delete mibewHandlers[handlerName]; - } - } -} - -function mibewCanRunHandler(handlerName) -{ - var dependences = mibewHandlersDependences[handlerName]; - for(var i = 0; i < dependences.length; i++){ - if(mibewRequestedScripts[dependences[i]]['status'] != 'ready'){ - return false; - } - } - return true; -} - -/** - * Create session cookie for top level domain with path equals to '/'. - * - * @param {String} name Cookie name - * @param {String} value Cookie value - */ -function mibewCreateCookie(name, value) { - var domainParts = /([^\.]+\.[^\.]+)$/.exec(document.location.hostname); - var domain = domainParts[1]; - document.cookie = "" + name + "=" + value + "; " - + "path=/; " - + (domain ? ("domain=" + domain + ";") : ''); -} - -/** - * 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 - */ -function mibewReadCookie(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; -} - -/** - * Update user id. API function - * @param {Object} response Data object from server - */ -function mibewUpdateUserId(response) { - mibewCreateCookie(mibewVisitorCookieName, response.user.id); -} diff --git a/src/messenger/webim/js/source/widget.js b/src/messenger/webim/js/source/widget.js new file mode 100644 index 00000000..fec1e212 --- /dev/null +++ b/src/messenger/webim/js/source/widget.js @@ -0,0 +1,374 @@ +/** + * @preserve Copyright 2005-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +/** + * @namespace Holds all Mibew functionality + */ +var Mibew = {}; + +(function(Mibew){ + + /** + * @namespace Holds objects instances + */ + Mibew.Objects = {}; + + /** + * Create new widget + * @constructor + * @todo Add options validation + */ + Mibew.Widget = function(options) { + /** + * Holds all scripts that must be loaded + * @type Object + * @private + */ + this.requestedScripts = {}; + + /** + * List of handlers that must be called + * @type Array + * @private + */ + this.handlers = []; + + /** + * List of dependences between handlers and scripts + * @type Object + * @private + */ + this.handlersDependences = {}; + + /** + * URL for requests + * @type String + */ + this.requestURL = options.requestURL; + + /** + * Timeout between requests to server + * @type Number + */ + this.requestTimeout = options.requestTimeout; + + /** + * Name of tracking cookie + * @type String + */ + this.visitorCookieName = options.visitorCookieName; + + /** + * URL of file with additional CSS rules for invitation + * @type String + */ + this.inviteStyle = options.inviteStyle; + + /** + * Locale of the Widget + * @type String + */ + this.locale = options.locale; + + // 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); + } + + /** + * Make request to the server. + * @private + */ + Mibew.Widget.prototype.makeRequest = function() { + // Try to get user id from local cookie + var userId = Mibew.Utils.readCookie(this.visitorCookieName); + + this.doLoadScript( + this.requestURL + + '?entry=' + escape(document.referrer) + + '&locale=' + this.locale + + '&rnd=' + Math.random() + + ((userId !== false) ? '&user_id=' + userId : ''), + 'responseScript' + ); + } + + /** + * Parse server response. Called as JSONP callback function + * @param {Object} response Data got from server + */ + Mibew.Widget.prototype.onResponse = function(response) { + // Create some shortcuts + var load = response.load; + var handlers = response.handlers; + var data = response.data; + var dependences = response.dependences; + var context = this; + + // Update list of scripts that must be loaded + for(var id in load){ + if (! load.hasOwnProperty(id)) { + continue; + } + // Check if script already loaded + if (! (load[id] in this.requestedScripts)) { + this.requestedScripts[id] = {}; + this.requestedScripts[id].url = load[id]; + this.requestedScripts[id].status = 'loading'; + this.loadScript(id); + } + } + + // Update list of dependences + for(var handler in dependences){ + if (! dependences.hasOwnProperty(handler)) { + continue; + } + // Check if dependences for this handler already stored + if (! (handler in this.handlersDependences)) { + this.handlersDependences[handler] = dependences[handler]; + } + } + + // Process all recieved handlers. Run handler if all dependences loaded + // and add it to handlers list otherwise. + for (var i = 0; i < handlers.length; i++) { + // Create shortcuts + var handlerName = handlers[i]; + + // TODO: Allow to run objects methods + if (this.canRunHandler(handlerName)) { + Mibew.APIFunctions[handlerName](data); + } else { + if (! (handlerName in this.handlers)) { + this.handlers[handlerName] = function(){ + Mibew.APIFunctions[handlerName](data); + }; + } + } + } + + this.cleanUpAfterRequest(); + + // Make new request after timeout + window.setTimeout( + function() { + context.makeRequest(); + }, + this.requestTimeout); + } + + /** + * Remove dynamically loaded request script from DOM + * @private + */ + Mibew.Widget.prototype.cleanUpAfterRequest = function() { + document.getElementsByTagName('head')[0] + .removeChild(document.getElementById('responseScript')); + } + + /** + * Load script and add handler for script onLoad event + * @param {String} id Identifier of the script to load + * @private + */ + Mibew.Widget.prototype.loadScript = function(id) { + // Store context + var context = this; + // Load script by adding script tag to DOM + var script = this.doLoadScript(this.requestedScripts[id].url, id); + + // Check if script loaded + script.onload = function(){ + context.scriptReady(id); + } + // Do it in crossbrowser way + script.onreadystatechange = function(){ + if (this.readyState == 'complete' || this.readyState == 'loaded') { + context.scriptReady(id); + } + } + } + + /** + * 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 dependences. + * @param {String} id Identifier of the loaded script + */ + Mibew.Widget.prototype.scriptReady = function(id) { + this.requestedScripts[id].status = 'ready'; + for (var handlerName in this.handlers) { + if (! this.handlers.hasOwnProperty(handlerName)) { + continue; + } + if(this.canRunHandler(handlerName)){ + this.handlers[handlerName](); + delete this.handlers[handlerName]; + } + } + } + + /** + * Check if handler can be run + */ + Mibew.Widget.prototype.canRunHandler = function(handlerName) { + var dependences = this.handlersDependences[handlerName]; + // Check for dependencess + for(var i = 0; i < dependences.length; i++){ + if(this.requestedScripts[dependences[i]].status != 'ready'){ + return false; + } + } + return true; + } + + + + /** + * Helper function which create new widget object, store it into + * Mibew.Objects.widget and run automatic requests. + */ + Mibew.Widget.init = function(options) { + Mibew.Objects.widget = new Mibew.Widget(options); + Mibew.Objects.widget.makeRequest(); + } + + /** + * @namespace Holds utility functions + */ + Mibew.Utils = {}; + + /** + * Create session cookie for top level domain with path equals to '/'. + * + * @param {String} name Cookie name + * @param {String} value Cookie value + */ + Mibew.Utils.createCookie = function(name, value) { + var domainParts = /([^\.]+\.[^\.]+)$/.exec(document.location.hostname); + var domain = domainParts[1]; + document.cookie = "" + name + "=" + value + "; " + + "path=/; " + + (domain ? ("domain=" + domain + ";") : ''); + } + + /** + * 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; + } + + /** + * @namespace Holds functions that can be called by the Core + */ + Mibew.APIFunctions = {}; + + /** + * Update user id. API function + * @param {Object} response Data object from server + */ + Mibew.APIFunctions.updateUserId = function(response) { + Mibew.Utils.createCookie( + Mibew.Objects.widget.visitorCookieName, + response.user.id + ); + } + + /** + * Show invitation popup + * @param {Object} response Data passed from server + */ + Mibew.APIFunctions.inviteOnResponse = function(response) { + var message = response.invitation.message; + var operator = response.invitation.operator; + var avatar = response.invitation.avatar; + + var popuptext = '
'; + popuptext += '
' + + '' + + '×
'; + if (operator) { + popuptext += '

' + + operator + + '

'; + } + if (avatar) { + popuptext += '' + operator
+                + ''; + } + popuptext += '

' + + message + + '

'; + popuptext += '
'; + + var invitationdiv = document.getElementById("mibewinvitation"); + if (invitationdiv) { + invitationdiv.innerHTML = popuptext; + } + } + + /** + * @namespace Holds invitation stuff + */ + Mibew.Invitation = {}; + + /** + * Hide invitation popup + */ + Mibew.Invitation.hide = function() { + var invitationPopup = document.getElementById('mibewinvitationpopup'); + if (invitationPopup) { + invitationPopup.style.display = 'none'; + } + } + + /** + * Accept invitation and open chat window + */ + Mibew.Invitation.accept = function() { + if (document.getElementById('mibewAgentButton')) { + document.getElementById('mibewAgentButton').onclick(); + Mibew.Invitation.hide(); + } + } + +})(Mibew); diff --git a/src/messenger/webim/libs/common/response.php b/src/messenger/webim/libs/common/response.php index a9c03a90..da0ad2aa 100644 --- a/src/messenger/webim/libs/common/response.php +++ b/src/messenger/webim/libs/common/response.php @@ -262,7 +262,7 @@ function build_widget_response($response) { 'dependences' => array(), 'data' => array() ); - return "mibewOnResponse(" . json_encode($result) . ");"; + return "Mibew.Objects.widget.onResponse(" . json_encode($result) . ");"; } ?> \ No newline at end of file diff --git a/src/messenger/webim/libs/getcode.php b/src/messenger/webim/libs/getcode.php index 74f96156..412b9e68 100644 --- a/src/messenger/webim/libs/getcode.php +++ b/src/messenger/webim/libs/getcode.php @@ -18,7 +18,8 @@ function generate_button($title, $locale, $style, $invitationstyle, $group, $inner, $showhost, $forcesecure, $modsecurity) { global $visitorcookie; - $link = get_app_location($showhost, $forcesecure) . "/client.php"; + $app_location = get_app_location($showhost, $forcesecure); + $link = $app_location . "/client.php"; if ($locale) $link = append_query($link, "locale=$locale"); if ($style) @@ -31,20 +32,37 @@ function generate_button($title, $locale, $style, $invitationstyle, $group, $inn $temp = get_popup($link, "$jslink", $inner, $title, "webim", "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,width=640,height=480,resizable=1"); if (Settings::get('enabletracking')) { - $temp = preg_replace('/^(' . + '' . + ''; } return "" . $temp . ""; } diff --git a/src/messenger/webim/request.php b/src/messenger/webim/widget.php similarity index 85% rename from src/messenger/webim/request.php rename to src/messenger/webim/widget.php index be3c4907..d57a749d 100644 --- a/src/messenger/webim/request.php +++ b/src/messenger/webim/widget.php @@ -61,18 +61,17 @@ if (Settings::get('enabletracking') == '1') { if ($user_id !== false) { // Update local cookie value at target site - $response['handlers'][] = 'mibewUpdateUserId'; - $response['dependences']['mibewUpdateUserId'] = array(); + $response['handlers'][] = 'updateUserId'; + $response['dependences']['updateUserId'] = array(); $response['data']['user']['id'] = $user_id; } } if ($invited !== FALSE) { - $response['load']['mibewInvitationScript'] = get_app_location(true, is_secure_request()) . '/js/compiled/invite.js'; - $response['handlers'][] = 'mibewInviteOnResponse'; - $response['dependences']['mibewInviteOnResponse'] = array('mibewInvitationScript'); - $locale = isset($_GET['lang']) ? $_GET['lang'] : ''; + $response['handlers'][] = 'inviteOnResponse'; + $response['dependences']['inviteOnResponse'] = array(); + $locale = isset($_GET['locale']) ? $_GET['locale'] : ''; $operatorName = ($locale == $home_locale) ? $operator['vclocalename'] : $operator['vccommonname']; $response['data']['invitation']['operator'] = htmlspecialchars($operatorName); $response['data']['invitation']['message'] = getlocal("invitation.message");