Browse Source

inline bots wip

master
Igor Zhukov 9 years ago
parent
commit
078d871626
  1. 26
      app/js/controllers.js
  2. 73
      app/js/directives.js
  3. 145
      app/js/message_composer.js
  4. 27
      app/js/messages_manager.js
  5. 5
      app/js/services.js
  6. 79
      app/less/app.less
  7. 27
      app/partials/desktop/composer_dropdown.html
  8. 5
      app/partials/desktop/im.html
  9. 15
      app/partials/desktop/inline_results.html

26
app/js/controllers.js

@ -2099,6 +2099,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}; };
$scope.mentions = {}; $scope.mentions = {};
$scope.commands = {}; $scope.commands = {};
$scope.inlineResults = {};
$scope.$watch('draftMessage.text', onMessageChange); $scope.$watch('draftMessage.text', onMessageChange);
$scope.$watch('draftMessage.files', onFilesSelected); $scope.$watch('draftMessage.files', onFilesSelected);
$scope.$watch('draftMessage.sticker', onStickerSelected); $scope.$watch('draftMessage.sticker', onStickerSelected);
@ -2389,7 +2390,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
return cancelEvent($event); return cancelEvent($event);
} }
var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32}) /; var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32}) ([\s\S]*)$/;
var lastInlineBot = false; var lastInlineBot = false;
function onMessageChange(newVal) { function onMessageChange(newVal) {
// console.log('ctrl text changed', newVal); // console.log('ctrl text changed', newVal);
@ -2405,16 +2406,27 @@ angular.module('myApp.controllers', ['myApp.i18n'])
Storage.set(backupDraftObj); Storage.set(backupDraftObj);
// console.log(dT(), 'draft save', backupDraftObj); // console.log(dT(), 'draft save', backupDraftObj);
var matches; var matches = newVal.match(inlineUsernameRegex);
if (matches = newVal.match(inlineUsernameRegex)) { if (matches) {
AppPeersManager.resolveInlineMention(matches[1]).then(function (placeholder) { $scope.draftMessage.inlineProgress = true;
$scope.draftMessage.inlinePlaceholder = placeholder; AppPeersManager.resolveInlineMention(matches[1]).then(function (inlineBot) {
$scope.draftMessage.inlinePlaceholder = inlineBot.placeholder;
AppMessagesManager.getInlineResults(inlineBot.id, matches[2], '').then(function (botResults) {
$scope.inlineResults = botResults;
console.log('results', botResults);
delete $scope.draftMessage.inlineProgress;
}, function () {
delete $scope.draftMessage.inlineProgress;
});
}, function () { }, function () {
delete $scope.draftMessage.inlinePlaceholder;
}) delete $scope.draftMessage.inlineProgress;
});
} }
} else { } else {
Storage.remove('draft' + $scope.curDialog.peerID); Storage.remove('draft' + $scope.curDialog.peerID);
delete $scope.draftMessage.inlinePlaceholder;
delete $scope.draftMessage.inlineProgress;
// console.log(dT(), 'draft delete', 'draft' + $scope.curDialog.peerID); // console.log(dT(), 'draft delete', 'draft' + $scope.curDialog.peerID);
} }
} }

73
app/js/directives.js

