diff --git a/upload/ajax/items.php b/upload/ajax/items.php
new file mode 100644
index 00000000..0735c144
--- /dev/null
+++ b/upload/ajax/items.php
@@ -0,0 +1,76 @@
+get_contacts(userid());
+ $new_friends = array();
+ if($friends)
+ {
+ foreach($friends as $friend)
+ {
+ $new_friends[] = array(
+ 'value' => $friend['contact_userid'],
+ 'name' => name($friend),
+ 'image' => $userquery->avatar($friend,'small'),
+ );
+ }
+
+ echo json_encode($new_friends);
+ }
+ }
+ break;
+ case "groups":
+ case "group":
+ {
+ $groups = $cbgroup->user_joined_groups(userid());
+
+ $new_groups = array();
+ if($groups)
+ {
+ foreach($groups as $group)
+ {
+ $new_groups[] = array(
+ 'value' => $group['group_id'],
+ 'name' => $group['group_name'],
+ 'image' => $cbgroup->get_group_thumb($group,'small')
+ );
+ }
+
+ echo json_encode($new_groups);
+ }
+ }
+ break;
+ case "mentions":
+ {
+ $friends = $userquery->get_contacts(userid());
+ $new_friends = array();
+ if($friends)
+ {
+ foreach($friends as $friend)
+ {
+ $new_friends[] = array(
+ 'id' => $friend['contact_userid'],
+ 'name' => name($friend),
+ 'avatar' => $userquery->avatar($friend,'small'),
+ 'type' => 'user'
+ );
+ }
+
+ echo json_encode($new_friends);
+ }
+ }
+ break;
+
+ default:
+ exit(json_encode(array('err' => array(lang('Invalid request')))));
+}
+?>
\ No newline at end of file
diff --git a/upload/styles/cbv3/js/css/jquery.mentionsInput.css b/upload/styles/cbv3/js/css/jquery.mentionsInput.css
new file mode 100644
index 00000000..0e1af877
--- /dev/null
+++ b/upload/styles/cbv3/js/css/jquery.mentionsInput.css
@@ -0,0 +1,120 @@
+
+.mentions-input-box {
+ position: relative;
+ background: #fff;
+}
+
+.mentions-input-box textarea {
+ width: 100%;
+ display: block;
+ height: 18px;
+
+
+ overflow: hidden;
+ background: transparent;
+ position: relative;
+ outline: 0;
+ resize: none;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.mentions-input-box .mentions-autocomplete-list {
+ display: none;
+ background: #fff;
+ border: 1px solid #b2b2b2;
+ position: absolute;
+ left: 0;
+ right: 0;
+ z-index: 10000;
+ margin-top: -2px;
+
+ border-radius:5px;
+ border-top-right-radius:0;
+ border-top-left-radius:0;
+
+ -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.148438);
+ -moz-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.148438);
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.148438);
+}
+
+.mentions-input-box .mentions-autocomplete-list ul {
+ margin: 0;
+ padding: 0;
+}
+
+.mentions-input-box .mentions-autocomplete-list li {
+ background-color: #fff;
+ padding: 0 5px;
+ margin: 0;
+ width: auto;
+ border-bottom: 1px solid #eee;
+ height: 26px;
+ line-height: 26px;
+ overflow: hidden;
+ cursor: pointer;
+ list-style: none;
+ white-space: nowrap;
+}
+
+.mentions-input-box .mentions-autocomplete-list li:last-child {
+ border-radius:5px;
+}
+
+.mentions-input-box .mentions-autocomplete-list li > img,
+.mentions-input-box .mentions-autocomplete-list li > div.icon {
+ width: 16px;
+ height: 16px;
+ float: left;
+ margin-top:5px;
+ margin-right: 5px;
+ -moz-background-origin:3px;
+
+ border-radius:3px;
+}
+
+.mentions-input-box .mentions-autocomplete-list li em {
+ font-weight: bold;
+ font-style: none;
+}
+
+.mentions-input-box .mentions-autocomplete-list li:hover,
+.mentions-input-box .mentions-autocomplete-list li.active {
+ background-color: #f2f2f2;
+}
+
+.mentions-input-box .mentions-autocomplete-list li b {
+ background: #ffff99;
+ font-weight: normal;
+}
+
+.mentions-input-box .mentions {
+ position: absolute;
+ left: 6px;
+ right: 0;
+ top: 9px;
+ bottom: 0;
+ padding: 0px;
+ color: #fff;
+ overflow: hidden;
+ font-size:14px;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+.mentions-input-box .mentions > div {
+ color: #fff;
+ white-space: pre-wrap;
+ width: 100%;
+}
+
+.mentions-input-box .mentions > div > strong {
+ font-weight:normal;
+ background: #d8dfea;
+}
+
+.mentions-input-box .mentions > div > strong > span {
+ filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0);
+}
diff --git a/upload/styles/cbv3/js/jquery.elastic.js b/upload/styles/cbv3/js/jquery.elastic.js
new file mode 100644
index 00000000..c5e857f6
--- /dev/null
+++ b/upload/styles/cbv3/js/jquery.elastic.js
@@ -0,0 +1,162 @@
+/**
+* @name Elastic
+* @descripton Elastic is jQuery plugin that grow and shrink your textareas automatically
+* @version 1.6.11
+* @requires jQuery 1.2.6+
+*
+* @author Jan Jarfalk
+* @author-email jan.jarfalk@unwrongest.com
+* @author-website http://www.unwrongest.com
+*
+* @licence MIT License - http://www.opensource.org/licenses/mit-license.php
+*/
+
+(function($){
+ jQuery.fn.extend({
+ elastic: function() {
+
+ // We will create a div clone of the textarea
+ // by copying these attributes from the textarea to the div.
+ var mimics = [
+ 'paddingTop',
+ 'paddingRight',
+ 'paddingBottom',
+ 'paddingLeft',
+ 'fontSize',
+ 'lineHeight',
+ 'fontFamily',
+ 'width',
+ 'fontWeight',
+ 'border-top-width',
+ 'border-right-width',
+ 'border-bottom-width',
+ 'border-left-width',
+ 'borderTopStyle',
+ 'borderTopColor',
+ 'borderRightStyle',
+ 'borderRightColor',
+ 'borderBottomStyle',
+ 'borderBottomColor',
+ 'borderLeftStyle',
+ 'borderLeftColor'
+ ];
+
+ return this.each( function() {
+
+ // Elastic only works on textareas
+ if ( this.type !== 'textarea' ) {
+ return false;
+ }
+
+ var $textarea = jQuery(this),
+ $twin = jQuery('
').css({
+ 'position' : 'absolute',
+ 'display' : 'none',
+ 'word-wrap' : 'break-word',
+ 'white-space' :'pre-wrap'
+ }),
+ lineHeight = parseInt($textarea.css('line-height'),10) || parseInt($textarea.css('font-size'),'10'),
+ minheight = parseInt($textarea.css('height'),10) || lineHeight*3,
+ maxheight = parseInt($textarea.css('max-height'),10) || Number.MAX_VALUE,
+ goalheight = 0;
+
+ // Opera returns max-height of -1 if not set
+ if (maxheight < 0) { maxheight = Number.MAX_VALUE; }
+
+ // Append the twin to the DOM
+ // We are going to meassure the height of this, not the textarea.
+ $twin.appendTo($textarea.parent());
+
+ // Copy the essential styles (mimics) from the textarea to the twin
+ var i = mimics.length;
+ while(i--){
+ $twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));
+ }
+
+ // Updates the width of the twin. (solution for textareas with widths in percent)
+ function setTwinWidth(){
+ var curatedWidth = Math.floor(parseInt($textarea.width(),10));
+ if($twin.width() !== curatedWidth){
+ $twin.css({'width': curatedWidth + 'px'});
+
+ // Update height of textarea
+ update(true);
+ }
+ }
+
+ // Sets a given height and overflow state on the textarea
+ function setHeightAndOverflow(height, overflow){
+
+ var curratedHeight = Math.floor(parseInt(height,10));
+ if($textarea.height() !== curratedHeight){
+ $textarea.css({'height': curratedHeight + 'px','overflow':overflow});
+ }
+ }
+
+ // This function will update the height of the textarea if necessary
+ function update(forced) {
+
+ // Get curated content from the textarea.
+ var textareaContent = $textarea.val().replace(/&/g,'&').replace(/ {2}/g, ' ').replace(/<|>/g, '>').replace(/\n/g, '
');
+
+ // Compare curated content with curated twin.
+ var twinContent = $twin.html().replace(/
/ig,'
');
+
+ if(forced || textareaContent+' ' !== twinContent){
+
+ // Add an extra white space so new rows are added when you are at the end of a row.
+ $twin.html(textareaContent+' ');
+
+ // Change textarea height if twin plus the height of one line differs more than 3 pixel from textarea height
+ if(Math.abs($twin.height() + lineHeight - $textarea.height()) > 3){
+
+ var goalheight = $twin.height()+lineHeight;
+ if(goalheight >= maxheight) {
+ setHeightAndOverflow(maxheight,'auto');
+ } else if(goalheight <= minheight) {
+ setHeightAndOverflow(minheight,'hidden');
+ } else {
+ setHeightAndOverflow(goalheight,'hidden');
+ }
+
+ }
+
+ }
+
+ }
+
+ // Hide scrollbars
+ $textarea.css({'overflow':'hidden'});
+
+ // Update textarea size on keyup, change, cut and paste
+ $textarea.bind('keyup change cut paste', function(){
+ update();
+ });
+
+ // Update width of twin if browser or textarea is resized (solution for textareas with widths in percent)
+ $(window).bind('resize', setTwinWidth);
+ $textarea.bind('resize', setTwinWidth);
+ $textarea.bind('update', update);
+
+ // Compact textarea on blur
+ $textarea.bind('blur',function(){
+ if($twin.height() < maxheight){
+ if($twin.height() > minheight) {
+ $textarea.height($twin.height());
+ } else {
+ $textarea.height(minheight);
+ }
+ }
+ });
+
+ // And this line is to catch the browser paste event
+ $textarea.bind('input paste',function(e){ setTimeout( update, 250); });
+
+ // Run update once when elastic is initialized
+ update();
+
+ });
+
+ }
+ });
+})(jQuery);
\ No newline at end of file
diff --git a/upload/styles/cbv3/js/jquery.events.input.js b/upload/styles/cbv3/js/jquery.events.input.js
new file mode 100644
index 00000000..9b2bbbfb
--- /dev/null
+++ b/upload/styles/cbv3/js/jquery.events.input.js
@@ -0,0 +1,132 @@
+/*
+ jQuery `input` special event v1.0
+
+ http://whattheheadsaid.com/projects/input-special-event
+
+ (c) 2010-2011 Andy Earnshaw
+ MIT license
+ www.opensource.org/licenses/mit-license.php
+
+ Modified by Kenneth Auchenberg
+ * Disabled usage of onPropertyChange event in IE, since its a bit delayed, if you type really fast.
+*/
+
+(function($) {
+ // Handler for propertychange events only
+ function propHandler() {
+ var $this = $(this);
+ if (window.event.propertyName == "value" && !$this.data("triggering.inputEvent")) {
+ $this.data("triggering.inputEvent", true).trigger("input");
+ window.setTimeout(function () {
+ $this.data("triggering.inputEvent", false);
+ }, 0);
+ }
+ }
+
+ $.event.special.input = {
+ setup: function(data, namespaces) {
+ var timer,
+ // Get a reference to the element
+ elem = this,
+ // Store the current state of the element
+ state = elem.value,
+ // Create a dummy element that we can use for testing event support
+ tester = document.createElement(this.tagName),
+ // Check for native oninput
+ oninput = "oninput" in tester || checkEvent(tester),
+ // Check for onpropertychange
+ onprop = "onpropertychange" in tester,
+ // Generate a random namespace for event bindings
+ ns = "inputEventNS" + ~~(Math.random() * 10000000),
+ // Last resort event names
+ evts = ["focus", "blur", "paste", "cut", "keydown", "drop", ""].join("." + ns + " ");
+
+ function checkState() {
+ var $this = $(elem);
+ if (elem.value != state && !$this.data("triggering.inputEvent")) {
+ state = elem.value;
+
+ $this.data("triggering.inputEvent", true).trigger("input");
+ window.setTimeout(function () {
+ $this.data("triggering.inputEvent", false);
+ }, 0);
+ }
+ }
+
+ // Set up a function to handle the different events that may fire
+ function handler(e) {
+ // When focusing, set a timer that polls for changes to the value
+ if (e.type == "focus") {
+ checkState();
+ clearInterval(timer);
+ timer = window.setInterval(checkState, 250);
+ } else if (e.type == "blur") {
+ // When blurring, cancel the aforeset timer
+ window.clearInterval(timer);
+ } else {
+ // For all other events, queue a timer to check state ASAP
+ window.setTimeout(checkState, 0);
+ }
+ }
+
+ // Bind to native event if available
+ if (oninput) {
+ return false;
+// } else if (onprop) {
+// // Else fall back to propertychange if available
+// $(this).find("input, textarea").andSelf().filter("input, textarea").bind("propertychange." + ns, propHandler);
+ } else {
+ // Else clutch at straws!
+ $(this).find("input, textarea").andSelf().filter("input, textarea").bind(evts, handler);
+ }
+ $(this).data("inputEventHandlerNS", ns);
+ },
+ teardown: function () {
+ var elem = $(this);
+ elem.find("input, textarea").unbind(elem.data("inputEventHandlerNS"));
+ elem.data("inputEventHandlerNS", "");
+ }
+ };
+
+ // Setup our jQuery shorthand method
+ $.fn.input = function (handler) {
+ return handler ? this.bind("input", handler) : this.trigger("input");
+ };
+
+ /*
+ The following function tests the element for oninput support in Firefox. Many thanks to
+ http://blog.danielfriesen.name/2010/02/16/html5-browser-maze-oninput-support/
+ */
+ function checkEvent(el) {
+ // First check, for if Firefox fixes its issue with el.oninput = function
+ el.setAttribute("oninput", "return");
+ if (typeof el.oninput == "function") {
+ return true;
+ }
+ // Second check, because Firefox doesn't map oninput attribute to oninput property
+ try {
+
+ // "* Note * : Disabled focus and dispatch of keypress event due to conflict with DOMready, which resulted in scrolling down to the bottom of the page, possibly because layout wasn't finished rendering.
+ var e = document.createEvent("KeyboardEvent"),
+ ok = false,
+ tester = function(e) {
+ ok = true;
+ e.preventDefault();
+ e.stopPropagation();
+ };
+
+ // e.initKeyEvent("keypress", true, true, window, false, false, false, false, 0, "e".charCodeAt(0));
+
+ document.body.appendChild(el);
+ el.addEventListener("input", tester, false);
+ // el.focus();
+ // el.dispatchEvent(e);
+ el.removeEventListener("input", tester, false);
+ document.body.removeChild(el);
+ return ok;
+
+ } catch(error) {
+
+ }
+ }
+})(jQuery);
\ No newline at end of file
diff --git a/upload/styles/cbv3/js/jquery.mentionsInput.js b/upload/styles/cbv3/js/jquery.mentionsInput.js
new file mode 100644
index 00000000..50c593f1
--- /dev/null
+++ b/upload/styles/cbv3/js/jquery.mentionsInput.js
@@ -0,0 +1,423 @@
+/*
+ * Mentions Input
+ * Version 1.0.2
+ * Written by: Kenneth Auchenberg (Podio)
+ *
+ * Using underscore.js
+ *
+ * License: MIT License - http://www.opensource.org/licenses/mit-license.php
+ */
+
+(function ($, _, undefined) {
+
+ // Settings
+ var KEY = { BACKSPACE : 8, TAB : 9, RETURN : 13, ESC : 27, LEFT : 37, UP : 38, RIGHT : 39, DOWN : 40, COMMA : 188, SPACE : 32, HOME : 36, END : 35 }; // Keys "enum"
+ var defaultSettings = {
+ triggerChar : '@',
+ onDataRequest : $.noop,
+ minChars : 2,
+ showAvatars : true,
+ elastic : true,
+ classes : {
+ autoCompleteItemActive : "active"
+ },
+ templates : {
+ wrapper : _.template(''),
+ autocompleteList : _.template(''),
+ autocompleteListItem : _.template('<%= content %>'),
+ autocompleteListItemAvatar : _.template('
'),
+ autocompleteListItemIcon : _.template(''),
+ mentionsOverlay : _.template(''),
+ mentionItemSyntax : _.template('@[<%= value %>:<%= type %>:<%= id %>]'),
+ mentionItemHighlight : _.template('<%= value %>')
+ }
+ };
+
+ var utils = {
+ htmlEncode : function (str) {
+ return _.escape(str);
+ },
+ highlightTerm : function (value, term) {
+ if (!term && !term.length) {
+ return value;
+ }
+ return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1");
+ },
+ setCaratPosition : function (domNode, caretPos) {
+ if (domNode.createTextRange) {
+ var range = domNode.createTextRange();
+ range.move('character', caretPos);
+ range.select();
+ } else {
+ if (domNode.selectionStart) {
+ domNode.focus();
+ domNode.setSelectionRange(caretPos, caretPos);
+ } else {
+ domNode.focus();
+ }
+ }
+ },
+ rtrim: function(string) {
+ return string.replace(/\s+$/,"");
+ }
+ };
+
+ var MentionsInput = function (settings) {
+
+ var domInput, elmInputBox, elmInputWrapper, elmAutocompleteList, elmWrapperBox, elmMentionsOverlay, elmActiveAutoCompleteItem;
+ var mentionsCollection = [];
+ var autocompleteItemCollection = {};
+ var inputBuffer = [];
+ var currentDataQuery;
+
+ settings = $.extend(true, {}, defaultSettings, settings );
+
+ function initTextarea() {
+ elmInputBox = $(domInput);
+
+ if (elmInputBox.attr('data-mentions-input') == 'true') {
+ return;
+ }
+
+ elmInputWrapper = elmInputBox.parent();
+ elmWrapperBox = $(settings.templates.wrapper());
+ elmInputBox.wrapAll(elmWrapperBox);
+ elmWrapperBox = elmInputWrapper.find('> div');
+
+ elmInputBox.attr('data-mentions-input', 'true');
+ elmInputBox.bind('keydown', onInputBoxKeyDown);
+ elmInputBox.bind('keypress', onInputBoxKeyPress);
+ elmInputBox.bind('input', onInputBoxInput);
+ elmInputBox.bind('click', onInputBoxClick);
+ elmInputBox.bind('blur', onInputBoxBlur);
+
+ // Elastic textareas, internal setting for the Dispora guys
+ if( settings.elastic ) {
+ elmInputBox.elastic();
+ }
+
+ }
+
+ function initAutocomplete() {
+ elmAutocompleteList = $(settings.templates.autocompleteList());
+ elmAutocompleteList.appendTo(elmWrapperBox);
+ elmAutocompleteList.delegate('li', 'mousedown', onAutoCompleteItemClick);
+ }
+
+ function initMentionsOverlay() {
+ elmMentionsOverlay = $(settings.templates.mentionsOverlay());
+ elmMentionsOverlay.prependTo(elmWrapperBox);
+ }
+
+ function updateValues() {
+ var syntaxMessage = getInputBoxValue();
+
+ _.each(mentionsCollection, function (mention) {
+ var textSyntax = settings.templates.mentionItemSyntax(mention);
+ syntaxMessage = syntaxMessage.replace(mention.value, textSyntax);
+ });
+
+ var mentionText = utils.htmlEncode(syntaxMessage);
+
+ _.each(mentionsCollection, function (mention) {
+ var formattedMention = _.extend({}, mention, {value: utils.htmlEncode(mention.value)});
+ var textSyntax = settings.templates.mentionItemSyntax(formattedMention);
+ var textHighlight = settings.templates.mentionItemHighlight(formattedMention);
+
+ mentionText = mentionText.replace(textSyntax, textHighlight);
+ });
+
+ mentionText = mentionText.replace(/\n/g, '
');
+ mentionText = mentionText.replace(/ {2}/g, ' ');
+
+ elmInputBox.data('messageText', syntaxMessage);
+ elmMentionsOverlay.find('div').html(mentionText);
+ }
+
+ function resetBuffer() {
+ inputBuffer = [];
+ }
+
+ function updateMentionsCollection() {
+ var inputText = getInputBoxValue();
+
+ mentionsCollection = _.reject(mentionsCollection, function (mention, index) {
+ return !mention.value || inputText.indexOf(mention.value) == -1;
+ });
+ mentionsCollection = _.compact(mentionsCollection);
+ }
+
+ function addMention(mention) {
+
+ var currentMessage = getInputBoxValue();
+
+ // Using a regex to figure out positions
+ var regex = new RegExp("\\" + settings.triggerChar + currentDataQuery, "gi");
+ regex.exec(currentMessage);
+
+ var startCaretPosition = regex.lastIndex - currentDataQuery.length - 1;
+ var currentCaretPosition = regex.lastIndex;
+
+ var start = currentMessage.substr(0, startCaretPosition);
+ var end = currentMessage.substr(currentCaretPosition, currentMessage.length);
+ var startEndIndex = (start + mention.value).length + 1;
+
+ mentionsCollection.push(mention);
+
+ // Cleaning before inserting the value, otherwise auto-complete would be triggered with "old" inputbuffer
+ resetBuffer();
+ currentDataQuery = '';
+ hideAutoComplete();
+
+ // Mentions & syntax message
+ var updatedMessageText = start + mention.value + ' ' + end;
+ elmInputBox.val(updatedMessageText);
+ updateValues();
+
+ // Set correct focus and selection
+ elmInputBox.focus();
+ utils.setCaratPosition(elmInputBox[0], startEndIndex);
+ }
+
+ function getInputBoxValue() {
+ return $.trim(elmInputBox.val());
+ }
+
+ function onAutoCompleteItemClick(e) {
+ var elmTarget = $(this);
+ var mention = autocompleteItemCollection[elmTarget.attr('data-uid')];
+
+ addMention(mention);
+
+ return false;
+ }
+
+ function onInputBoxClick(e) {
+ resetBuffer();
+ }
+
+ function onInputBoxBlur(e) {
+ hideAutoComplete();
+ }
+
+ function onInputBoxInput(e) {
+ updateValues();
+ updateMentionsCollection();
+ hideAutoComplete();
+
+ var triggerCharIndex = _.lastIndexOf(inputBuffer, settings.triggerChar);
+ if (triggerCharIndex > -1) {
+ currentDataQuery = inputBuffer.slice(triggerCharIndex + 1).join('');
+ currentDataQuery = utils.rtrim(currentDataQuery);
+
+ _.defer(_.bind(doSearch, this, currentDataQuery));
+ }
+ }
+
+ function onInputBoxKeyPress(e) {
+ if(e.keyCode !== KEY.BACKSPACE) {
+ var typedValue = String.fromCharCode(e.which || e.keyCode);
+ inputBuffer.push(typedValue);
+ }
+ }
+
+ function onInputBoxKeyDown(e) {
+
+ // This also matches HOME/END on OSX which is CMD+LEFT, CMD+RIGHT
+ if (e.keyCode == KEY.LEFT || e.keyCode == KEY.RIGHT || e.keyCode == KEY.HOME || e.keyCode == KEY.END) {
+ // Defer execution to ensure carat pos has changed after HOME/END keys
+ _.defer(resetBuffer);
+
+ // IE9 doesn't fire the oninput event when backspace or delete is pressed. This causes the highlighting
+ // to stay on the screen whenever backspace is pressed after a highlighed word. This is simply a hack
+ // to force updateValues() to fire when backspace/delete is pressed in IE9.
+ if (navigator.userAgent.indexOf("MSIE 9") > -1) {
+ _.defer(updateValues);
+ }
+
+ return;
+ }
+
+ if (e.keyCode == KEY.BACKSPACE) {
+ inputBuffer = inputBuffer.slice(0, -1 + inputBuffer.length); // Can't use splice, not available in IE
+ return;
+ }
+
+ if (!elmAutocompleteList.is(':visible')) {
+ return true;
+ }
+
+ switch (e.keyCode) {
+ case KEY.UP:
+ case KEY.DOWN:
+ var elmCurrentAutoCompleteItem = null;
+ if (e.keyCode == KEY.DOWN) {
+ if (elmActiveAutoCompleteItem && elmActiveAutoCompleteItem.length) {
+ elmCurrentAutoCompleteItem = elmActiveAutoCompleteItem.next();
+ } else {
+ elmCurrentAutoCompleteItem = elmAutocompleteList.find('li').first();
+ }
+ } else {
+ elmCurrentAutoCompleteItem = $(elmActiveAutoCompleteItem).prev();
+ }
+
+ if (elmCurrentAutoCompleteItem.length) {
+ selectAutoCompleteItem(elmCurrentAutoCompleteItem);
+ }
+
+ return false;
+
+ case KEY.RETURN:
+ case KEY.TAB:
+ if (elmActiveAutoCompleteItem && elmActiveAutoCompleteItem.length) {
+ elmActiveAutoCompleteItem.trigger('mousedown');
+ return false;
+ }
+
+ break;
+ }
+
+ return true;
+ }
+
+ function hideAutoComplete() {
+ elmActiveAutoCompleteItem = null;
+ elmAutocompleteList.empty().hide();
+ }
+
+ function selectAutoCompleteItem(elmItem) {
+ elmItem.addClass(settings.classes.autoCompleteItemActive);
+ elmItem.siblings().removeClass(settings.classes.autoCompleteItemActive);
+
+ elmActiveAutoCompleteItem = elmItem;
+ }
+
+ function populateDropdown(query, results) {
+ elmAutocompleteList.show();
+
+ // Filter items that has already been mentioned
+ var mentionValues = _.pluck(mentionsCollection, 'value');
+ results = _.reject(results, function (item) {
+ return _.include(mentionValues, item.name);
+ });
+
+ if (!results.length) {
+ hideAutoComplete();
+ return;
+ }
+
+ elmAutocompleteList.empty();
+ var elmDropDownList = $("").appendTo(elmAutocompleteList).hide();
+
+ _.each(results, function (item, index) {
+ var itemUid = _.uniqueId('mention_');
+
+ autocompleteItemCollection[itemUid] = _.extend({}, item, {value: item.name});
+
+ var elmListItem = $(settings.templates.autocompleteListItem({
+ 'id' : utils.htmlEncode(item.id),
+ 'display' : utils.htmlEncode(item.name),
+ 'type' : utils.htmlEncode(item.type),
+ 'content' : utils.highlightTerm(utils.htmlEncode((item.name)), query)
+ })).attr('data-uid', itemUid);
+
+ if (index === 0) {
+ selectAutoCompleteItem(elmListItem);
+ }
+
+ if (settings.showAvatars) {
+ var elmIcon;
+
+ if (item.avatar) {
+ elmIcon = $(settings.templates.autocompleteListItemAvatar({ avatar : item.avatar }));
+ } else {
+ elmIcon = $(settings.templates.autocompleteListItemIcon({ icon : item.icon }));
+ }
+ elmIcon.prependTo(elmListItem);
+ }
+ elmListItem = elmListItem.appendTo(elmDropDownList);
+ });
+
+ elmAutocompleteList.show();
+ elmDropDownList.show();
+ }
+
+ function doSearch(query) {
+ if (query && query.length && query.length >= settings.minChars) {
+ settings.onDataRequest.call(this, 'search', query, function (responseData) {
+ populateDropdown(query, responseData);
+ });
+ }
+ }
+
+ function resetInput() {
+ elmInputBox.val('');
+ mentionsCollection = [];
+ updateValues();
+ }
+
+ // Public methods
+ return {
+ init : function (domTarget) {
+
+ domInput = domTarget;
+
+ initTextarea();
+ initAutocomplete();
+ initMentionsOverlay();
+ resetInput();
+
+ if( settings.prefillMention ) {
+ addMention( settings.prefillMention );
+ }
+
+ },
+
+ val : function (callback) {
+ if (!_.isFunction(callback)) {
+ return;
+ }
+
+ var value = mentionsCollection.length ? elmInputBox.data('messageText') : getInputBoxValue();
+ callback.call(this, value);
+ },
+
+ reset : function () {
+ resetInput();
+ },
+
+ getMentions : function (callback) {
+ if (!_.isFunction(callback)) {
+ return;
+ }
+
+ callback.call(this, mentionsCollection);
+ }
+ };
+ };
+
+ $.fn.mentionsInput = function (method, settings) {
+
+ var outerArguments = arguments;
+
+ if (typeof method === 'object' || !method) {
+ settings = method;
+ }
+
+ return this.each(function () {
+ var instance = $.data(this, 'mentionsInput') || $.data(this, 'mentionsInput', new MentionsInput(settings));
+
+ if (_.isFunction(instance[method])) {
+ return instance[method].apply(this, Array.prototype.slice.call(outerArguments, 1));
+
+ } else if (typeof method === 'object' || !method) {
+ return instance.init.call(this, this);
+
+ } else {
+ $.error('Method ' + method + ' does not exist');
+ }
+
+ });
+ };
+
+})(jQuery, _);
diff --git a/upload/styles/cbv3/js/underscore.js b/upload/styles/cbv3/js/underscore.js
new file mode 100644
index 00000000..ad3a39ad
--- /dev/null
+++ b/upload/styles/cbv3/js/underscore.js
@@ -0,0 +1,5 @@
+// Underscore.js 1.4.2
+// http://underscorejs.org
+// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
+// Underscore may be freely distributed under the MIT license.
+(function(){var e=this,t=e._,n={},r=Array.prototype,i=Object.prototype,s=Function.prototype,o=r.push,u=r.slice,a=r.concat,f=r.unshift,l=i.toString,c=i.hasOwnProperty,h=r.forEach,p=r.map,d=r.reduce,v=r.reduceRight,m=r.filter,g=r.every,y=r.some,b=r.indexOf,w=r.lastIndexOf,E=Array.isArray,S=Object.keys,x=s.bind,T=function(e){if(e instanceof T)return e;if(!(this instanceof T))return new T(e);this._wrapped=e};typeof exports!="undefined"?(typeof module!="undefined"&&module.exports&&(exports=module.exports=T),exports._=T):e._=T,T.VERSION="1.4.2";var N=T.each=T.forEach=function(e,t,r){if(e==null)return;if(h&&e.forEach===h)e.forEach(t,r);else if(e.length===+e.length){for(var i=0,s=e.length;i2;e==null&&(e=[]);if(d&&e.reduce===d)return r&&(t=T.bind(t,r)),i?e.reduce(t,n):e.reduce(t);N(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError("Reduce of empty array with no initial value");return n},T.reduceRight=T.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(v&&e.reduceRight===v)return r&&(t=T.bind(t,r)),arguments.length>2?e.reduceRight(t,n):e.reduceRight(t);var s=e.length;if(s!==+s){var o=T.keys(e);s=o.length}N(e,function(u,a,f){a=o?o[--s]:--s,i?n=t.call(r,n,e[a],a,f):(n=e[a],i=!0)});if(!i)throw new TypeError("Reduce of empty array with no initial value");return n},T.find=T.detect=function(e,t,n){var r;return C(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},T.filter=T.select=function(e,t,n){var r=[];return e==null?r:m&&e.filter===m?e.filter(t,n):(N(e,function(e,i,s){t.call(n,e,i,s)&&(r[r.length]=e)}),r)},T.reject=function(e,t,n){var r=[];return e==null?r:(N(e,function(e,i,s){t.call(n,e,i,s)||(r[r.length]=e)}),r)},T.every=T.all=function(e,t,r){t||(t=T.identity);var i=!0;return e==null?i:g&&e.every===g?e.every(t,r):(N(e,function(e,s,o){if(!(i=i&&t.call(r,e,s,o)))return n}),!!i)};var C=T.some=T.any=function(e,t,r){t||(t=T.identity);var i=!1;return e==null?i:y&&e.some===y?e.some(t,r):(N(e,function(e,s,o){if(i||(i=t.call(r,e,s,o)))return n}),!!i)};T.contains=T.include=function(e,t){var n=!1;return e==null?n:b&&e.indexOf===b?e.indexOf(t)!=-1:(n=C(e,function(e){return e===t}),n)},T.invoke=function(e,t){var n=u.call(arguments,2);return T.map(e,function(e){return(T.isFunction(t)?t:e[t]).apply(e,n)})},T.pluck=function(e,t){return T.map(e,function(e){return e[t]})},T.where=function(e,t){return T.isEmpty(t)?[]:T.filter(e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},T.max=function(e,t,n){if(!t&&T.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.max.apply(Math,e);if(!t&&T.isEmpty(e))return-Infinity;var r={computed:-Infinity};return N(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},T.min=function(e,t,n){if(!t&&T.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.min.apply(Math,e);if(!t&&T.isEmpty(e))return Infinity;var r={computed:Infinity};return N(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;or||n===void 0)return 1;if(n>>1;n.call(r,e[u])=0})})},T.difference=function(e){var t=a.apply(r,u.call(arguments,1));return T.filter(e,function(e){return!T.contains(t,e)})},T.zip=function(){var e=u.call(arguments),t=T.max(T.pluck(e,"length")),n=new Array(t);for(var r=0;r=0;n--)t=[e[n].apply(this,t)];return t[0]}},T.after=function(e,t){return e<=0?t():function(){if(--e<1)return t.apply(this,arguments)}},T.keys=S||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)T.has(e,n)&&(t[t.length]=n);return t},T.values=function(e){var t=[];for(var n in e)T.has(e,n)&&t.push(e[n]);return t},T.pairs=function(e){var t=[];for(var n in e)T.has(e,n)&&t.push([n,e[n]]);return t},T.invert=function(e){var t={};for(var n in e)T.has(e,n)&&(t[e[n]]=n);return t},T.functions=T.methods=function(e){var t=[];for(var n in e)T.isFunction(e[n])&&t.push(n);return t.sort()},T.extend=function(e){return N(u.call(arguments,1),function(t){for(var n in t)e[n]=t[n]}),e},T.pick=function(e){var t={},n=a.apply(r,u.call(arguments,1));return N(n,function(n){n in e&&(t[n]=e[n])}),t},T.omit=function(e){var t={},n=a.apply(r,u.call(arguments,1));for(var i in e)T.contains(n,i)||(t[i]=e[i]);return t},T.defaults=function(e){return N(u.call(arguments,1),function(t){for(var n in t)e[n]==null&&(e[n]=t[n])}),e},T.clone=function(e){return T.isObject(e)?T.isArray(e)?e.slice():T.extend({},e):e},T.tap=function(e,t){return t(e),e};var M=function(e,t,n,r){if(e===t)return e!==0||1/e==1/t;if(e==null||t==null)return e===t;e instanceof T&&(e=e._wrapped),t instanceof T&&(t=t._wrapped);var i=l.call(e);if(i!=l.call(t))return!1;switch(i){case"[object String]":return e==String(t);case"[object Number]":return e!=+e?t!=+t:e==0?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if(typeof e!="object"||typeof t!="object")return!1;var s=n.length;while(s--)if(n[s]==e)return r[s]==t;n.push(e),r.push(t);var o=0,u=!0;if(i=="[object Array]"){o=e.length,u=o==t.length;if(u)while(o--)if(!(u=M(e[o],t[o],n,r)))break}else{var a=e.constructor,f=t.constructor;if(a!==f&&!(T.isFunction(a)&&a instanceof a&&T.isFunction(f)&&f instanceof f))return!1;for(var c in e)if(T.has(e,c)){o++;if(!(u=T.has(t,c)&&M(e[c],t[c],n,r)))break}if(u){for(c in t)if(T.has(t,c)&&!(o--))break;u=!o}}return n.pop(),r.pop(),u};T.isEqual=function(e,t){return M(e,t,[],[])},T.isEmpty=function(e){if(e==null)return!0;if(T.isArray(e)||T.isString(e))return e.length===0;for(var t in e)if(T.has(e,t))return!1;return!0},T.isElement=function(e){return!!e&&e.nodeType===1},T.isArray=E||function(e){return l.call(e)=="[object Array]"},T.isObject=function(e){return e===Object(e)},N(["Arguments","Function","String","Number","Date","RegExp"],function(e){T["is"+e]=function(t){return l.call(t)=="[object "+e+"]"}}),T.isArguments(arguments)||(T.isArguments=function(e){return!!e&&!!T.has(e,"callee")}),typeof /./!="function"&&(T.isFunction=function(e){return typeof e=="function"}),T.isFinite=function(e){return T.isNumber(e)&&isFinite(e)},T.isNaN=function(e){return T.isNumber(e)&&e!=+e},T.isBoolean=function(e){return e===!0||e===!1||l.call(e)=="[object Boolean]"},T.isNull=function(e){return e===null},T.isUndefined=function(e){return e===void 0},T.has=function(e,t){return c.call(e,t)},T.noConflict=function(){return e._=t,this},T.identity=function(e){return e},T.times=function(e,t,n){for(var r=0;r":">",'"':""","'":"'","/":"/"}};_.unescape=T.invert(_.escape);var D={escape:new RegExp("["+T.keys(_.escape).join("")+"]","g"),unescape:new RegExp("("+T.keys(_.unescape).join("|")+")","g")};T.each(["escape","unescape"],function(e){T[e]=function(t){return t==null?"":(""+t).replace(D[e],function(t){return _[e][t]})}}),T.result=function(e,t){if(e==null)return null;var n=e[t];return T.isFunction(n)?n.call(e):n},T.mixin=function(e){N(T.functions(e),function(t){var n=T[t]=e[t];T.prototype[t]=function(){var e=[this._wrapped];return o.apply(e,arguments),F.call(this,n.apply(T,e))}})};var P=0;T.uniqueId=function(e){var t=P++;return e?e+t:t},T.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var H=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},j=/\\|'|\r|\n|\t|\u2028|\u2029/g;T.template=function(e,t,n){n=T.defaults({},n,T.templateSettings);var r=new RegExp([(n.escape||H).source,(n.interpolate||H).source,(n.evaluate||H).source].join("|")+"|$","g"),i=0,s="__p+='";e.replace(r,function(t,n,r,o,u){s+=e.slice(i,u).replace(j,function(e){return"\\"+B[e]}),s+=n?"'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'":r?"'+\n((__t=("+r+"))==null?'':__t)+\n'":o?"';\n"+o+"\n__p+='":"",i=u+t.length}),s+="';\n",n.variable||(s="with(obj||{}){\n"+s+"}\n"),s="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+s+"return __p;\n";try{var o=new Function(n.variable||"obj","_",s)}catch(u){throw u.source=s,u}if(t)return o(t,T);var a=function(e){return o.call(this,e,T)};return a.source="function("+(n.variable||"obj")+"){\n"+s+"}",a},T.chain=function(e){return T(e).chain()};var F=function(e){return this._chain?T(e).chain():e};T.mixin(T),N(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];T.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),(e=="shift"||e=="splice")&&n.length===0&&delete n[0],F.call(this,n)}}),N(["concat","join","slice"],function(e){var t=r[e];T.prototype[e]=function(){return F.call(this,t.apply(this._wrapped,arguments))}}),T.extend(T.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this);
\ No newline at end of file