/**
* yalst-trunk: themeApi.js
*
* Created by Matthias Seemann on 21.01.2015.
* Copyright (c) 2015 Visisoft OHG. All rights reserved.
*/
define(['chatEventPublisher', 'lib/jQueryMobileUtils', 'browserEvents'], function (sharedChatEventPublisher, $mobileUtils, sharedBrowserEventStreams)
{
/**
* @file ThemeAPI - Api for third-party JavaScript running in the chat browser application on the
* visitor's page.<br><br>
* Below an example of custom JavaScript as part of a <em>yalst theme</em> file is shown.
* @copyright Visisoft OHG 2015
* @version 1.1
* @author Matthias Seemann
* @module themeApi
* @example
* function themeApiReady()
* {
* var startTime, quitTime, opName, chatId;
*
* require(['themeApi'], function(themeApi)
* {
* // subscribe for all chat events
* var subscription = themeApi.chatEventPublisher.subscribe(
* 'chat',
* function(event, data)
* {
* if (event == "chat.pending"){
* startTime = Date.now();
* chatId = data.chatId;
* }
* else if (event == "chat.joined"){
* opName = data.operatorName;
* }
* }
* );
* // subscribe in particular for "chat closed" event
* themeApi.chatEventPublisher.subscribe(
* 'chat.closed',
* function(event, data)
* {
* if (data.reason == "visitor"){
* console.log("The visitor quit.");
* }
* else if (data.reason == "operator"){
* console.log("The operator quit.");
* }
*
* /// clean up
* subscription.dispose();
* }
* );
* });
* }
*/
/**
* Global callback function which is invoked when the chat application has
* staged the Theme Api JavaScript libraries. This is the first point in time
* when the Theme Api is usable by external JavaScript.<br>
* This function should be defined in the user-provided JavaScript in a <em>yalst Theme</em>.
* @global
* @returns {undefined}
* @example
* function themeApiReady()
{
require(['themeApi', 'api'], function(themeApi, sharedRemoteApi)
{
themeApi.chatEventPublisher.subscribe("chat", console.log.bind(console));
themeApi.chatEventPublisher.subscribe("pageChange", console.log.bind(console));
sharedRemoteApi.isConfiguredPromise.then(function()
{
sharedRemoteApi.hello().then(function(helloInfo)
{
console.log("The livesupport is now " + helloInfo.chat + ".");
});
});
});
}
*/
function themeApiReady(){ return undefined; }
/**
* High level event. Encompasses all special chat events.
* @event module:themeApi.event:"chat"
* @type {object}
*/
/**
* Event fires when the user initiates a chat by tapping on the start button
* if the selected live support department or service is not busy or offline.
* @event module:themeApi.event:"chat.pending"
* @type {object}
* @property {string} chatId - The ID of the new chat.
* @property {string} sessionId - The session ID of the new chat
*/
/**
* Event fires when an operator/agent has picked up the chat request from the user.
* @event module:themeApi.event:"chat.joined"
* @type {object}
* @property {string} operatorName - The name of the associated operator.
* @property {string} [infoLine] - String with more details about the associated operator.
* @property {string} chatId - The ID of the new chat.
* @property {string} sessionId - The session ID of the new chat
*/
/**
* Event fires when the chat gets finished.
* @event module:themeApi.event:"chat.closed"
* @type {object}
* @property {module:themeApi.QuitReason} reason - The reason for quitting the chat.
*/
/**
* Event fires when the visitor has decided to close the chat.
* When the close command is received by the chat server this event
* will be followed by the {@link module:themeApi.event:"chat.closed"} event
* @event module:themeApi.event:"chat.closing"
* @since 1.1
*/
/**
* Event fires when no operator will join the pending chat.
* @event module:themeApi.event:"chat.refused"
* @type {object}
* @property {module:themeApi.RefuseReason} reason - The reason for refusing the chat.
*/
/**
* Event fires when a chat is pending or running, right before the web app will be hibernated by the browser.
* <strong>Important:</strong> You must execute
* <strong>only synchronous (blocking)</strong> JavaScript instructions in your handler for
* this event!
* @event module:themeApi.event:"chat.paused"
*/
/**
* Event fires when the browser revives the formally hibernated web app being pending or running
* @event module:themeApi.event:"chat.resumed"
* @property {string} operatorName - The name of the associated operator.
* @property {string} [infoLine] - String with more details about the associated operator.
*/
/**
* Event fires when the browser re-renders a closed chat
* @event module:themeApi.event:"chat.re-rendered"
* @type {object}
* @property {string} chatId - The ID of the new chat.
* @property {string} sessionId - The session ID of the new chat
*/
/**
* Event fires when a new page (view) is shown
* @event module:themeApi.event:"pageChange"
* @type {object}
* @property {module:themeApi.PageId} pageId - the new page identifier
* @property {string|undefined} departmentId - The department selected by the visitor.
*/
/**
* Event fires when the start/welcome form is validated with success on the user hitting the start chat button.
* @event module:themeApi.event:"startFormValidated"
* @type {object}
* @property {string|undefined} departmentId - The department which provides the start form.
*/
/**
* Event fires when the user hits the Send Button in a chat.
* @event module:themeApi.event:"sendMessage"
* @type {object}
* @property {string|undefined} text - The text typed in bx the user. Undefined if a file or image
* was posted instead.
*/
/**
* Event fires when a chat contribution is received from the operator or the chat system.
* @event module:themeApi.event:"receivedMessage"
* @type {object}
* @since 1.1
* @property {string|undefined} text - The transmitted text. Undefined if a file or image
* @property {string|undefined} origin - The originator , <tt>"operator"</tt>, <tt>"system"</tt> or <tt>"visitor"</tt>
*/
/**
* Enumeration of reasons for chat termination.
* Given by the {@link module:themeApi.event:"chat.closed"} event.
* @readonly
* @enum {string} QuitReason
* @memberof module:themeApi.
*/
var QuitReason = {
/**
* <tt>"visitor"</tt> - the visitor has terminated the chat
* @default "visitor"
*/
visitor: 'visitor',
/**
* <tt>"operator"</tt> - the operator has terminated the chat
*/
operator: 'operator',
/**
* <tt>"system"</tt> - unknown reason
*/
system: 'system',
/**
* <tt>'timeout'</tt> - <strong>!Not implemented!</strong> the chat
* was terminated by the system because there was no activity for a longer
* period in time
*/
timeout: 'timeout'
};
/**
* Enumeration of reasons for chat termination.
* Given by the {@link module:themeApi.event:"chat.refused"} event.
* @readonly
* @enum {string} RefuseReason
* @memberof module:themeApi.
*/
var RefuseReason = {
/**
* <tt>"refused"</tt> - an operator did decline the chat request
* in the operator console
*/
refused: 'refused',
/**
* <tt>"blocked"</tt> - an operator did block the user in the operator console
*/
blocked: 'blocked',
/**
* <tt>'timeout'</tt> - The chat was not joined by any operator withing the
* maximum wait time
*/
timeout: 'timeout'
};
/**
* Enumeration of the application pages (views) presented to the user.
* Used by the {@link module:themeApi.event:"pageChange"} event.
* <img src="images/screens.png" style="margin-top:20px; margin-bottom:20px; width:1000px;"/>
* @readonly
* @enum {string} PageId
* @memberof module:themeApi.
* @example
* require(['themeApi'], function(themeApi)
* {
* themeApi.chatEventPublisher.subscribe(
* "pageChange",
* function(event, info)
* {
* // "blank"->"hello"->"wizard-department-choice"
* console.log("page:" + info.pageId);
* // e.g. "B" or undefined
* console.log("department:" + info.departmentId);
* });
* });
*/
var PageId = {
/**
* <strong>Internal use only</strong> <tt>"blank"</tt> - initial page shown while the loading resource files
* from the network or browser cache
*/
blank : "blank",
/**
* <tt>"hello"</tt> - intermediate page with (optional)
* logo images and welcome
* phrases, shown before the welcoming forms are loaded
*/
hello : "hello",
/**
* <tt>"wizard-department-choice"</tt> - choice of departments is shown when the welcome forms
* differ between departments
*/
"wizard-department-choice" : "wizard-department-choice",
/**
* <tt>"department-form-wizard"</tt> - welcoming form of a particular department
*/
"department-form-wizard" : "department-form-wizard",
/**
* <tt>"chat"</tt> - the chat view
*/
chat: "chat",
/**
* <tt>"offline"</tt> - a predefined offline text
*/
offline: "offline",
/**
* <tt>"contact-form"</tt> - a page with the contact form can be displayed if the chat is offline or the operator could not pick up the chat request in time
*/
"contact-form": "contact-form",
/** <tt>"all-departments-form"</tt> - the common welcome form for all departments
* with a department selector
* */
"all-departments-form" : "all-departments-form",
/** <tt>"fixed-or-any-department-form"</tt> - a welcoming form for a particular department
* or any department
*/
"fixed-or-any-department-form": "fixed-or-any-department-form",
/** <tt>"faq-root"</tt> - Root page of the FAQ pages
*/
"faq-root" : "faq-root",
/** <tt>"auxiliary-gui-dialog"</tt> - an internal gui helper page</br>
* e.g. when a long custom (non-native) select box is displayed on an extra page by jQuery Mobile
*/
"auxiliary-gui-dialog": "auxiliary-gui-dialog"
};
var pageIdPatterns = [
{
pattern : /^#blankPage/,
id: PageId.blank
},
{
pattern : /^#helloPage/,
id: PageId.hello
},
{
pattern : /^#wizard-department-choice-page/,
id: PageId["wizard-department-choice"]
},
{
pattern : /^#departmentFormWizardPage([a-zA-Z0-9])/,
id: PageId["department-form-wizard"]
},
{
pattern : /^#chatPage([a-zA-Z0-9]|undefined|unknown|null)?$/,
id: PageId.chat
},
{
pattern: /^#offlinePage([a-zA-Z0-9]|undefined)?$/,
id : PageId.offline
},
{
pattern: /^#all-departments-form-page/,
id: PageId["all-departments-form"]
},
{
pattern: /^#fixed-or-any-department-form-page/,
id: PageId["fixed-or-any-department-form"]
},
{
pattern: /^output.faq/, id: PageId["faq-root"]
},
{
pattern: /-dialog$/,
id: PageId["auxiliary-gui-dialog"]
}
];
var matchesStringWithPatternProperty = curryN(2, function(text, object)
{
return text.match(object.pattern);
});
sharedBrowserEventStreams.get().pageNavigationStream
.onValue(function(ids)
{
var matchesNextPageIdWithPageIdPattern = matchesStringWithPatternProperty(ids.nextPageId);
var pageIdPattern = R.find(matchesNextPageIdWithPageIdPattern, pageIdPatterns);
if (!pageIdPattern)
{
throw new Error("navigation to unknown page:" + ids.nextPageId);
}
var infoPayload = {
pageId: pageIdPattern.id,
departmentId: matchesNextPageIdWithPageIdPattern(pageIdPattern)[1]
};
sharedChatEventPublisher.publish("pageChange", infoPayload);
}
);
return /** @alias module:themeApi */ {
/**
* Publisher will emit key events during a chat procedure
* @memberof module:themeApi
* @type {module:lib/pubsub~Publisher}
* @fires module:themeApi.event:"chat"
* @fires module:themeApi.event:"chat.pending"
* @fires module:themeApi.event:"chat.joined"
* @fires module:themeApi.event:"chat.closed"
* @fires module:themeApi.event:"chat.closing"
* @fires module:themeApi.event:"chat.refused"
* @fires module:themeApi.event:"chat.paused"
* @fires module:themeApi.event:"chat.resumed"
* @fires module:themeApi.event:"chat.re-rendered"
* @fires module:themeApi.event:"pageChange"
* @fires module:themeApi.event:"sendMessage"
* @fires module:themeApi.event:"receivedMessage"
* @fires module:themeApi.event:"startFormValidated"
*/
chatEventPublisher: sharedChatEventPublisher,
/**
* api version number e.g. "1.0.0+1808"
* @type {string}
*/
version: "1.1.0+2150",
QuitReason: QuitReason,
RefuseReason: RefuseReason,
PageId: PageId
};
});