@ -1517,16 +1517,11 @@ angular.module('myApp.directives', ['myApp.filters'])
getSendOnEnter: function () { getSendOnEnter: function () {
return sendOnEnter; return sendOnEnter;
}, },
getPeerImage: function (element, peerID, noReplace) { dropdownDirective: function (element, callback) {
if (cachedPeerPhotos[peerID] && !noReplace) {
element.replaceWith(cachedPeerPhotos[peerID]);
return;
}
var scope = $scope.$new(true); var scope = $scope.$new(true);
scope.peerID = peerID; $compile('<div my-composer-dropdown></div>')(scope, function (clonedElement) {
peerPhotoCompiled(scope, function (clonedElement) {
cachedPeerPhotos[peerID] = clonedElement;
element.replaceWith(clonedElement); element.replaceWith(clonedElement);
callback(scope, clonedElement);
}); });
}, },
mentions: $scope.mentions, mentions: $scope.mentions,
@ -3289,7 +3284,7 @@ angular.module('myApp.directives', ['myApp.filters'])
var width = attrs.width || element.width() || 40; var width = attrs.width || element.width() || 40;
var stroke = attrs.stroke || (width / 2 * 0.14); var stroke = attrs.stroke || (width / 2 * 0.14);
var center = width / 2; var center = width / 2;
var radius = center - stroke; var radius = center - (stroke / 2);
// Doesn't work without unique id for every gradient // Doesn't work without unique id for every gradient
var curNum = ++num; var curNum = ++num;
@ -3345,3 +3340,63 @@ angular.module('myApp.directives', ['myApp.filters'])
}; };
}) })
.directive('myComposerDropdown', function () {
return {
templateUrl: templateUrl('composer_dropdown')
}
})
.directive('myEmojiSuggestions', function () {
return {
link: function($scope, element, attrs) {
$scope.$watchCollection('emojiCodes', function (codes) {
// var codes = $scope.$eval(attrs.myEmojiSuggestions);
var html = [];
var iconSize = Config.Mobile ? 26 : 20;
var emoticonCode, emoticonData, spritesheet, pos, categoryIndex;
var count = Math.min(5, codes.length);
var i, x, y;
for (i = 0; i < count; i++) {
emoticonCode = codes[i];
if (emoticonCode.code) {
emoticonCode = emoticonCode.code;
}
if (emoticonData = Config.Emoji[emoticonCode]) {
spritesheet = EmojiHelper.spritesheetPositions[emoticonCode];
categoryIndex = spritesheet[0];
pos = spritesheet[1];
x = iconSize * spritesheet[3];
y = iconSize * spritesheet[2];
html.push('<li><a class="composer_emoji_option" data-code="' + encodeEntities(emoticonCode) + '"><i class="emoji emoji-w', iconSize, ' emoji-spritesheet-' + categoryIndex + '" style="background-position: -' + x + 'px -' + y + 'px;"></i><span class="composer_emoji_shortcut">:' + encodeEntities(emoticonData[1][0]) + ':</span></a></li>');
}
}
onContentLoaded(function () {
element.html(html);
});
});
}
};
})
.directive('myInlineResults', function () {
return {
templateUrl: templateUrl('inline_results'),
scope: {
botResults: '=myInlineResults'
},
link: function ($scope, element, attrs) {
$scope.$watch('botResults.results.length', function (show) {
console.log($scope.botResults, show);
});
}
}
})

145
app/js/message_composer.js

