diff --git a/src/styles/components/_buddycondensed.scss b/src/styles/components/_buddycondensed.scss
index 8a003bca..1712e3b2 100644
--- a/src/styles/components/_buddycondensed.scss
+++ b/src/styles/components/_buddycondensed.scss
@@ -42,17 +42,14 @@
display: none;
position: fixed;
top: 49px;
- &:hover {
- display: block;
- }
}
.desc {
- display: inline-block;
+ display: none;
font-weight: bold;
padding-left: 10px;
cursor: default;
- &:hover ~ .overDefaultDisplayNum {
- display: block;
+ &.overNum {
+ display: inline-block;
}
}
.actions {
@@ -87,8 +84,5 @@
.buddyPicture {
cursor: pointer;
overflow: visible;
- &:hover .actions {
- display: inline-block;
- }
}
}
diff --git a/static/js/directives/buddycondensed.js b/static/js/directives/buddycondensed.js
index b794d7c9..5da426f5 100644
--- a/static/js/directives/buddycondensed.js
+++ b/static/js/directives/buddycondensed.js
@@ -18,13 +18,21 @@
* along with this program. If not, see .
*
*/
-define(['angular', 'text!partials/buddycondensed.html'], function(angular, template) {
+define(['angular', 'jquery', 'text!partials/buddycondensed.html', 'hoverIntent'], function(angular, $, template) {
// buddycondensed
return [function() {
- var controller = ['$scope', 'mediaStream', 'contacts', function($scope, mediaStream, contacts) {
+ var controller = ['$scope', 'mediaStream', 'contacts', 'buddyData', function($scope, mediaStream, contacts, buddyData) {
var buddycondensed = [];
+ var getContactSessionId = function(userid) {
+ var session = null;
+ var scope = buddyData.lookup(userid, false, false);
+ if (scope) {
+ session = scope.session.get();
+ }
+ return session && session.Id ? session.Id : null;
+ };
var empty = function(x) {
return x === null || x === undefined || isNaN(x) || x === "";
};
@@ -50,6 +58,7 @@ define(['angular', 'text!partials/buddycondensed.html'], function(angular, templ
};
var joined = function(buddy) {
buddycondensed.push(buddy);
+ buddycondensed.push(angular.extend({}, buddy));
};
var left = function(id) {
for (var i in buddycondensed) {
@@ -77,6 +86,15 @@ define(['angular', 'text!partials/buddycondensed.html'], function(angular, templ
}
$scope.$apply();
};
+ $scope.call = function(userid) {
+ mediaStream.webrtc.doCall(getContactSessionId(userid));
+ };
+ $scope.chat = function(userid) {
+ $scope.$emit("startchat", getContactSessionId(userid), {
+ autofocus: true,
+ restore: true
+ });
+ };
$scope.listDefault = function() {
if(buddycondensed.length >= $scope.maxBuddiesToShow) {
return buddycondensed.slice(0, $scope.maxBuddiesToShow);
@@ -118,7 +136,52 @@ define(['angular', 'text!partials/buddycondensed.html'], function(angular, templ
});
}];
- var link = function($scope, elem, attrs, ctrl) {};
+ var link = function($scope, elem, attrs, ctrl) {
+ var overDefaultDisplayNum = elem.find(".overDefaultDisplayNum");
+ var desc = elem.find(".desc");
+ var aboveElem1 = false;
+ var aboveElem2 = false;
+ var outMoreBuddy = function(event) {
+ //console.log('out', event.currentTarget.className);
+ if (event.currentTarget === desc.get(0)) {
+ aboveElem1 = false;
+ } else if (event.currentTarget === overDefaultDisplayNum.get(0)) {
+ aboveElem2 = false;
+ }
+ if(!aboveElem1 && !aboveElem2) {
+ overDefaultDisplayNum.hide();
+ }
+ };
+ var overMoreBuddy = function(event) {
+ //console.log('out', event.currentTarget.className);
+ if (event.currentTarget === desc.get(0)) {
+ aboveElem1 = true;
+ } else if (event.currentTarget === overDefaultDisplayNum.get(0)) {
+ aboveElem2 = true;
+ }
+ overDefaultDisplayNum.show();
+ };
+ elem.hoverIntent({
+ over: overMoreBuddy,
+ out: outMoreBuddy,
+ timeout: 1000,
+ selector: '.desc, .overDefaultDisplayNum'
+ });
+ var overBuddyPicture = function(event) {
+ //console.log('overBuddyPicture', event.currentTarget.className);
+ $(event.currentTarget).find(".actions").css("display", "inline-block");
+ };
+ var outBuddyPicture = function(event) {
+ //console.log('outBuddyPicture', event.currentTarget.className);
+ $(event.currentTarget).find(".actions").css("display", "none");
+ };
+ elem.hoverIntent({
+ over: overBuddyPicture,
+ out: outBuddyPicture,
+ timeout: 100,
+ selector: '.buddyPicture'
+ });
+ };
return {
restrict: 'E',
diff --git a/static/js/libs/jquery/jquery.hoverIntent.js b/static/js/libs/jquery/jquery.hoverIntent.js
new file mode 100644
index 00000000..ac2f1192
--- /dev/null
+++ b/static/js/libs/jquery/jquery.hoverIntent.js
@@ -0,0 +1,115 @@
+/*!
+ * hoverIntent v1.8.1 // 2014.08.11 // jQuery v1.9.1+
+ * http://cherne.net/brian/resources/jquery.hoverIntent.html
+ *
+ * You may use hoverIntent under the terms of the MIT license. Basically that
+ * means you are free to use hoverIntent as long as this header is left intact.
+ * Copyright 2007, 2014 Brian Cherne
+ */
+
+/* hoverIntent is similar to jQuery's built-in "hover" method except that
+ * instead of firing the handlerIn function immediately, hoverIntent checks
+ * to see if the user's mouse has slowed down (beneath the sensitivity
+ * threshold) before firing the event. The handlerOut function is only
+ * called after a matching handlerIn.
+ *
+ * // basic usage ... just like .hover()
+ * .hoverIntent( handlerIn, handlerOut )
+ * .hoverIntent( handlerInOut )
+ *
+ * // basic usage ... with event delegation!
+ * .hoverIntent( handlerIn, handlerOut, selector )
+ * .hoverIntent( handlerInOut, selector )
+ *
+ * // using a basic configuration object
+ * .hoverIntent( config )
+ *
+ * @param handlerIn function OR configuration object
+ * @param handlerOut function OR selector for delegation OR undefined
+ * @param selector selector OR undefined
+ * @author Brian Cherne
+ */
+(function($) {
+ $.fn.hoverIntent = function(handlerIn,handlerOut,selector) {
+
+ // default configuration values
+ var cfg = {
+ interval: 100,
+ sensitivity: 6,
+ timeout: 0
+ };
+
+ if ( typeof handlerIn === "object" ) {
+ cfg = $.extend(cfg, handlerIn );
+ } else if ($.isFunction(handlerOut)) {
+ cfg = $.extend(cfg, { over: handlerIn, out: handlerOut, selector: selector } );
+ } else {
+ cfg = $.extend(cfg, { over: handlerIn, out: handlerIn, selector: handlerOut } );
+ }
+
+ // instantiate variables
+ // cX, cY = current X and Y position of mouse, updated by mousemove event
+ // pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
+ var cX, cY, pX, pY;
+
+ // A private function for getting mouse position
+ var track = function(ev) {
+ cX = ev.pageX;
+ cY = ev.pageY;
+ };
+
+ // A private function for comparing current and previous mouse position
+ var compare = function(ev,ob) {
+ ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
+ // compare mouse positions to see if they've crossed the threshold
+ if ( Math.sqrt( (pX-cX)*(pX-cX) + (pY-cY)*(pY-cY) ) < cfg.sensitivity ) {
+ $(ob).off("mousemove.hoverIntent",track);
+ // set hoverIntent state to true (so mouseOut can be called)
+ ob.hoverIntent_s = true;
+ return cfg.over.apply(ob,[ev]);
+ } else {
+ // set previous coordinates for next time
+ pX = cX; pY = cY;
+ // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
+ ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
+ }
+ };
+
+ // A private function for delaying the mouseOut function
+ var delay = function(ev,ob) {
+ ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
+ ob.hoverIntent_s = false;
+ return cfg.out.apply(ob,[ev]);
+ };
+
+ // A private function for handling mouse 'hovering'
+ var handleHover = function(e) {
+ // copy objects to be passed into t (required for event object to be passed in IE)
+ var ev = $.extend({},e);
+ var ob = this;
+
+ // cancel hoverIntent timer if it exists
+ if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
+
+ // if e.type === "mouseenter"
+ if (e.type === "mouseenter") {
+ // set "previous" X and Y position based on initial entry point
+ pX = ev.pageX; pY = ev.pageY;
+ // update "current" X and Y position based on mousemove
+ $(ob).on("mousemove.hoverIntent",track);
+ // start polling interval (self-calling timeout) to compare mouse coordinates over time
+ if (!ob.hoverIntent_s) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
+
+ // else e.type == "mouseleave"
+ } else {
+ // unbind expensive mousemove event
+ $(ob).off("mousemove.hoverIntent",track);
+ // if hoverIntent state is true, then call the mouseOut function after the specified delay
+ if (ob.hoverIntent_s) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
+ }
+ };
+
+ // listen for mouseenter and mouseleave
+ return this.on({'mouseenter.hoverIntent':handleHover,'mouseleave.hoverIntent':handleHover}, cfg.selector);
+ };
+})(jQuery);
diff --git a/static/js/main.js b/static/js/main.js
index a54270a7..0bf3104f 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -43,6 +43,7 @@ require.config({
'avltree': 'libs/avltree',
'injectCSS': 'libs/jquery/jquery.injectCSS',
'mobile-events': 'libs/jquery/jquery.mobile-events',
+ 'hoverIntent': 'libs/jquery/jquery.hoverIntent',
'jed': 'libs/jed',
'audiocontext': 'libs/audiocontext',
'rAF': 'libs/rAF',
@@ -112,6 +113,10 @@ require.config({
deps: ['jquery'],
exports: '$'
},
+ 'hoverIntent': {
+ deps: ['jquery'],
+ exports: '$'
+ },
'sjcl': {
exports: 'sjcl'
},
diff --git a/static/partials/buddycondensed.html b/static/partials/buddycondensed.html
index 846c72e1..5ca3f45e 100644
--- a/static/partials/buddycondensed.html
+++ b/static/partials/buddycondensed.html
@@ -1,6 +1,6 @@
-
+ {{listOverDefault().length - maxBuddiesToShow}} {{_("more")}}
+
+ {{listOverDefault().length}} {{_("more")}}
@@ -9,8 +9,8 @@
{{buddy.Userid|displayName}}
@@ -24,8 +24,8 @@
{{buddy.Userid|displayName}}