1 /**
  2  * @fileOverview JavaScript representation of the Finesse Dialog object.
  3  *
  4  * @name finesse.restservices.Dialog
  5  * @requires finesse.clientservices.ClientServices
  6  * @requires Class
  7  * @requires finesse.FinesseBase
  8  * @requires finesse.restservices.RestBase
  9  */
 10 
 11 var finesse = finesse || {};
 12 finesse.restservices = finesse.restservices || {};
 13 
 14 /** @private */
 15 finesse.restservices.Dialog = finesse.restservices.RestBase.extend(/** @lends finesse.restservices.Dialog.prototype */{
 16 
 17     /**
 18      * @class
 19      * JavaScript representation of a Dialog object. Also exposes methods to operate
 20      * on the object against the server.
 21      *
 22 	 * @param {Object} options
 23 	 *     An object with the following properties:<ul>
 24      *         <li><b>id:</b> The id of the object being constructed</li>
 25      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
 26      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
 27      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
 28      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
 29      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
 30      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
 31      *             <li><b>content:</b> {String} Raw string of response</li>
 32      *             <li><b>object:</b> {Object} Parsed object of response</li>
 33      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
 34      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
 35      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
 36      *             </ul></li>
 37      *         </ul></li>
 38      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
 39      * @constructs finesse.restservices.Dialog
 40      **/
 41     init: function (options) {
 42         this._super(options);
 43     },
 44 
 45     /**
 46      * @private
 47      * Gets the REST class for the current object - this is the Dialog class.
 48      * @returns {Object} The Dialog class.
 49      */
 50     getRestClass: function () {
 51         return finesse.restservices.Dialog;
 52     },
 53     
 54     //constant for agent device
 55     _agentDeviceType: "AGENT_DEVICE",
 56     
 57     /**
 58      * @private
 59      * Gets the REST type for the current object - this is a "Dialog".
 60      * @returns {String} The Dialog string.
 61      */
 62     getRestType: function () {
 63         return "Dialog";
 64     },
 65 
 66     /**
 67      * @private
 68      * Override default to indicate that this object doesn't support making
 69      * requests.
 70      */
 71     supportsRequests: false,
 72 
 73     /**
 74      * @private
 75      * Override default to indicate that this object doesn't support subscriptions.
 76      */
 77     supportsSubscriptions: false,
 78 
 79     /**
 80      * Getter for the DNIS.
 81      * @returns {String} The DNIS.
 82      */
 83     getDNIS: function () {
 84         this.isLoaded();
 85         return this.getData().DNIS;
 86     },
 87 
 88     /**
 89      * Getter for the from address.
 90      * @returns {String} The from address.
 91      */
 92     getFromAddress: function () {
 93         this.isLoaded();
 94         return this.getData().fromAddress;
 95     },
 96 
 97     /**
 98      * Getter for the to address.
 99      * @returns {String} The to address.
100      */
101     getToAddress: function () {
102         this.isLoaded();
103         return this.getData().toAddress;
104     },
105     /**
106      * Getter for the media type.
107      * @returns {String} The media type.
108      */
109     getMediaType: function () {
110         this.isLoaded();
111         return this.getData().mediaType;
112     },
113 /**
114      * Getter for the uri.
115      * @returns {String} The uri.
116      */
117     getDialogUri: function () {
118         this.isLoaded();
119         return this.getData().uri;
120     },
121 
122     /**
123      * Getter for the callType.
124      * @returns {String} The callType.
125      */
126     getCallType: function () {
127         this.isLoaded();
128         return this.getData().mediaProperties.callType;
129     },
130 
131     /**
132      * Getter for the Dialog state.
133      * @returns {String} The Dialog state.
134      */
135     getState: function () {
136         this.isLoaded();
137         return this.getData().state;
138     },
139 
140     /**
141      * Retrieves a list of participants within the Dialog object.
142      * @returns {Object} List of
143      */
144     getParticipants: function () {
145         this.isLoaded();
146         var participants = this.getData().participants.Participant;
147         //Due to the nature of the XML->JSO converter library, a single
148         //element in the XML array will be considered to an object instead of
149         //a real array. This will handle those cases to ensure that an array is
150         //always returned.
151 
152         return finesse.utilities.Utilities.getArray(participants);
153     },
154 
155     /**
156      * Determines the droppable participants.  A droppable participant is a participant that is an agent extension.   
157      * (It is not a CTI Route Point, IVR Port, or the caller)
158      * 
159      * @param filterExtension used to remove a single extension from the list
160      * @returns participants which is an array of all participants which can be dropped
161      */
162     getDroppableParticipants: function (filterExtension) {
163       this.isLoaded();
164       var droppableParticipants = [], participants, index, idx, filterExtensionToRemove = "", callStateOk, part;
165 
166       participants = this.getParticipants();
167 
168       if (filterExtension)
169       {
170         filterExtensionToRemove = filterExtension;
171       }
172 
173       //Loop through all the participants to remove non-agents & remove filterExtension
174       //We could have removed filterExtension using splice, but we have to iterate through
175       //the list anyway.
176       for(idx=0;idx<participants.length;idx=idx+1)
177       {
178         part = participants[idx];
179 
180         //Skip the filterExtension
181         if (part.mediaAddress !== filterExtensionToRemove)
182         {
183             callStateOk = this._isParticipantStateDroppable(part);
184 
185             //Remove non-agents & make sure callstate 
186             if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
187             {
188               droppableParticipants.push(part);
189             }
190         }
191     }
192 
193     return finesse.utilities.Utilities.getArray(droppableParticipants);
194     },
195 
196     _isParticipantStateDroppable : function (part)
197     {
198       var isParticipantStateDroppable = false;
199       if (part.state === finesse.restservices.Dialog.States.ACTIVE || part.state === finesse.restservices.Dialog.States.ACCEPTED || part.state === finesse.restservices.Dialog.States.HELD)
200       {
201         isParticipantStateDroppable = true;
202       }
203       
204       return isParticipantStateDroppable;
205     },
206     
207     /**
208      * Is the participant droppable
209      *
210      * @param participantExt
211      * @returns boolean
212      */
213     isParticipantDroppable : function (participantExt) {
214       var droppableParticipants = null, isDroppable = false, idx, part, callStateOk;
215       
216       droppableParticipants = this.getDroppableParticipants();
217       
218       if (droppableParticipants) 
219       {
220         for(idx=0;idx<droppableParticipants.length;idx=idx+1)
221         {
222           part = droppableParticipants[idx];
223          
224           if (part.mediaAddress === participantExt)
225           {
226             callStateOk = this._isParticipantStateDroppable(part);
227 
228             //Remove non-agents & make sure callstate 
229             if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
230             {
231               isDroppable = true;
232               break;
233             }
234           }
235         }
236       }
237       
238       return isDroppable;
239 	},
240 	
241     /**
242      * Retrieves a list of media properties a.k.a. call variables from the dialog object
243      * @returns {Object} Map of call variables; names mapped to values
244      */
245     getMediaProperties: function () {
246 
247         var mpData, resultMap = {};
248 
249         this.isLoaded();
250 
251         mpData = this.getData().mediaProperties;
252 
253         if (mpData) {
254 
255             if (this.getState() !== finesse.restservices.Dialog.States.INITIATING) {
256 
257                 if (mpData.callvariables && mpData.callvariables.CallVariable) {
258                     jQuery.each(mpData.callvariables.CallVariable, function (i, callVariable) {
259                         resultMap[callVariable.name] = callVariable.value;
260                     });
261                 }
262 
263             }
264 
265             if (mpData.wrapUpReason) {
266                 resultMap.wrapUpReason = mpData.wrapUpReason;
267             }
268 
269         }
270 
271         return resultMap;
272     },
273 
274     /**
275      * @private
276      * Invoke a request to the server given a content body and handlers.
277      *
278      * @param {Object} contentBody
279      *     A JS object containing the body of the action request.
280      * @param {Object} handlers
281      *     An object containing the following (optional) handlers for the request:<ul>
282      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
283      *         response object as its only parameter:<ul>
284      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
285      *             <li><b>content:</b> {String} Raw string of response</li>
286      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
287      *         <li>A error callback function for an unsuccessful request to be invoked with the
288      *         error response object as its only parameter:<ul>
289      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
290      *             <li><b>content:</b> {String} Raw string of response</li>
291      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
292      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
293      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
294      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
295      *             </ul></li>
296      *         </ul>
297      */
298     _makeRequest: function (contentBody, handlers) {
299         // Protect against null dereferencing of options allowing its
300         // (nonexistant) keys to be read as undefined
301         handlers = handlers || {};
302 
303         this.restRequest(this.getRestUrl(), {
304             method: 'PUT',
305             success: handlers.success,
306             error: handlers.error,
307             content: contentBody
308         });
309     },
310 
311     /**
312      * Invoke a consult call out to a destination.
313      *
314      * @param {String} mediaAddress
315      *     The media address of the user performing the consult call.
316      * @param {String} toAddress
317      *     The destination address of the consult call.
318      * @param {Object} handlers
319      *     An object containing the following (optional) handlers for the request:<ul>
320      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
321      *         response object as its only parameter:<ul>
322      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
323      *             <li><b>content:</b> {String} Raw string of response</li>
324      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
325      *         <li>A error callback function for an unsuccessful request to be invoked with the
326      *         error response object as its only parameter:<ul>
327      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
328      *             <li><b>content:</b> {String} Raw string of response</li>
329      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
330      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
331      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
332      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
333      *             </ul></li>
334      *         </ul>
335      */
336     makeConsultCall: function (mediaAddress, toAddress, handlers) {
337         this.isLoaded();
338         var contentBody = {};
339         contentBody[this.getRestType()] = {
340             "targetMediaAddress": mediaAddress,
341             "toAddress": toAddress,
342             "requestedAction": finesse.restservices.Dialog.Actions.CONSULT_CALL
343         };
344         this._makeRequest(contentBody, handlers);
345         return this; // Allow cascading
346     },
347     
348     /**
349      * Invoke a single step transfer request.
350      *
351      * @param {String} mediaAddress
352      *     The media address of the user performing the single step transfer.
353      * @param {String} toAddress
354      *     The destination address of the single step transfer.
355      * @param {Object} handlers
356      *     An object containing the following (optional) handlers for the request:<ul>
357      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
358      *         response object as its only parameter:<ul>
359      *         <li><b>status:</b> {Number} The HTTP status code returned</li>
360      *         <li><b>content:</b> {String} Raw string of response</li>
361      *         <li><b>object:</b> {Object} Parsed object of response</li></ul>
362      *         <li>A error callback function for an unsuccessful request to be invoked with the
363      *         error response object as its only parameter:<ul>
364      *         <li><b>status:</b> {Number} The HTTP status code returned</li>
365      *         <li><b>content:</b> {String} Raw string of response</li>
366      *         <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
367      *         <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
368      *         <li><b>errorType:</b> {String} Type of error that was caught</li>
369      *         <li><b>errorMessage:</b> {String} Message associated with error</li>
370      *         </ul></li>
371      *         </ul>
372      */
373     initiateDirectTransfer: function (mediaAddress, toAddress, handlers) {
374         this.isLoaded();
375         var contentBody = {};
376         contentBody[this.getRestType()] = {
377             "targetMediaAddress": mediaAddress,
378             "toAddress": toAddress,
379             "requestedAction": finesse.restservices.Dialog.Actions.TRANSFER_SST
380         };
381         this._makeRequest(contentBody, handlers);
382         return this; // Allow cascading
383     },
384 
385     /**
386      * Update this dialog's wrap-up reason.
387      *
388      * @param {String} wrapUpReason
389      *     The new wrap-up reason for this dialog
390      * @param {Object} handlers
391      *     An object containing the following (optional) handlers for the request:<ul>
392      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
393      *         response object as its only parameter:<ul>
394      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
395      *             <li><b>content:</b> {String} Raw string of response</li>
396      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
397      *         <li>A error callback function for an unsuccessful request to be invoked with the
398      *         error response object as its only parameter:<ul>
399      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
400      *             <li><b>content:</b> {String} Raw string of response</li>
401      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
402      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
403      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
404      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
405      *             </ul></li>
406      *         </ul>
407      */
408     updateWrapUpReason: function (wrapUpReason, options)
409     {
410         this.isLoaded();
411         var mediaProperties =
412         {
413             "wrapUpReason": wrapUpReason
414         };
415 
416         options = options || {};
417         options.content = {};
418         options.content[this.getRestType()] =
419         {
420             "mediaProperties": mediaProperties,
421             "requestedAction": finesse.restservices.Dialog.Actions.UPDATE_CALL_DATA
422         };
423         options.method = "PUT";
424         this.restRequest(this.getRestUrl(), options);
425 
426         return this;
427     },
428 
429     /**
430      * Invoke a request to server based on the action given.
431      * @param {String} mediaAddress
432      *     The media address of the user performing the consult call.
433      * @param {String} action
434      *     The action string indicating the action to invoke on dialog.
435      * @param {Object} handlers
436      *     An object containing the following (optional) handlers for the request:<ul>
437      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
438      *         response object as its only parameter:<ul>
439      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
440      *             <li><b>content:</b> {String} Raw string of response</li>
441      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
442      *         <li>A error callback function for an unsuccessful request to be invoked with the
443      *         error response object as its only parameter:<ul>
444      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
445      *             <li><b>content:</b> {String} Raw string of response</li>
446      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
447      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
448      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
449      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
450      *             </ul></li>
451      *         </ul>
452      * @private
453      */
454     requestAction: function (mediaAddress, action, handlers) {
455         this.isLoaded();
456         var contentBody = {};
457         contentBody[this.getRestType()] = {
458             "targetMediaAddress": mediaAddress,
459             "requestedAction": action
460         };
461         this._makeRequest(contentBody, handlers);
462         return this; // Allow cascading
463     },
464     
465     /**
466      * Wrapper around "requestAction" to request PARTICIPANT_DROP action.
467      *
468      * @param targetMediaAddress is the address to drop
469      * @param handlers to be invoked on success/error
470      * @private 
471      */
472     dropParticipant: function (targetMediaAddress, handlers) {
473 		this.requestAction(targetMediaAddress, finesse.restservices.Dialog.Actions.PARTICIPANT_DROP, handlers);
474     },
475     
476     /**
477      * Invoke a request to server based on the action given.
478      * @param {String} mediaAddress
479      * @param {String} action
480      *     The action string indicating the action to invoke on dialog.
481      * @param {Object} [handlers]
482      *     A set of handlers invoked when async response comes back.
483      * @param {Function} [handlers.success]
484      *     The success handler when request is successful.
485      * @param {Function} [hanlders.error]
486      *     The error handler when request has errors.
487      * @private
488      */
489     sendDTMFRequest: function (mediaAddress, handlers, digit) {
490         this.isLoaded();
491         var contentBody = {};
492         contentBody[this.getRestType()] = {
493             "targetMediaAddress": mediaAddress,
494             "requestedAction": "SEND_DTMF",
495             "actionParams": {
496 				"ActionParam": {
497 					"name": "dtmfString",
498 					"value": digit
499 				}
500 			}
501         };
502         this._makeRequest(contentBody, handlers);
503         return this; // Allow cascading
504     }
505 });
506 
507 /**
508  * Possible dialog state constants.
509  */
510 finesse.restservices.Dialog.States = {
511     ALERTING: "ALERTING",
512     INITIATING: "INITIATING",
513     ACTIVE: "ACTIVE",
514     DROPPED: "DROPPED",
515     HELD: "HELD",
516     INITIATED: "INITIATED",
517     FAILED: "FAILED",
518     INACTIVE: "INACTIVE",
519     WRAP_UP: "WRAP_UP",
520     ACCEPTED: "ACCEPTED"
521 };
522 
523 /**
524  * Possible dialog state reasons code constants.
525  */
526 finesse.restservices.Dialog.ReasonStates = {
527     BUSY: "BUSY",
528     BAD_DESTINATION: "BAD_DESTINATION",
529     OTHER: "OTHER",
530     DEVICE_RESOURCE_NOT_AVAILABLE : "DEVICE_RESOURCE_NOT_AVAILABLE"
531 };
532 
533 /**
534  * Possible participant actions constants.
535  */
536 finesse.restservices.Dialog.Actions = {
537     DROP: "DROP",
538     ANSWER: "ANSWER",
539     HOLD: "HOLD",
540     BARGE_CALL: "BARGE_CALL",
541     PARTICIPANT_DROP: "PARTICIPANT_DROP",
542     MAKE_CALL: "MAKE_CALL",
543     RETRIEVE: "RETRIEVE",
544     CONSULT_CALL: "CONSULT_CALL",
545     TRANSFER: "TRANSFER",
546     TRANSFER_SST: "TRANSFER_SST",
547     CONFERENCE: "CONFERENCE",
548     UPDATE_CALL_DATA: "UPDATE_CALL_DATA",
549     DTMF : "SEND_DTMF",
550     ACCEPT: "ACCEPT",
551     REJECT: "REJECT",
552     CLOSE : "CLOSE"
553 };
554