@ -674,38 +674,19 @@ EmojiPanel.prototype.update = function () {
function MessageComposer (textarea, options) { function MessageComposer (textarea, options) {
var self = this;
this.textareaEl = $(textarea); this.textareaEl = $(textarea);
this.setUpInput(); this.setUpInput();
this.autoCompleteWrapEl = $('<div class="composer_dropdown_wrap"></div>').appendTo(document.body); this.autoCompleteWrapEl = $('<div class="composer_dropdown_wrap"></div>').appendTo(document.body);
this.autoCompleteEl = $('<ul class="composer_dropdown dropdown-menu"></ul>').appendTo(this.autoCompleteWrapEl); var autoCompleteEl = $('<div></div>').appendTo(this.autoCompleteWrapEl);
this.scroller = new Scroller(this.autoCompleteEl, {maxHeight: 180}); options.dropdownDirective(div, function (scope, autoCompleteEl) {
self.autoCompleteEl = autoCompleteEl;
var self = this; self.autoCompleteScope = scope;
this.autoCompleteEl.on('mousedown', function (e) { self.setUpAutoComplete();
e = e.originalEvent || e;
var target = $(e.target), mention, code, command;
if (target[0].tagName != 'A') {
target = $(target[0].parentNode);
}
if (code = target.attr('data-code')) {
if (self.onEmojiSelected) {
self.onEmojiSelected(code, true);
}
EmojiHelper.pushPopularEmoji(code);
}
if (mention = target.attr('data-mention')) {
self.onMentionSelected(mention);
}
if (command = target.attr('data-command')) {
if (self.onCommandSelected) {
self.onCommandSelected(command);
}
self.hideSuggestions();
}
return cancelEvent(e);
}); });
this.isActive = false; this.isActive = false;
@ -714,10 +695,9 @@ function MessageComposer (textarea, options) {
this.onMessageSubmit = options.onMessageSubmit; this.onMessageSubmit = options.onMessageSubmit;
this.getSendOnEnter = options.getSendOnEnter; this.getSendOnEnter = options.getSendOnEnter;
this.onFilePaste = options.onFilePaste; this.onFilePaste = options.onFilePaste;
this.onCommandSend = options.onCommandSend;
this.mentions = options.mentions; this.mentions = options.mentions;
this.commands = options.commands; this.commands = options.commands;
this.getPeerImage = options.getPeerImage;
this.onCommandSend = options.onCommandSend;
} }
MessageComposer.autoCompleteRegEx = /(\s|^)(:|@|\/)([A-Za-z0-9\-\+\*@_]*)$/; MessageComposer.autoCompleteRegEx = /(\s|^)(:|@|\/)([A-Za-z0-9\-\+\*@_]*)$/;
@ -738,6 +718,35 @@ MessageComposer.prototype.setUpInput = function () {
} }
} }
MessageComposer.prototype.setUpAutoComplete = function () {
this.scroller = new Scroller(this.autoCompleteEl, {maxHeight: 180});
var self = this;
this.autoCompleteEl.on('mousedown', function (e) {
e = e.originalEvent || e;
var target = $(e.target), mention, code, command;
if (target[0].tagName != 'A') {
target = $(target[0].parentNode);
}
if (code = target.attr('data-code')) {
if (self.onEmojiSelected) {
self.onEmojiSelected(code, true);
}
EmojiHelper.pushPopularEmoji(code);
}
if (mention = target.attr('data-mention')) {
self.onMentionSelected(mention);
}
if (command = target.attr('data-command')) {
if (self.onCommandSelected) {
self.onCommandSelected(command);
}
self.hideSuggestions();
}
return cancelEvent(e);
});
}
MessageComposer.prototype.setUpRich = function () { MessageComposer.prototype.setUpRich = function () {
this.textareaEl.hide(); this.textareaEl.hide();
this.richTextareaEl = $('<div class="composer_rich_textarea" contenteditable="true" dir="auto"></div>'); this.richTextareaEl = $('<div class="composer_rich_textarea" contenteditable="true" dir="auto"></div>');
@ -1353,8 +1362,7 @@ MessageComposer.prototype.blur = function () {
} }
} }
MessageComposer.prototype.renderSuggestions = function (html) { MessageComposer.prototype.renderSuggestions = function () {
this.autoCompleteEl.html(html.join(''));
this.autoCompleteWrapEl.show(); this.autoCompleteWrapEl.show();
this.scroller.reinit(); this.scroller.reinit();
this.updatePosition(); this.updatePosition();
@ -1362,72 +1370,35 @@ MessageComposer.prototype.renderSuggestions = function (html) {
} }
MessageComposer.prototype.showEmojiSuggestions = function (codes) { MessageComposer.prototype.showEmojiSuggestions = function (codes) {
var html = []; var self = this;
var iconSize = Config.Mobile ? 26 : 20; this.autoCompleteScope.$apply(function () {
self.autoCompleteScope.type = 'emoji';
var emoticonCode, emoticonData, spritesheet, pos, categoryIndex; self.autoCompleteScope.emojiCodes = codes;
var count = Math.min(5, codes.length); });
var i, x, y; onContentLoaded(function () {
self.renderSuggestions();
for (i = 0; i < count; i++) { });
emoticonCode = codes[i];
if (emoticonCode.code) {
emoticonCode = emoticonCode.code;
}
if (emoticonData = Config.Emoji[emoticonCode]) {
spritesheet = EmojiHelper.spritesheetPositions[emoticonCode];
categoryIndex = spritesheet[0];
pos = spritesheet[1];
x = iconSize * spritesheet[3];
y = iconSize * spritesheet[2];
html.push('<li><a class="composer_emoji_option" data-code="' + encodeEntities(emoticonCode) + '"><i class="emoji emoji-w', iconSize, ' emoji-spritesheet-' + categoryIndex + '" style="background-position: -' + x + 'px -' + y + 'px;"></i><span class="composer_emoji_shortcut">:' + encodeEntities(emoticonData[1][0]) + ':</span></a></li>');
}
}
this.renderSuggestions(html);
} }
MessageComposer.prototype.showMentionSuggestions = function (users) { MessageComposer.prototype.showMentionSuggestions = function (users) {
var html = [];
var user;
var count = users.length;
var i;
for (i = 0; i < count; i++) {
user = users[i];
html.push('<li><a class="composer_mention_option" data-mention="' + user.username + '"><span class="composer_user_photo" data-user-id="' + user.id + '"></span><span class="composer_user_name">' + user.rFullName + '</span><span class="composer_user_mention">@' + user.username + '</span></a></li>');
}
this.renderSuggestions(html);
var self = this; var self = this;
this.autoCompleteEl.find('.composer_user_photo').each(function (k, element) { this.autoCompleteScope.$apply(function () {
self.getPeerImage($(element), element.getAttribute('data-user-id')); self.autoCompleteScope.type = 'mentions';
self.autoCompleteScope.mentionUsers = users;
});
onContentLoaded(function () {
self.renderSuggestions();
}); });
} }
MessageComposer.prototype.showCommandsSuggestions = function (commands) { MessageComposer.prototype.showCommandsSuggestions = function (commands) {
var html = [];
var command;
var count = Math.min(200, commands.length);
var i;
for (i = 0; i < count; i++) {
command = commands[i];
html.push('<li><a class="composer_command_option" data-command="' + encodeEntities(command.value) + '"><span class="composer_user_photo" data-user-id="' + command.botID + '"></span><span class="composer_command_value">' + encodeEntities(command.value) + '</span><span class="composer_command_desc">' + command.rDescription + '</span></a></li>');
}
this.renderSuggestions(html);
var self = this; var self = this;
var usedImages = {}; this.autoCompleteScope.$apply(function () {
this.autoCompleteEl.find('.composer_user_photo').each(function (k, element) { self.autoCompleteScope.type = 'commands';
var noReplace = true; self.autoCompleteScope.commands = commands;
var botID = element.getAttribute('data-user-id'); });
if (!usedImages[botID]) { onContentLoaded(function () {
usedImages[botID] = true; self.renderSuggestions();
noReplace = false;
}
self.getPeerImage($(element), botID, noReplace);
}); });
} }

27
app/js/messages_manager.js

@ -3010,7 +3010,31 @@ angular.module('myApp.services')
}; };
} }
}) })
}) });
var inlineResults = {};
function getInlineResults (botID, query, offset) {
return MtpApiManager.invokeApi('messages.getInlineBotResults', {
bot: AppUsersManager.getUserInput(botID),
query: query,
offset: offset
}).then(function(botResults) {
var queryID = botResults.query_id;
delete botResults._;
delete botResults.flags;
delete botResults.query_id;
angular.forEach(botResults.results, function (result) {
var qID = queryID + '_' + result.id;
result.qID = qID;
result.rTitle = RichTextProcessor.wrapRichText(result.title, {noLinebreaks: true, noLinks: true});
result.rDescription = RichTextProcessor.wrapRichText(result.description, {noLinebreaks: true, noLinks: true});
inlineResults[qID] = result;
});
return botResults;
});
}
return { return {
getConversations: getConversations, getConversations: getConversations,
@ -3033,6 +3057,7 @@ angular.module('myApp.services')
getMessagePeer: getMessagePeer, getMessagePeer: getMessagePeer,
getMessageThumb: getMessageThumb, getMessageThumb: getMessageThumb,
clearDialogCache: clearDialogCache, clearDialogCache: clearDialogCache,
getInlineResults: getInlineResults,
wrapForDialog: wrapForDialog, wrapForDialog: wrapForDialog,
wrapForHistory: wrapForHistory, wrapForHistory: wrapForHistory,
wrapReplyMarkup: wrapReplyMarkup, wrapReplyMarkup: wrapReplyMarkup,

5
app/js/services.js

@ -949,7 +949,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
if (peerID > 0) { if (peerID > 0) {
var bot = AppUsersManager.getUser(peerID); var bot = AppUsersManager.getUser(peerID);
if (bot.pFlags.bot && bot.bot_inline_placeholder !== undefined) { if (bot.pFlags.bot && bot.bot_inline_placeholder !== undefined) {
return bot.bot_inline_placeholder; return qSync.when({
id: peerID,
placeholder: bot.bot_inline_placeholder
});
} }
} }
return $q.reject(); return $q.reject();

79
app/less/app.less

@ -434,6 +434,10 @@ a {
animation: infinite_rotation 0.8s linear infinite; animation: infinite_rotation 0.8s linear infinite;
} }
.composer_progress_icon & {
stroke: rgba(0,0,0,0.3);
}
.progress-arc-percent & { .progress-arc-percent & {
stroke: #FFF; stroke: #FFF;
stroke: rgba(255,255,255,0.95); stroke: rgba(255,255,255,0.95);
@ -449,14 +453,23 @@ a {
.stop0 { .stop0 {
stop-opacity: 1.0; stop-opacity: 1.0;
stop-color: #68a4d1; stop-color: #68a4d1;
.composer_progress_icon & {
stop-color: rgba(0,0,0,0.3);
}
} }
.stop60 { .stop60 {
stop-opacity: 1.0; stop-opacity: 1.0;
stop-color: #68a4d1; stop-color: #68a4d1;
.composer_progress_icon & {
stop-color: rgba(0,0,0,0.3);
}
} }
.stop100 { .stop100 {
stop-opacity: 0.0; stop-opacity: 0.0;
stop-color: #68a4d1; stop-color: #68a4d1;
.composer_progress_icon & {
stop-color: rgba(0,0,0,0.3);
}
} }
/* Infinite rotation */ /* Infinite rotation */
@ -2485,7 +2498,27 @@ img.img_fullsize {
} }
/* Message composer */ /* Message composer */
.composer_progress_icon {
display: block;
opacity: 0;
position: absolute;
right: 3px;
top: 2px;
cursor: pointer;
padding: 0;
width: 22px;
height: 22px;
margin-top: 1px;
transition: opacity cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.2s;
pointer-events: none;
.composer_progress_enabled & {
opacity: 1;
}
}
.composer_emoji_insert_btn { .composer_emoji_insert_btn {
opacity: 1;
display: block; display: block;
position: absolute; position: absolute;
right: 3px; right: 3px;
@ -2496,7 +2529,13 @@ img.img_fullsize {
width: 22px; width: 22px;
height: 22px; height: 22px;
margin-top: 1px; margin-top: 1px;
transition: opacity cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.2s;
.composer_progress_enabled & {
opacity: 0;
}
} }
.icon-emoji { .icon-emoji {
display: inline-block; display: inline-block;
width: 22px; width: 22px;
@ -3093,6 +3132,46 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
} }
} }
.im_send_form_inline_results {
position: relative;
display: none;
}
.inline_results_wrap {
position: absolute;
bottom: 0;
background: #FFF;
border: 0;
.box-shadow(0px 1px 1px 0px rgba(60,75,87,0.27));
// margin-top: -5px;
// margin-left: -1px;
// position: static;
display: block;
float: none;
top: auto;
left: auto;
border: 0;
border-radius: 0;
padding: 0;
margin: 0;
z-index: auto;
}
.inline_result_wrap {
display: block;
font-size: 13px;
line-height: 15px;
padding: 4px 10px;
color: #52719a;
&:hover,
&.composer_autocomplete_option_active {
color: #52719a;
background: #f2f6fa;
}
}
.error_modal_window { .error_modal_window {
.modal-dialog { .modal-dialog {

27
app/partials/desktop/composer_dropdown.html

@ -0,0 +1,27 @@
<div ng-switch="type">
<ul ng-switch-when="mentions" class="composer_dropdown">
<li ng-repeat="user in users">
<a class="composer_mention_option" data-mention="{{user.username}}">
<span class="composer_user_photo" my-peer-photolink="user.id" img-class="composer_user_photo"></span>
<span class="composer_user_name" ng-bind-html="user.rFullName"></span>
<span class="composer_user_mention" ng-bind="'@' + user.username"></span>
</a>
</li>
</ul>
<ul ng-switch-when="commands" class="composer_dropdown">
<li ng-repeat="command in commands track by (command.botID + command.value)">
<a class="composer_command_option" data-command="{{command.value}}">
<span class="composer_user_photo" my-peer-photolink="command.botID" img-class="composer_user_photo"></span>
<span class="composer_command_value" ng-bind="command.value"></span>
<span class="composer_command_desc" ng-bind-html="command.rDescription"></span>
</a>
</li>
</ul>
<ul ng-switch-when="emoji" my-emoji-suggestions="emojiCodes" class="composer_dropdown"></ul>
<div ng-switch-when="inline" my-inline-results="botResults"></div>
</div>

5
app/partials/desktop/im.html

@ -182,7 +182,9 @@
</a> </a>
<a class="pull-left im_panel_own_photo" my-peer-photolink="draftMessage.isBroadcast ? historyPeer.id : ownID" img-class="im_panel_own_photo" watch="true" ng-click="openSettings()" no-open="true"></a> <a class="pull-left im_panel_own_photo" my-peer-photolink="draftMessage.isBroadcast ? historyPeer.id : ownID" img-class="im_panel_own_photo" watch="true" ng-click="openSettings()" no-open="true"></a>
<form my-send-form draft-message="draftMessage" mentions="mentions" commands="commands" class="im_send_form" ng-class="{im_send_form_empty: !draftMessage.text.length}"> <form my-send-form draft-message="draftMessage" mentions="mentions" commands="commands" class="im_send_form" ng-class="{im_send_form_empty: !draftMessage.text.length, composer_progress_enabled: draftMessage.inlineProgress}">
<div class="im_send_form_inline_results" my-inline-results="inlineResults"></div>
<div class="im_send_reply_wrap" ng-if="draftMessage.replyToMessage != null"> <div class="im_send_reply_wrap" ng-if="draftMessage.replyToMessage != null">
<a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear()"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a> <a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear()"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a>
@ -196,6 +198,7 @@
<div class="im_send_field_wrap" ng-class="historyState.replyKeyboard._ == 'replyKeyboardMarkup' ? 'im_send_field_wrap_2ndbtn' : ''"> <div class="im_send_field_wrap" ng-class="historyState.replyKeyboard._ == 'replyKeyboardMarkup' ? 'im_send_field_wrap_2ndbtn' : ''">
<a class="composer_emoji_insert_btn"><i class="icon icon-emoji"></i></a> <a class="composer_emoji_insert_btn"><i class="icon icon-emoji"></i></a>
<div class="composer_progress_icon" my-arc-progress width="22" stroke="2.5"></div>
<a class="composer_command_btn" ng-show="!historyState.replyKeyboard && commands.list.length > 0 && (!draftMessage.text.length || draftMessage.text[0] == '/')" ng-mousedown="toggleSlash($event)" ng-class="draftMessage.text[0] == '/' ? 'active' : ''"><i class="icon icon-slash"></i></a> <a class="composer_command_btn" ng-show="!historyState.replyKeyboard && commands.list.length > 0 && (!draftMessage.text.length || draftMessage.text[0] == '/')" ng-mousedown="toggleSlash($event)" ng-class="draftMessage.text[0] == '/' ? 'active' : ''"><i class="icon icon-slash"></i></a>
<a class="composer_keyboard_btn" ng-show="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-mousedown="replyKeyboardToggle($event)" ng-class="!historyState.replyKeyboard.pFlags.hidden ? 'active' : ''"><i class="icon icon-keyboard"></i></a> <a class="composer_keyboard_btn" ng-show="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-mousedown="replyKeyboardToggle($event)" ng-class="!historyState.replyKeyboard.pFlags.hidden ? 'active' : ''"><i class="icon icon-keyboard"></i></a>

15
app/partials/desktop/inline_results.html

@ -0,0 +1,15 @@
<div class="inline_results_wrap">
<div class="inline_result_wrap" ng-repeat="result in botResults.results track by result.qID" ng-switch="result.type">
<div ng-switch-default class="inline_result_">
<div class="inline_article_thumb_wrap pull-left" ng-switch="result.thumbUrl.length > 0">
<img ng-switch-when="true" ng-src="{{result.thumbUrl}}"/>
<div ng-switch-default class="inline_article_thumb_initials" ng-bind="result.initials"></div>
</div>
<div class="inline_article_content_wrap">
<div class="inline_article_title" ng-if="::result.title.length > 0" ng-bind-html="::result.rTitle"></div>
<div class="inline_article_description" ng-if="::result.description.length > 0" ng-bind-html="::result.rDescription"></div>
<div class="inline_article_url" ng-if="::result.url.length > 0" ng-bind="::result.url"></div>
</div>
</div>
</div>
</div>
Loading…
Cancel
Save