Browse Source

Merge e416c01b7f into b03ccff479

pull/119/merge
theurere 12 years ago
parent
commit
d086324f52
  1. 1
      html/main.html
  2. 125
      src/styles/components/_buddycondensed.scss
  3. 1
      src/styles/main.scss
  4. 2
      static/css/main.min.css
  5. 194
      static/js/directives/buddycondensed.js
  6. 6
      static/js/directives/directives.js
  7. 115
      static/js/libs/jquery/jquery.hoverIntent.js
  8. 5
      static/js/main.js
  9. 35
      static/partials/buddycondensed.html

1
html/main.html

@ -18,6 +18,7 @@
<div class="navbar-brand left"> <div class="navbar-brand left">
<%template "logo" .%> <%template "logo" .%>
</div> </div>
<buddycondensed></buddycondensed>
</div> </div>
<div class="navbar-collapse collapse" collapse="isCollapsed"> <div class="navbar-collapse collapse" collapse="isCollapsed">
<ul class="nav navbar-nav navbar-right right"> <ul class="nav navbar-nav navbar-right right">

125
src/styles/components/_buddycondensed.scss

@ -0,0 +1,125 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#buddycondensed {
display: inline-block;
position: fixed;
padding-left: 20px;
@media (max-width: 1125px) {
display: none;
}
@media (max-width: 480px) {
display: none;
padding-left: 0;
position: relative;
width: 80%;
}
}
.buddycondensed {
&.buddy {
background: none;
border-bottom: none;
cursor: auto;
min-height: 50px;
vertical-align: bottom;
overflow: visible;
.buddyPicture {
margin: 2px;
}
}
.defaultDisplayNum {
@media (max-width: 480px) {
padding: 10px 0px;
}
}
.overDefaultDisplayNum {
background: #f8f8f8;
display: none;
max-width: 80%;
position: fixed;
top: 49px;
@media (max-width: 480px) {
overflow-y: auto;
max-height: 400px;
top: 109px;
}
}
.desc {
display: none;
font-weight: bold;
padding-left: 10px;
cursor: default;
@media (max-width: 480px) {
position: absolute;
top: 50px;
}
&.overNum {
display: inline-block;
@media (max-width: 480px) {
display: block;
}
}
}
.actions {
background: #FFF;
border: 1px solid rgba(128, 128, 128, 0.16);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
border-radius: 2px;
display: none;
cursor: default;
height: 51px;
padding: 0 15px;
bottom: 12px;
position: relative;
white-space: nowrap;
z-index: 10;
@media (max-width: 480px) {
height: 60px;
padding: 0 5px;
bottom: 22px;
}
.btn-group {
margin-bottom: 5px;
width: 55px;
@media (max-width: 480px) {
display: block;
}
}
.btn-primary {
padding: 2px 4px;
}
.fa {
font-size: 20px;
color: #FFF;
line-height: 24px;
}
.displayName {
margin-right: 10px;
@media (max-width: 480px) {
line-height: 22px;
display: block;
}
}
}
.buddyPicture {
cursor: default;
overflow: visible;
}
}

1
src/styles/main.scss

@ -38,6 +38,7 @@
@import "components/rightslide"; @import "components/rightslide";
@import "components/bar"; @import "components/bar";
@import "components/buddylist"; @import "components/buddylist";
@import "components/buddycondensed";
@import "components/buddypicturecapture"; @import "components/buddypicturecapture";
@import "components/buddypictureupload"; @import "components/buddypictureupload";
@import "components/settings"; @import "components/settings";

2
static/css/main.min.css vendored

