' +\n '# for (var i = 0; i < suggestedActions.length; i++) { #' +\n '#:suggestedActions[i].title#' +\n '# } #' +\n '
'\n );\n\n var HERO_CARD_TEMPLATE = kendo.template(\n '' +\n '# if (typeof images !== \"undefined\" && images.length > 0) { #' +\n '
' +\n '# } #' +\n '
' +\n '# if (typeof title !== \"undefined\") { #' +\n '
#:title#
' +\n '# } #' +\n '# if (typeof subtitle !== \"undefined\") { #' +\n '
#:subtitle#
' +\n '# } #' +\n '# if (typeof text !== \"undefined\") { #' +\n '
#:text#
' +\n '# } #' +\n '
' +\n '# if (typeof buttons !== \"undefined\" && buttons.length > 0) { #' +\n '
' +\n '# for (var i = 0; i < buttons.length; i++) { #' +\n '#:buttons[i].title#' +\n '# } #' +\n '
' +\n '# } #' +\n '
'\n );\n\n extend(kendo.chat, {\n Templates: {},\n Components: {}\n });\n\n kendo.chat.registerTemplate = function(templateName, template) {\n kendo.chat.Templates[templateName] = kendo.template(template);\n };\n\n kendo.chat.getTemplate = function(templateName) {\n return kendo.chat.Templates[templateName] || TEXT_MESSAGE_TEMPLATE;\n };\n\n kendo.chat.registerTemplate(\"text\", TEXT_MESSAGE_TEMPLATE);\n kendo.chat.registerTemplate(\"message\", TEXT_MESSAGE_TEMPLATE);\n kendo.chat.registerTemplate(\"typing\", TYPING_INDICATOR_TEMPLATE);\n kendo.chat.registerTemplate(\"suggestedAction\", SUGGESTED_ACTIONS_TEMPLATE);\n kendo.chat.registerTemplate(\"heroCard\", HERO_CARD_TEMPLATE);\n kendo.chat.registerTemplate(\"application/vnd.microsoft.card.hero\", HERO_CARD_TEMPLATE);\n\n kendo.chat.registerComponent = function(componentName, component) {\n kendo.chat.Components[componentName] = component;\n };\n\n kendo.chat.getComponent = function(componentName) {\n return kendo.chat.Components[componentName] || null;\n };\n\n var Component = kendo.chat.Component = kendo.Class.extend({\n init: function(options, view) {\n this.element = $('\")\n .addClass(viewStyles.messageListContent)\n .appendTo(this.element);\n },\n\n _attachEvents: function() {\n var styles = ChatView.styles;\n\n this.element\n .on(\"click\" + NS, this._listClick.bind(this))\n .on(\"click\" + NS, DOT + styles.message, this._messageClick.bind(this))\n .on(\"click\" + NS, DOT + styles.suggestedAction, this._suggestedActionClick.bind(this))\n .on(\"click\" + NS, DOT + styles.cardAction + SPACE + DOT + styles.button, this._cardActionClick.bind(this));\n\n this.element.on(\"keydown\" + NS, DOT + styles.suggestedAction, this._suggestedActionKeydown.bind(this));\n },\n\n _scrollable: function() {\n var viewStyles = ChatView.styles;\n\n this.element\n .on(\"click\" + NS, DOT + viewStyles.cardDeckScrollWrap + SPACE + DOT + viewStyles.button, this._scrollButtonClick.bind(this));\n },\n\n _scrollButtonClick: function(e) {\n var viewStyles = ChatView.styles;\n var button = $(e.currentTarget);\n var scrollToLeft = button.find(DOT + viewStyles.scrollButtonIconLeft).length !== 0;\n var scrollContainer = button.siblings(DOT + viewStyles.cardDeck);\n var lastCard = scrollContainer.find(DOT + viewStyles.card).last();\n var cardWidth = lastCard.outerWidth(true);\n\n if (scrollToLeft) {\n kendo.scrollLeft(scrollContainer, kendo.scrollLeft(scrollContainer) - cardWidth);\n } else {\n kendo.scrollLeft(scrollContainer, kendo.scrollLeft(scrollContainer) + cardWidth);\n }\n },\n\n getTemplate: function(templateName) {\n return kendo.chat.getTemplate(templateName);\n },\n\n getComponent: function(type) {\n return kendo.chat.getComponent(type);\n },\n\n renderMessage: function(message, sender) {\n if (!message.timestamp) {\n message.timestamp = new Date();\n }\n\n if (!message.text) {\n message.text = \"\";\n }\n\n var bubbleElement = this._renderTemplate(message.type, message);\n\n this._renderBubble(message.type, bubbleElement, sender);\n\n if (message.type == \"typing\") {\n if (this.typingParticipants.length > 0) {\n this._removeTypingParticipant(sender);\n }\n } else {\n this._lastSender = sender.id;\n }\n },\n\n renderSuggestedActions: function(suggestedActions) {\n this._removeSuggestedActions();\n\n var element = this._renderTemplate(\"suggestedAction\", { suggestedActions: suggestedActions });\n\n this.list.append(element);\n\n this._scrollToBottom();\n },\n\n renderAttachments: function(options) {\n var wrapper = this._renderAttachmentWrapper(options.attachmentLayout);\n var cardContainer = options.attachmentLayout === \"carousel\" ? wrapper.find(DOT + ChatView.styles.cardDeck) : wrapper;\n var attachments = options.attachments;\n\n if (!attachments.length) {\n return;\n }\n\n for (var i = 0; i < attachments.length; i++) {\n var cardElement = this._renderTemplate(attachments[i].contentType, attachments[i].content);\n\n cardContainer.append(cardElement);\n }\n\n this._removeSuggestedActions();\n this._removeTypingIndicator();\n\n this.list.append(wrapper);\n\n this._lastSender = null;\n },\n\n renderComponent: function(type) {\n var componentType = this.getComponent(type);\n var component = new componentType({}, this);\n\n this.list.append(component.element);\n\n this._scrollToBottom();\n },\n\n _renderAttachmentWrapper: function(layout) {\n var viewStyles = ChatView.styles;\n var wrapper = $(\"
\");\n\n if (layout === \"carousel\") {\n wrapper.addClass(viewStyles.cardDeckScrollWrap);\n\n var buttonLeft = this._renderScrollButton(viewStyles.scrollButtonIconLeft);\n wrapper.append(buttonLeft);\n\n wrapper.append($(\"
\").addClass(viewStyles.cardDeck));\n\n var buttonRight = this._renderScrollButton(viewStyles.scrollButtonIconRight);\n wrapper.append(buttonRight);\n } else {\n wrapper.addClass(viewStyles.cardList);\n }\n\n return wrapper;\n },\n\n _renderScrollButton: function(directionClass) {\n var viewStyles = ChatView.styles;\n\n return $(\"