Source: themeApi.js

/**
 * 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
	};
});