@ -1,4 +1,4 @@
/*! /*!
* Spreed WebRTC. * Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG * Copyright (C) 2013-2014 struktur AG
* *

194
static/js/directives/buddycondensed.js

@ -0,0 +1,194 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
define(['angular', 'jquery', 'text!partials/buddycondensed.html', 'hoverIntent'], function(angular, $, template) {
// buddycondensed
return [function() {
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 || x === "";
};
var sortCondensed = function() {
buddycondensed.sort(function(current, next) {
if (!current.Status || current.Status && empty(current.Status.displayName)) {
return 1;
} else {
if (next.Status && !empty(next.Status.displayName)) {
if (current.Status.displayName < next.Status.displayName) {
return -1;
} else if (current.Status.displayName > next.Status.displayName) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
});
};
var joined = function(buddy) {
buddycondensed.push(buddy);
};
var left = function(id) {
for (var i in buddycondensed) {
if (buddycondensed[i].Id === id) {
buddycondensed.splice(i,1);
break;
}
}
$scope.$apply();
};
var contactadded = function(data) {
//console.log('contactadded', data);
var hasSession = false;
for (var i in buddycondensed) {
// replace session data with contact data
if (buddycondensed[i].Userid === data.Userid) {
buddycondensed[i] = data;
hasSession = true;
break;
}
}
if (!hasSession) {
joined(data);
}
$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);
} else {
return buddycondensed;
}
};
$scope.listOverDefault = function() {
if (buddycondensed.length >= $scope.maxBuddiesToShow) {
return buddycondensed.slice($scope.maxBuddiesToShow);
} else {
return [];
}
};
$scope.maxBuddiesToShow = 5;
contacts.e.on("contactadded", function(event, data) {
contactadded(data);
sortCondensed();
});
mediaStream.api.e.on("received.userleftorjoined", function(event, dataType, data) {
//console.log("received.userleftorjoined", data.Id);
if (dataType === "Left") {
left(data.Id);
} else {
joined(data);
}
sortCondensed();
});
mediaStream.api.e.on("received.users", function(event, data) {
//console.log("received.users", data);
var selfId = $scope.id;
data.forEach(function(x) {
if (x.Id !== selfId) {
joined(x);
}
});
sortCondensed();
$scope.$apply();
});
}];
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',
scope: true,
replace: true,
link: link,
controller: controller,
template: template
};
}];
});

6
static/js/directives/directives.js

@ -43,7 +43,8 @@ define([
'directives/odfcanvas', 'directives/odfcanvas',
'directives/presentation', 'directives/presentation',
'directives/youtubevideo', 'directives/youtubevideo',
'directives/bfi'], function(_, onEnter, onEscape, statusMessage, buddyList, buddyPictureCapture, buddyPictureUpload, settings, chat, audioVideo, usability, audioLevel, fileInfo, screenshare, roomBar, socialShare, page, contactRequest, defaultDialog, pdfcanvas, odfcanvas, presentation, youtubevideo, bfi) { 'directives/bfi',
'directives/buddycondensed'], function(_, onEnter, onEscape, statusMessage, buddyList, buddyPictureCapture, buddyPictureUpload, settings, chat, audioVideo, usability, audioLevel, fileInfo, screenshare, roomBar, socialShare, page, contactRequest, defaultDialog, pdfcanvas, odfcanvas, presentation, youtubevideo, bfi, buddycondensed) {
var directives = { var directives = {
onEnter: onEnter, onEnter: onEnter,
@ -68,7 +69,8 @@ define([
odfcanvas: odfcanvas, odfcanvas: odfcanvas,
presentation: presentation, presentation: presentation,
youtubevideo: youtubevideo, youtubevideo: youtubevideo,
bfi: bfi bfi: bfi,
buddycondensed: buddycondensed
}; };
var initialize = function(angModule) { var initialize = function(angModule) {

115
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 <brian(at)cherne(dot)net>
*/
(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);

5
static/js/main.js

@ -43,6 +43,7 @@ require.config({
'avltree': 'libs/avltree', 'avltree': 'libs/avltree',
'injectCSS': 'libs/jquery/jquery.injectCSS', 'injectCSS': 'libs/jquery/jquery.injectCSS',
'mobile-events': 'libs/jquery/jquery.mobile-events', 'mobile-events': 'libs/jquery/jquery.mobile-events',
'hoverIntent': 'libs/jquery/jquery.hoverIntent',
'jed': 'libs/jed', 'jed': 'libs/jed',
'audiocontext': 'libs/audiocontext', 'audiocontext': 'libs/audiocontext',
'rAF': 'libs/rAF', 'rAF': 'libs/rAF',
@ -112,6 +113,10 @@ require.config({
deps: ['jquery'], deps: ['jquery'],
exports: '$' exports: '$'
}, },
'hoverIntent': {
deps: ['jquery'],
exports: '$'
},
'sjcl': { 'sjcl': {
exports: 'sjcl' exports: 'sjcl'
}, },

35
static/partials/buddycondensed.html

@ -0,0 +1,35 @@
<div id="buddycondensed">
<div class="buddycondensed buddy">
<span class="desc" ng-class="{overNum:listOverDefault().length > 0}">+ {{listOverDefault().length}} {{_("more")}}</span>
<div class="defaultDisplayNum pull-left">
<span ng-repeat="buddy in listDefault()">
<span class="buddyPicture">
<i class="fa fa-user"/>
<img ng-show="buddy.Status.buddyPicture" alt ng-src="{{buddy.Userid | buddyImageSrc}}"/>
<span class="actions">
<span class="displayName">{{buddy.Userid|displayName}}</span>
<div class="btn-group">
<a class="btn btn-primary" ng-click="call(buddy.Userid)" title="Start video call"><i class="fa fa-phone"></i></a>
<a class="btn btn-primary" ng-click="chat(buddy.Userid)" title="Start chat"><i class="fa fa-comments-o"></i></a>
</div>
</span>
</span>
</span>
</div>
<div class="overDefaultDisplayNum">
<span ng-repeat="buddy in listOverDefault()">
<span class="buddyPicture">
<i class="fa fa-user"/>
<img ng-show="buddy.Status.buddyPicture" alt ng-src="{{buddy.Userid | buddyImageSrc}}"/>
<span class="actions">
<span class="displayName">{{buddy.Userid|displayName}}</span>
<div class="btn-group">
<a class="btn btn-primary" ng-click="call(buddy.Userid)" title="Start video call"><i class="fa fa-phone"></i></a>
<a class="btn btn-primary" ng-click="chat(buddy.Userid)" title="Start chat"><i class="fa fa-comments-o"></i></a>
</div>
</span>
</span>
</span>
</div>
</div>
</div>
Loading…
Cancel
Save