Browse Source

Merge commit '736096161798b48df74c497736e75a0b68fd805d' into theurere-sass-libs-branch

Conflicts:
	src/styles/components/_audiovideo.scss
	src/styles/global/_base.scss
	src/styles/global/_variables.scss
	src/styles/main.scss

Merge from 7360961617.
pull/29/head
Evan Theurer 12 years ago
parent
commit
7ddc562c4d
  1. 4
      .gitignore
  2. 9
      Makefile
  3. 18
      debian/changelog
  4. 4
      html/main.html
  5. 8
      server.conf.in
  6. 1
      src/app/spreed-speakfreely-server/api.go
  7. 1
      src/app/spreed-speakfreely-server/buffercache.go
  8. 1
      src/app/spreed-speakfreely-server/config.go
  9. 1
      src/app/spreed-speakfreely-server/connection.go
  10. 1
      src/app/spreed-speakfreely-server/context.go
  11. 7
      src/app/spreed-speakfreely-server/hub.go
  12. 23
      src/app/spreed-speakfreely-server/images.go
  13. 13
      src/app/spreed-speakfreely-server/main.go
  14. 1
      src/app/spreed-speakfreely-server/random.go
  15. 1
      src/app/spreed-speakfreely-server/rooms.go
  16. 1
      src/app/spreed-speakfreely-server/roomworker.go
  17. 1
      src/app/spreed-speakfreely-server/server.go
  18. 1
      src/app/spreed-speakfreely-server/stats.go
  19. 18
      src/app/spreed-speakfreely-server/tokenprovider.go
  20. 1
      src/app/spreed-speakfreely-server/tokens.go
  21. 1
      src/app/spreed-speakfreely-server/user.go
  22. 21
      src/app/spreed-speakfreely-server/ws.go
  23. 1
      src/app/spreed-speakfreely-server/wsdata.go
  24. 25
      src/i18n/messages-ja.po
  25. 100
      src/styles/color-pallete.html
  26. 327
      src/styles/components/_audiovideo.scss
  27. 319
      src/styles/components/_bar.scss
  28. 179
      src/styles/components/_webrtc.scss
  29. 1
      src/styles/global/_base.scss
  30. 23
      src/styles/global/_mixins.scss
  31. 66
      src/styles/global/_variables.scss
  32. 5
      src/styles/main.scss
  33. 7
      static/js/app.js
  34. 4
      static/js/controllers/mediastreamcontroller.js
  35. 2
      static/js/controllers/roomchangecontroller.js
  36. 93
      static/js/directives/audiovideo.js
  37. 4
      static/js/mediastream/peerconference.js
  38. 19
      static/js/mediastream/webrtc.js
  39. 2
      static/js/services/fileupload.js
  40. 74
      static/js/services/videolayout.js
  41. 24
      static/partials/audiovideo.html
  42. 6
      static/partials/audiovideopeer.html
  43. 2
      static/translation/messages-ja.json

4
.gitignore vendored

@ -24,3 +24,7 @@ vendor/*
/.sass-cache /.sass-cache
/extra /extra
/src/i18n/*.mo /src/i18n/*.mo
server.key
server.csr
server.crt
server.pem

9
Makefile

@ -20,8 +20,8 @@
PKG := app/spreed-speakfreely-server PKG := app/spreed-speakfreely-server
EXENAME := spreed-speakfreely-server EXENAME := spreed-speakfreely-server
CONFIG_FILE := spreed-speakfreely-server.conf CONFIG_FILE ?= spreed-speakfreely-server.conf
CONFIG_PATH := /etc CONFIG_PATH ?= /etc
VENDOR = "$(CURDIR)/vendor" VENDOR = "$(CURDIR)/vendor"
GOPATH = "$(VENDOR):$(CURDIR)" GOPATH = "$(VENDOR):$(CURDIR)"
@ -63,6 +63,9 @@ binary:
binaryrace: binaryrace:
GOPATH=$(GOPATH) go build -race -o $(OUTPUT)/$(EXENAME) -ldflags '$(LDFLAGS)' $(PKG) GOPATH=$(GOPATH) go build -race -o $(OUTPUT)/$(EXENAME) -ldflags '$(LDFLAGS)' $(PKG)
binaryall:
GOPATH=$(GOPATH) go build -a -o $(OUTPUT)/$(EXENAME) -ldflags '$(LDFLAGS)' $(PKG)
fmt: fmt:
GOPATH=$(GOPATH) go fmt app/... GOPATH=$(GOPATH) go fmt app/...
@ -154,4 +157,4 @@ tarball: distclean release install
echo -n $(VERSION) > $(TARPATH)/version.txt echo -n $(VERSION) > $(TARPATH)/version.txt
tar czf $(DIST)/$(PACKAGE_NAME).tar.gz -C $(DIST) $(PACKAGE_NAME) tar czf $(DIST)/$(PACKAGE_NAME).tar.gz -C $(DIST) $(PACKAGE_NAME)
.PHONY: clean distclean pristine get build styles javascript release releasetest dist_gopath install gopath binary tarball assets .PHONY: clean distclean pristine get build styles javascript release releasetest dist_gopath install gopath binary binaryrace binaryall tarball assets

18
debian/changelog vendored

@ -1,3 +1,21 @@
spreed-speakfreely-server (0.17.4) precise; urgency=low
* Updated Japanese translation.
* Allow Makefile variables CONFIG_FILE and CONFIG_PATH.
* Fixed a possible conference connection issue when all ICE connected were successfull.
* Videos are now properly aligned to window top.
* Top bar buttons no longer overlap.
* Use onepeople audio video renderer per default.
* Added support for native HTTPS server.
* Fixed a data channel not ready error.
* Use new video layout implementation to draw when there is a main view.
* Added UI controls to switch video layout.
* Made the conferencekiosk renderer mode working and enabled it in Ui.
* Use new websocket.Upgraded API.
* No longer hang up on reload when not confirmed.
-- Simon Eisenmann <simon@struktur.de> Thu, 24 Apr 2014 17:59:05 +0200
spreed-speakfreely-server (0.17.3) precise; urgency=low spreed-speakfreely-server (0.17.3) precise; urgency=low
* Buddy images are now loaded with seperate URL calls. * Buddy images are now loaded with seperate URL calls.

4
html/main.html

@ -27,9 +27,7 @@
<div class="audio-level" title="{{_('Your audio level')}}"></div> <div class="audio-level" title="{{_('Your audio level')}}"></div>
</div> </div>
<div id="audiovideo" class="ng-cloak" ng-show="peer"> <div id="audiovideo" class="ng-cloak" ng-show="peer">
<div id="container"> <audio-video/>
<audio-video/>
</div>
</div> </div>
<div id="screenshare" class="ng-cloak mainview"> <div id="screenshare" class="ng-cloak mainview">
<screenshare/> <screenshare/>

8
server.conf.in

@ -10,6 +10,14 @@ listen = 127.0.0.1:8080
#stats = true # Provide stats API at /api/v1/stats (do not enable this in production or unprotected!). #stats = true # Provide stats API at /api/v1/stats (do not enable this in production or unprotected!).
#pprofListen = 127.0.0.1:6060 # See http://golang.org/pkg/net/http/pprof/ for details #pprofListen = 127.0.0.1:6060 # See http://golang.org/pkg/net/http/pprof/ for details
[https]
#listen = 127.0.0.1:8443
#certificate = server.crt # Full path to certificate.
#key = server.key # Full path to key.
#minVersion = SSLv3 # Minimal supported encryption (SSLv3, TLSv1, TLSv1.1, TLSv1.2).
#readtimeout = 10
#writetimeout = 10
[app] [app]
#title = Spreed Speak Freely #title = Spreed Speak Freely
#ver = 1234 # version string to use for static resource #ver = 1234 # version string to use for static resource

1
src/app/spreed-speakfreely-server/api.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import () import ()

1
src/app/spreed-speakfreely-server/buffercache.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (

1
src/app/spreed-speakfreely-server/config.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (

1
src/app/spreed-speakfreely-server/connection.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (

1
src/app/spreed-speakfreely-server/context.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
type Context struct { type Context struct {

7
src/app/spreed-speakfreely-server/hub.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (
@ -113,7 +114,7 @@ func (h *Hub) Stat(details bool) *HubStat {
rooms := make(map[string][]string) rooms := make(map[string][]string)
for roomid, room := range h.roomTable { for roomid, room := range h.roomTable {
users := make([]string, 0, len(room.connections)) users := make([]string, 0, len(room.connections))
for id, _ := range room.connections { for id := range room.connections {
users = append(users, id) users = append(users, id)
} }
rooms[roomid] = users rooms[roomid] = users
@ -215,9 +216,9 @@ func (h *Hub) GetGlobalConnections() []*Connection {
if room, ok := h.roomTable[h.config.globalRoomid]; ok { if room, ok := h.roomTable[h.config.globalRoomid]; ok {
h.mutex.RUnlock() h.mutex.RUnlock()
return room.GetConnections() return room.GetConnections()
} else {
h.mutex.RUnlock()
} }
h.mutex.RUnlock()
return make([]*Connection, 0) return make([]*Connection, 0)
} }

23
src/app/spreed-speakfreely-server/images.go

@ -1,3 +1,24 @@
/*
* Spreed Speak Freely.
* Copyright (C) 2013-2014 struktur AG
*
* This file is part of Spreed Speak Freely.
*
* 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/>.
*
*/
package main package main
import ( import (
@ -51,7 +72,7 @@ func NewImageCache() ImageCache {
} }
func (self *imageCache) Update(userId string, image string) string { func (self *imageCache) Update(userId string, image string) string {
var mimetype string = "image/x-unknown" mimetype := "image/x-unknown"
pos := strings.Index(image, ";") pos := strings.Index(image, ";")
if pos != -1 { if pos != -1 {
mimetype = image[:pos] mimetype = image[:pos]

13
src/app/spreed-speakfreely-server/main.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (
@ -49,12 +50,12 @@ var templates *template.Template
var config *Config var config *Config
// Helper to retrieve languages from request. // Helper to retrieve languages from request.
func getRequestLanguages(r *http.Request, supported_languages []string) []string { func getRequestLanguages(r *http.Request, supportedLanguages []string) []string {
accept_language_header, ok := r.Header["Accept-Language"] acceptLanguageHeader, ok := r.Header["Accept-Language"]
var langs []string var langs []string
if ok { if ok {
langs = goacceptlanguageparser.ParseAcceptLanguage(accept_language_header[0], supported_languages) langs = goacceptlanguageparser.ParseAcceptLanguage(acceptLanguageHeader[0], supportedLanguages)
} }
return langs return langs
@ -121,7 +122,7 @@ func handleRoomView(room string, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "private, max-age=0") w.Header().Set("Cache-Control", "private, max-age=0")
// Detect if the request was made with SSL. // Detect if the request was made with SSL.
ssl := false ssl := r.TLS != nil
proto, ok := r.Header["X-Forwarded-Proto"] proto, ok := r.Header["X-Forwarded-Proto"]
if ok { if ok {
ssl = proto[0] == "https" ssl = proto[0] == "https"
@ -290,9 +291,8 @@ func runner(runtime phoenix.Runtime) error {
templates, err = templates.ParseGlob(path.Join(extraFolder, "*.html")) templates, err = templates.ParseGlob(path.Join(extraFolder, "*.html"))
if err != nil { if err != nil {
return fmt.Errorf("Failed to load extra templates: %s", err) return fmt.Errorf("Failed to load extra templates: %s", err)
} else {
log.Printf("Loaded extra templates from: %s", extraFolder)
} }
log.Printf("Loaded extra templates from: %s", extraFolder)
} }
// Create our hub instance. // Create our hub instance.
@ -358,6 +358,7 @@ func runner(runtime phoenix.Runtime) error {
} }
runtime.DefaultHTTPHandler(r) runtime.DefaultHTTPHandler(r)
runtime.DefaultHTTPSHandler(r)
return runtime.Start() return runtime.Start()
} }

1
src/app/spreed-speakfreely-server/random.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (

1
src/app/spreed-speakfreely-server/rooms.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (

1
src/app/spreed-speakfreely-server/roomworker.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (

1
src/app/spreed-speakfreely-server/server.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (

1
src/app/spreed-speakfreely-server/stats.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (

18
src/app/spreed-speakfreely-server/tokenprovider.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (
@ -49,19 +50,19 @@ func (tf *TokenFile) ReloadIfModified() error {
return nil return nil
} }
func reload_tokens(tf *TokenFile) { func reloadRokens(tf *TokenFile) {
r, err := os.Open(tf.Path) r, err := os.Open(tf.Path)
if err != nil { if err != nil {
panic(err) panic(err)
} }
csv_reader := csv.NewReader(r) csvReader := csv.NewReader(r)
csv_reader.Comma = ':' csvReader.Comma = ':'
csv_reader.Comment = '#' csvReader.Comment = '#'
csv_reader.TrimLeadingSpace = true csvReader.TrimLeadingSpace = true
records, err := csv_reader.ReadAll() records, err := csvReader.ReadAll()
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -76,15 +77,14 @@ func reload_tokens(tf *TokenFile) {
func TokenFileProvider(filename string) TokenProvider { func TokenFileProvider(filename string) TokenProvider {
tf := &TokenFile{Path: filename} tf := &TokenFile{Path: filename}
tf.Reload = func() { reload_tokens(tf) } tf.Reload = func() { reloadRokens(tf) }
return func(token string) string { return func(token string) string {
tf.ReloadIfModified() tf.ReloadIfModified()
_, exists := tf.Tokens[token] _, exists := tf.Tokens[token]
if !exists { if !exists {
return "" return ""
} else {
return token
} }
return token
} }
} }

1
src/app/spreed-speakfreely-server/tokens.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (

1
src/app/spreed-speakfreely-server/user.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (

21
src/app/spreed-speakfreely-server/ws.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
import ( import (
@ -31,6 +32,23 @@ const (
wsWriteBufSize = 1024 wsWriteBufSize = 1024
) )
var (
upgrader = websocket.Upgrader{
ReadBufferSize: wsReadBufSize,
WriteBufferSize: wsWriteBufSize,
CheckOrigin: func(r *http.Request) bool {
// Allow all connections by default to keep backwards
// compatibility, but we should really check the Origin
// header instead!
//
// NOTE: We can omit "CheckOrigin" if the host in Origin
// must be the same as the host of the request (which
// is probably always the case).
return true
},
}
)
func makeWsHubHandler(h *Hub) http.HandlerFunc { func makeWsHubHandler(h *Hub) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
@ -42,9 +60,8 @@ func makeWsHubHandler(h *Hub) http.HandlerFunc {
} }
// Upgrade to Websocket mode. // Upgrade to Websocket mode.
ws, err := websocket.Upgrade(w, r, nil, wsReadBufSize, wsWriteBufSize) ws, err := upgrader.Upgrade(w, r, nil)
if _, ok := err.(websocket.HandshakeError); ok { if _, ok := err.(websocket.HandshakeError); ok {
w.WriteHeader(http.StatusBadRequest)
return return
} else if err != nil { } else if err != nil {
log.Println(err) log.Println(err)

1
src/app/spreed-speakfreely-server/wsdata.go

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package main package main
type DataHello struct { type DataHello struct {

25
src/i18n/messages-ja.po

@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: Spreed Speak Freely 1.0\n" "Project-Id-Version: Spreed Speak Freely 1.0\n"
"Report-Msgid-Bugs-To: simon@struktur.de\n" "Report-Msgid-Bugs-To: simon@struktur.de\n"
"POT-Creation-Date: 2014-04-14 16:16+0200\n" "POT-Creation-Date: 2014-04-14 16:16+0200\n"
"PO-Revision-Date: 2014-04-09 14:43+0100\n" "PO-Revision-Date: 2014-04-23 22:25+0100\n"
"Last-Translator: Curt Frisemo <curt.frisemo@spreed.com>\n" "Last-Translator: Curt Frisemo <curt.frisemo@spreed.com>\n"
"Language-Team: Curt Frisemo <curt.frisemo@spreed.com>\n" "Language-Team: Curt Frisemo <curt.frisemo@spreed.com>\n"
"Plural-Forms: nplurals=1; plural=0\n" "Plural-Forms: nplurals=1; plural=0\n"
@ -111,12 +111,11 @@ msgstr "ルームを出る"
msgid "Current room" msgid "Current room"
msgstr "現在のルーム" msgstr "現在のルーム"
#, fuzzy
msgid "Screen sharing options" msgid "Screen sharing options"
msgstr "メディアスクリーン共有" msgstr "画面共有オプション"
msgid "Fit screen." msgid "Fit screen."
msgstr "" msgstr "画面に合わせる"
msgid "Your picture" msgid "Your picture"
msgstr "あなたの写真" msgstr "あなたの写真"
@ -152,7 +151,7 @@ msgid "High"
msgstr "高い" msgstr "高い"
msgid "HD" msgid "HD"
msgstr "" msgstr "HD"
msgid "Language" msgid "Language"
msgstr "言語" msgstr "言語"
@ -289,7 +288,7 @@ msgid "Click here for help (Google Chrome)."
msgstr "ここをクリックしてヘルプ表示(Google Chrome)" msgstr "ここをクリックしてヘルプ表示(Google Chrome)"
msgid "Please set your user details and settings." msgid "Please set your user details and settings."
msgstr "あなたのユーザー情報とセッティングを設定してください." msgstr "あなたのプロフィールとアプリの動作を設定してください."
msgid "" msgid ""
"Please note that some settings require you to reload or to make a new " "Please note that some settings require you to reload or to make a new "
@ -369,7 +368,7 @@ msgid " does not pick up."
msgstr "は電話にでません." msgstr "は電話にでません."
msgid " tried to call you." msgid " tried to call you."
msgstr "" msgstr "は電話しようとしました."
msgid " called you." msgid " called you."
msgstr "から電話がありました." msgstr "から電話がありました."
@ -396,6 +395,9 @@ msgid ""
"usermedia-screen-capture and open it with your browser and enable the " "usermedia-screen-capture and open it with your browser and enable the "
"flag on top. Then restart the browser and you are ready to go." "flag on top. Then restart the browser and you are ready to go."
msgstr "" msgstr ""
"画面共有は拒否されました.ブラウザの画面共有の設定を確認して下さい. Chromeのアドレスバーに chrome://flags/#enable-"
"usermedia-screen-capture "
"を入力して開き、スクリーンキャプチャのサポートを有効にしてください。その後ブラウザを再起動してください。"
msgid "Use browser language" msgid "Use browser language"
msgstr "ブラウザの言語を使用" msgstr "ブラウザの言語を使用"
@ -437,19 +439,18 @@ msgstr "アクセスコードの確認に失敗しました.インターネッ
#, fuzzy, python-format #, fuzzy, python-format
msgid "and %s" msgid "and %s"
msgstr "" msgstr "と %2"
#, python-format #, python-format
msgid "and %d others" msgid "and %d others"
msgstr "" msgstr ""
msgid "User" msgid "User"
msgstr "" msgstr "ユーザー"
msgid "Someone" msgid "Someone"
msgstr "" msgstr "誰か"
#, fuzzy
msgid "Me" msgid "Me"
msgstr "名前" msgstr ""

100
src/styles/color-pallete.html

@ -0,0 +1,100 @@
<!DOCTYPE html>
<title>CSS site color pallete</title>
<style>
td {
width: 20em;
padding: 0 2%;
}
.color-block {
height: 50px;
border: 10px solid white;
}
.color-desc {
height: 25px;
padding: 5% 0 0 0;
text-align: center;
}
</style>
<table>
<tr>
<td class="color-desc">
$background: #e5e5e5
</td>
<td class="color-desc">
$componentbg: #f5f5f5
</td>
<td class="color-desc">
$componentfg1: #262626
</td>
</tr>
<tr>
<td class="color-block" style="background-color: #e5e5e5;"></td>
<td class="color-block" style="background-color: #f5f5f5;"></td>
<td class="color-block" style="background-color: #262626;"></td>
</tr>
<tr>
<td class="color-desc">
$componentfg2: rgba(0,0,0,.5)
</td>
<td class="color-desc">
$componentfg3: rgba(0,0,0,.2)
</td>
<td class="color-desc">
$componentfg4: #737373
</td>
</tr>
<tr>
<td class="color-block" style="background-color: rgba(0,0,0,.5);"></td>
<td class="color-block" style="background-color: rgba(0,0,0,.2);"></td>
<td class="color-block" style="background-color: #737373;"></td>
</tr>
<tr>
<td class="color-desc">
$sidepanebg: white
</td>
<td class="color-desc">
$bordercolor: #ccc
</td>
<td class="color-desc">
$actioncolor1: rgb(132,184,25)
</td>
</tr>
<tr>
<td class="color-block" style="background-color: white;"></td>
<td class="color-block" style="background-color: #ccc;"></td>
<td class="color-block" style="background-color: rgb(132,184,25);"></td>
</tr>
<tr>
<td class="color-desc">
$actioncolor2: rgb(0,149,52)
</td>
<td class="color-desc">
none: #222
</td>
<td class="color-desc">
none: #db4f39
</td>
</tr>
<tr>
<td class="color-block" style="background-color: rgb(0,149,52);"></td>
<td class="color-block" style="background-color: #222;"></td>
<td class="color-block" style="background-color: #db4f39;"></td>
</tr>
<tr>
<td class="color-desc">
none: #84b819
</td>
<td class="color-desc">
none: rgba(0,0,0,.4)
</td>
<td class="color-desc">
none: rgba(0,0,0,.3)
</td>
</tr>
<tr>
<td class="color-block" style="background-color: #84b819;"></td>
<td class="color-block" style="background-color: rgba(0,0,0,.4);"></td>
<td class="color-block" style="background-color: rgba(0,0,0,.3);"></td>
</tr>
</table>

327
src/styles/components/_audiovideo.scss

@ -19,59 +19,264 @@
* *
*/ */
#audiovideo { #audiovideo {
position:absolute; position:absolute;
left:0px; left:0px;
top:44px; top:44px;
bottom:0px; bottom:0px;
right:0px; right:0px;
boder-top:1px solid $bordercolor; border-top:1px solid $bordercolor;
-webkit-user-select: none; -webkit-user-select: none;
-khtml-user-select: none; -khtml-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-o-user-select: none; -o-user-select: none;
user-select: none; user-select: none;
} }
#audiovideo.fullscreen { #audiovideo.fullscreen {
top:0px !important; top:0px !important;
right:0px !important; right:0px !important;
bottom:0px !important; bottom:0px !important;
left:0px !important; left:0px !important;
background:black !important; background:black !important;
} }
#audiovideo.fullscreen .remoteVideo .peeractions { #audiovideo.fullscreen .remoteVideo .peerActions {
display:none; display:none;
} }
.withChat #audiovideo, .withBuddylist #audiovideo { .withChat #audiovideo, .withBuddylist #audiovideo {
right:260px; right:260px;
} }
.withBuddylist.withChat #audiovideo { .withBuddylist.withChat #audiovideo {
right:520px; right:520px;
} }
.mainScreenshare {
#audiovideo { .audiovideo {
width:150px; position:absolute;
.remoteVideo .peerlabel { top:0px;
font-size:12px; left:0px;
font-weight:bold; bottom:0px;
} right:0px;
.remoteVideo .peeractions i { }
font-size:1em;
} .audiovideo.active {
perspective: 1000;
-webkit-perspective: 1000;
}
.audiovideo .audiovideoBase {
position:relative;
width:100%;
height:100%;
transition-property: transform;
-webkit-transition-property: -webkit-transform;
transition-duration: 2s;
-webkit-transition-duration: 2s;
transform: rotateY(0deg);
-webkit-transform: rotateY(0deg);
z-index:2;
}
.audiovideo.active .audiovideoBase {
transform: rotateY(180deg);
-webkit-transform: rotateY(180deg);
}
.audiovideo .localContainer {
position: absolute;
left:0px;
right:0px;
top:0px;
bottom:0px;
transform: scale(-1, 1);
-webkit-transform: scale(-1, 1);
pointer-events:none;
z-index:2;
}
.audiovideo .remoteContainer {
position: absolute;
left:0px;
right:0px;
top:0px;
bottom:0px;
transform: rotateY(180deg);
-webkit-transform: rotateY(180deg);
pointer-events:none;
z-index:2;
}
.audiovideo .miniContainer {
position: absolute;
max-height: 18%;
bottom: 2px;
right: 2px;
transform: scale(-1, 1);
-webkit-transform: scale(-1, 1);
opacity: 0;
transition-property: opacity;
-webkit-transition-property: opacity;
transition-duration: 0.5s;
-webkit-transition-duration: 0.5s;
}
.audiovideo .miniContainer.visible {
opacity: 1;
}
.audiovideo .miniVideo {
max-height: 100%;
max-width:100%;
display:block;
}
.audiovideo .localVideo {
width: 100%;
max-height: 100%;
opacity: 0;
transition-property: opacity;
-webkit-transition-property: opacity;
transition-duration: 2s;
-webkit-transition-duration: 2s;
background: rgba(0,0,0,0.4);
display:block;
}
.audiovideo .remoteVideos {
position:absolute;
left:0px;
right:0px;
bottom:0px;
top:0px;
opacity: 0;
transition-property: opacity;
-webkit-transition-property: opacity;
transition-duration: 2s;
-webkit-transition-duration: 2s;
video {
width:100%;
height:100%;
display:block;
} }
} }
.remoteVideo .peerlabel { .audiovideo {
.remoteVideo {
display: inline-block;
width:100%;
max-width:100%;
max-height:100%;
vertical-align:bottom;
position:relative;
visibility:hidden;
background: rgba(0,0,0,0.4);
overflow:hidden;
}
.remoteVideo.withvideo {
visibility:visible;
}
.remoteVideo.onlyaudio {
visibility:visible;
background: #666;
}
.remoteVideo .onlyaudio {
display:none;
position:absolute;
left:0px;
top:45%;
right:0px;
color:rgba(255,255,255,0.3);
text-align:center;
font-size:80px;
margin-top:-40px;
pointer-events:auto;
}
.remoteVideo.onlyaudio video {
display:none;
}
.remoteVideo.onlyaudio .onlyaudio {
display:block;
}
.remoteVideo .peerActions {
position: absolute;
z-index: 10;
left:0px;
right:0px;
bottom:5%;
text-align:center;
opacity:.0;
transition-property: opacity;
-webkit-transition-property: opacity;
transition-duration: 0.2s;
-webkit-transition-duration: 0.2s;
pointer-events:auto;
}
.remoteVideo .peerActions i {
font-size:3vw;
}
.remoteVideo .peerActions:hover {
opacity:.5;
}
.remoteVideo .peerLabel {
position: absolute;
z-index:8;
left:4%;
bottom:4%;
font-size:2.5vw;
color:white;
opacity:.7;
text-shadow: 0px 0px 4px black;
max-width:30%;
overflow:hidden;
white-space:nowrap;
text-overflow:ellipsis;
padding:4px;
}
}
.audiovideo .overlayActions {
position: absolute;
left:0px;
top:0px;
bottom:0px;
height:70px;
margin:auto 0;
width:40px;
padding:3px 0;
background: rgba(0,0,0,0.9);
z-index:5;
opacity:0;
button {
width:40px;
display: block;
color:#ccc;
cursor:pointer;
text-shadow: 0 0 5px black;
outline: 0;
}
}
.audiovideo.active:hover .overlayActions {
opacity: 0.3;
}
.audiovideo.active .overlayActions:hover {
opacity: 0.6;
}
.remoteVideo .peerLabel {
-webkit-transition: color 500ms ease-out; -webkit-transition: color 500ms ease-out;
-moz-transition: color 500ms ease-out; -moz-transition: color 500ms ease-out;
-o-transition: color 500ms ease-out; -o-transition: color 500ms ease-out;
transition: color 500ms ease-out; transition: color 500ms ease-out;
} }
.remoteVideo.talking .peerlabel { .remoteVideo.talking .peerLabel {
color: $audiovideolevel; color: #9dd53a;
} }
.remoteVideo .overlaylogo { .remoteVideo .overlayLogo {
position:absolute; position:absolute;
right:4%; right:4%;
top:4%; top:4%;
@ -88,37 +293,63 @@ right:520px;
pointer-events: none; pointer-events: none;
} }
.localVideo video { .miniContainer video {
border:1px solid transparent; border:1px solid transparent;
} }
.localVideo.talking video { .miniContainer.talking video {
border:1px solid $audiovideolevel; border:1px solid #9dd53a;
}
.renderer-smally {
width:150px;
.remoteVideos {
padding-bottom: 85px;
}
.miniContainer {
bottom:0px;
left:0px;
right:0px;
max-height:none;
height:85px;
}
.remoteVideo .peerLabel {
font-size:.9em;
font-weight:bold;
}
.remoteVideo .peerActions i {
font-size:1em;
}
}
.renderer-onepeople {
} }
.renderer-conferencekiosk { .renderer-conferencekiosk {
#remoteVideos { .remoteVideos {
top:auto; top:auto;
bottom:2px; bottom:2px;
text-align:center; text-align:center;
background:rgba(0,0,0,0.4); background:rgba(0,0,0,0.4);
padding-right:192px; min-height:108px;
pointer-events:auto;
white-space:nowrap;
>div { >div {
height:108px; height:108px;
width:192px; width:192px;
cursor:pointer; cursor:pointer;
} }
.overlaylogo { .overlayLogo {
display:none; display:none;
} }
.peerlabel, .peeractions i { .peerLabel, .peerActions i {
font-size:1.1em; font-size:1.1em;
} }
.peerlabel { .peerLabel {
background: rgba(0,0,0,0.9); background: rgba(0,0,0,0.9);
} }
} }
#mini { .miniContainer {
max-height: none; max-height: none;
height:108px; height:108px;
width:192px; width:192px;
@ -136,14 +367,13 @@ right:520px;
transition-duration: 2s; transition-duration: 2s;
-webkit-transition-duration: 2s; -webkit-transition-duration: 2s;
} }
.bigVideo video {
width:100%;
height:100%;
}
} }
.bigVideo video {
width:100%;
height:100%;
}
@media only screen and (max-width: 630px) { @media only screen and (max-width: 630px) {
.mainScreenshare #audiovideo { .mainScreenshare #audiovideo {
display:none; display:none;
@ -152,6 +382,5 @@ right:520px;
@media only screen and (max-width:590px) { @media only screen and (max-width:590px) {
#audiovideo { #audiovideo {
right:0px; right:0px;
z-index:12;
} }
} }

319
src/styles/components/_bar.scss

@ -18,167 +18,178 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#bar { #bar {
-webkit-box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.2); background-color: $componentbg;
box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.2); color: $componentfg1;
position: absolute; font: bold 1em/40px $font-sans-serif;
top: 0; left: 0;
left: 0; min-height: 44px;
right: 0; position: absolute;
min-height: 44px; right: 0;
background-color: $componentbg; top: 0;
color: $componentfg1; text-align: center;
font-size: 1em; z-index: 5;
font-weight: bold; @include box-shadow(0 2px 10px 0 $componentfg3);
line-height: 40px; @include touch-callout(none);
text-align: center; @include user-select(none);
-webkit-touch-callout: none; > .left {
-webkit-user-select: none; bottom: 0;
-khtml-user-select: none; left: 0;
-moz-user-select: none; top: 0;
-ms-user-select: none; padding-left: 12px;
user-select: none; position: absolute;
z-index:5; // TODO(theurere): cleanup
} > *, #bar > .right > * {
#bar > .left { vertical-align: middle;
position: absolute; }
left:0px; }
top:0px; .logo {
bottom:0px; background: url(../img/logo-small.png) no-repeat;
padding-left:12px; background-size: 100%;
} display: inline-block;
#bar .logo { color: black;
font-size:11px; font: normal 11px/11px $font-sans-serif;
line-height:11px; height: 32px;
font-weight:normal; text-align: left;
color:black; width: 90px;
width:90px; > span {
height:32px; font-style: italic;
background-size:100%; left: 38px;
background-repeat:no-repeat; position: relative;
background-image:url(../img/logo-small.png); top: 26px;
display:inline-block; }
text-align:left; > span a {
} color: $dgrey;
#bar .logo > span { }
position: relative; }
left: 38px;
top: 26px;
font-style: italic;
}
#bar .logo > span a {
color:#222;
}
#bar > .right {
position: absolute;
right:0px;
top:1px;
bottom:1px;
padding-right:8px;
}
#bar > .left > *, #bar > .right > * {
vertical-align: middle;
}
#bar > .right .btn {
border-color:transparent;
background:transparent;
color:rgba(0,0,0,0.3);
height:42px;
width:42px;
display: inline-block;
font-size:24px;
padding:0px;
line-height:40px;
text-align:center;
margin-left:-8px;
}
#bar > .right .btn:focus {
border: none;
box-shadow: 0;
outline: none;
}
#bar > .right .btn:hover {
border-color: #ccc;
background-color:none;
color:rgba(0,0,0,0.4);
}
#bar > .right .btn.active {
border-color: #ccc;
background-color:none;
color:rgba(0,0,0,0.4);
}
#bar > .right .btn.active.amutebtn {
background-color: #db4f39;
border-color: #db4f39;
color: white;
}
#bar > .right .btn.active.aenablebtn {
background-color: #84b819;
border-color: #84b819;
color: white;
}
#bar .btn {
position:relative;
}
#bar .badge {
background-color: #84b819;
font-size:.4em;
position:absolute;
right:0px;
top:2px;
border:1px solid white;
}
#bar .userpicture {
margin:-5px 0.5em 0px 0.5em;
width:46px;
height:46px;
border-radius:2px;
display:inline-block;
}
#bar > .middle {
z-index:5;
background-color: $componentbg;
display:inline-block;
padding:0 1em;
position:relative;
min-height: 44px;
vertical-align:middle;
margin-left:-70px;
} }
@media all and (max-width: 700px) { #bar {
#bar { > .right {
z-index:40; bottom: 1px;
-webkit-box-shadow: none; padding-right: 8px;
box-shadow: none; position: absolute;
right: 0;
top: 1px;
.btn {
background: transparent;
border-color: transparent;
color: $grey3;
display: inline-block;
font: 24px/40px $font-sans-serif;
height: 42px;
margin-left: -4px;
padding: 0;
text-align: center;
width: 42px;
} }
#bar > .middle.status-connecting, #bar > .middle.status-closed, #bar > .middle.status-reconnecting, #bar > .middle.status-error, #bar > .middle.status-ringing { .btn:focus {
max-width:100%; border: none;
border-bottom:1px solid $bordercolor; @include box-shadow(0);
.actions { outline: none;
display: block;
padding:.2em 0 .8em 0;
}
min-height: 45px;
} }
#bar > .middle.status-connected, #bar > .middle.status-conference { .btn:hover {
position:absolute; background-color: none;
left:0px; border-color: #ccc;
right:0px; color: $grey4;
max-width:100%;
} }
#bar > .middle { .btn.active {
display:block; background-color: none;
max-width:40%; border-color: #ccc;
overflow:hidden; color: $grey4;
text-overflow:ellipsis; }
white-space:nowrap; .btn.active.amutebtn {
margin-left:0px; background-color: $red;
border-color: $red;
img { color: white;
display:none;
}
} }
.btn.active.aenablebtn {
background-color: $actioncolor1;
border-color: $actioncolor1;
color: white;
}
}
}
#bar {
.btn {
position: relative;
}
.badge {
background-color: $actioncolor1;
border: 1px solid white;
font-size: .4em;
position: absolute;
right: 0;
top: 2px;
}
.userpicture {
border-radius: 2px;
display: inline-block;
height: 46px;
margin: -5px .5em 0 .5em;
width: 46px;
}
> .middle {
background-color: $componentbg;
display: inline-block;
min-height: 44px;
padding: 0 1em;
position: relative;
margin-left: -70px;
vertical-align: middle;
z-index: 5;
@include breakpt($breakpoint-medium) {
display: block;
margin-left: 0px;
max-width: 40%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
} }
#bar {
@include breakpt($breakpoint-medium) {
z-index: 40;
@include box-shadow(none);
}
> .middle img {
@include breakpt($breakpoint-medium) {
display: none;
}
}
> .middle.status-connected,
> .middle.status-conference {
@include breakpt($breakpoint-medium) {
max-width: 100%;
left: 0;
position: absolute;
right: 0;
}
}
> .middle.status-connecting,
> .middle.status-closed,
> .middle.status-reconnecting,
> .middle.status-error,
> .middle.status-ringing {
@include breakpt($breakpoint-medium) {
border-bottom: 1px solid $bordercolor;
max-width: 100%;
min-height: 45px;
}
}
// TODO(theurere): cleanup
> .middle.status-connecting .actions,
> .middle.status-closed .actions,
> .middle.status-reconnecting .actions,
> .middle.status-error .actions,
> .middle.status-ringing .actions {
@include breakpt($breakpoint-medium) {
display: block;
padding: .2em 0 .8em 0;
}
}
}

179
src/styles/components/_webrtc.scss

@ -1,179 +0,0 @@
/*
* Spreed Speak Freely.
* Copyright (C) 2013-2014 struktur AG
*
* This file is part of Spreed Speak Freely.
*
* 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/>.
*
*/
#container {
position: absolute;
/*margin: 0px auto;*/
perspective: 1000;
-webkit-perspective: 1000;
top:0px;
left:0px;
bottom:0px;
right:0px;
}
#container .visible {
opacity:1 !important;
}
#card {
position:relative;
width:100%;
height:100%;
transition-property: transform;
-webkit-transition-property: -webkit-transform;
transition-duration: 2s;
-webkit-transition-duration: 2s;
transform: rotateY(0deg);
-webkit-transform: rotateY(0deg);
}
#card.active {
transform: rotateY(180deg);
-webkit-transform: rotateY(180deg);
}
#local {
position: absolute;
left:0px;
right:0px;
top:0px;
bottom:0px;
transform: scale(-1, 1);
-webkit-transform: scale(-1, 1);
pointer-events:none;
}
#remote {
position: absolute;
left:0px;
right:0px;
top:0px;
bottom:0px;
transform: rotateY(180deg);
-webkit-transform: rotateY(180deg);
}
#mini {
position: absolute;
max-height: 18%;
bottom: 2px;
right: 2px;
transform: scale(-1, 1);
-webkit-transform: scale(-1, 1);
opacity: 0;
transition-property: opacity;
-webkit-transition-property: opacity;
transition-duration: 0.5s;
-webkit-transition-duration: 0.5s;
}
#localVideo {
width: 100%;
height: 100%;
opacity: 0;
transition-property: opacity;
-webkit-transition-property: opacity;
transition-duration: 2s;
-webkit-transition-duration: 2s;
background: rgba(0,0,0,0.4);
/*background: red;*/
}
#remoteVideos {
position:absolute;
left:0px;
right:0px;
bottom:0px;
top:0px;
opacity: 0;
transition-property: opacity;
-webkit-transition-property: opacity;
transition-duration: 2s;
-webkit-transition-duration: 2s;
/*background: blue;*/
}
#remoteVideos video {
width:100%;
height:100%;
}
#miniVideo {
max-height: 100%;
max-width:100%;
}
.remoteVideo {
display: inline-block;
width:100%;
height:100%;
vertical-align:bottom;
position:relative;
visibility:hidden;
background: rgba(0,0,0,0.4);
}
.remoteVideo.withvideo {
visibility:visible;
}
.remoteVideo.onlyaudio {
visibility:visible;
background: #666;
}
.remoteVideo .onlyaudio {
display:none;
position:absolute;
left:0px;
top:45%;
right:0px;
color:rgba(255,255,255,0.3);
text-align:center;
font-size:80px;
margin-top:-40px;
}
.remoteVideo.onlyaudio video {
display:none;
}
.remoteVideo.onlyaudio .onlyaudio {
display:block;
}
.remoteVideo .peeractions {
position: absolute;
z-index: 10;
left:0px;
right:0px;
bottom:5%;
text-align:center;
opacity:.0;
transition-property: opacity;
-webkit-transition-property: opacity;
transition-duration: 0.2s;
-webkit-transition-duration: 0.2s;
}
.remoteVideo .peeractions i {
font-size:3vw;
}
.remoteVideo:hover .peeractions {
opacity:.5;
}
.remoteVideo .peerlabel {
position: absolute;
z-index:8;
left:4%;
bottom:4%;
font-size:2.5vw;
color:white;
opacity:.7;
text-shadow: 0px 0px 4px black;
max-width:30%;
overflow:hidden;
white-space:nowrap;
text-overflow:ellipsis;
padding:4px;
}

1
src/styles/global/_base.scss

@ -21,6 +21,7 @@
html, body { html, body {
background-clip: padding-box; background-clip: padding-box;
height: 100%; height: 100%;
font: normal $base-font-size $font-sans-serif;
} }
body { body {
margin: 0; margin: 0;

23
src/styles/global/_mixins.scss

@ -0,0 +1,23 @@
@mixin breakpt($pt) {
@media (max-width: $pt) {
@content;
}
}
@mixin user-select($select) {
-khtml-user-select: $select;
-moz-user-select: $select;
-ms-user-select: $select;
-webkit-user-select: $select;
user-select: $select;
}
@mixin touch-callout($callout) {
-webkit-touch-callout: $callout;
}
@mixin box-shadow($shadow) {
-webkit-box-shadow: $shadow;
box-shadow: $shadow;
}

66
src/styles/global/_variables.scss

@ -1,48 +1,42 @@
/*
// ** Custom Variables ** * Spreed Speak Freely.
// -------------------------------------------------- * Copyright (C) 2013-2014 struktur AG
*
// Colors * This file is part of Spreed Speak Freely.
*
* 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/>.
*
*/
$background: #e5e5e5; $background: #e5e5e5;
$componentbg: #f5f5f5; $componentbg: #f5f5f5;
$componentfg1: #262626; $componentfg1: #262626;
$componentfg2: rgba(0,0,0,.5); $componentfg2: rgba(0,0,0,.5);
$componentfg3: rgba(0,0,0,.2); $componentfg3: rgba(0,0,0,.2); //#ccc
$componentfg4: #737373; $componentfg4: #737373;
$sidepanebg: white; $sidepanebg: white;
$bordercolor: #ccc; $bordercolor: #ccc;
$actioncolor1: rgb(132,184,25); $actioncolor1: rgb(132,184,25);
$actioncolor2: rgb(0,149,52); $actioncolor2: rgb(0,149,52);
$audiovideolevel: #9dd53a;
// ** Fontawsome changed variables **
// original at libs/fontawesome/_variables.scss
// --------------------------
// ** Bootstrap changed variables **
// original at libs/bootstrap/_variables.scss
// --------------------------------------------------
//== Scaffolding
//
// ## Settings for some of the most global styles.
//** Background color for `<body>`.
$body-bg: $background;
//== Typography $grey3: rgba(0,0,0,.3);
// $grey4: rgba(0,0,0,.4);
//## Font, line-height, and color for body text, headings, and more. $dgrey: rgb(34,34,34);
$red: rgb(219,79,57);
$font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif; $breakpoint-small: 480px;
$font-family-serif: Georgia, "Times New Roman", Times, serif; $breakpoint-medium: 700px;
//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`. $breakpoint-large: 1280px;
$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
$font-family-base: $font-family-sans-serif;
$font-size-base: 13px; $font-sans-serif: "Helvetica Neue",Helvetica,Arial,sans-serif;
$base-font-size: 13px;

5
src/styles/main.scss

@ -19,11 +19,11 @@
* *
*/ */
@import "global/variables";
@import "compass"; @import "compass";
@import "libs/libs"; @import "libs/libs";
@import "global/variables"; @import "global/mixins";
@import "global/base"; @import "global/base";
@import "global/angular"; @import "global/angular";
@ -33,7 +33,6 @@
@import "components/rightslide"; @import "components/rightslide";
@import "components/bar"; @import "components/bar";
@import "components/webrtc";
@import "components/buddylist"; @import "components/buddylist";
@import "components/settings"; @import "components/settings";
@import "components/chat"; @import "components/chat";

7
static/js/app.js

@ -119,12 +119,7 @@ define([
var domain = "messages"; var domain = "messages";
var catalog = domain + "-" + lang; var catalog = domain + "-" + lang;
var bootstrap = function(translationData) { var bootstrap = function(translationData) {
if (translationData) { if (!translationData) {
// Set loaded translation data.
translationData.missing_key_callback = function(key) {
console.warn("Missing key " + key + " for " + lang);
};
} else {
// Fallback catalog in case translation could not be loaded. // Fallback catalog in case translation could not be loaded.
lang = "en"; lang = "en";
translationData = {}; translationData = {};

4
static/js/controllers/mediastreamcontroller.js

@ -589,7 +589,7 @@ define(['underscore', 'bigscreen', 'moment', 'webrtc.adapter'], function(_, BigS
changed = true; changed = true;
} }
if (changed) { if (changed) {
$scope.$broadcast("mainresize"); $scope.$broadcast("mainresize", layout.main);
} }
}); });
@ -618,7 +618,7 @@ define(['underscore', 'bigscreen', 'moment', 'webrtc.adapter'], function(_, BigS
$element.addClass(makeName("main", layout.main)); $element.addClass(makeName("main", layout.main));
} }
} }
$scope.$broadcast("mainresize"); $scope.$broadcast("mainresize", layout.main);
}}() }}()
), true); ), true);

2
static/js/controllers/roomchangecontroller.js

@ -23,7 +23,7 @@ define([], function() {
// RoomchangeController // RoomchangeController
return ["$scope", "$element", "$window", "$location", "mediaStream", "$http", "$timeout", function($scope, $element, $window, $location, mediaStream, $http, $timeout) { return ["$scope", "$element", "$window", "$location", "mediaStream", "$http", "$timeout", function($scope, $element, $window, $location, mediaStream, $http, $timeout) {
console.log("Room change controller", $element, $scope.roomdata); //console.log("Room change controller", $element, $scope.roomdata);
var url = mediaStream.url.api("rooms"); var url = mediaStream.url.api("rooms");

93
static/js/directives/audiovideo.js

@ -30,17 +30,19 @@ define(['jquery', 'underscore', 'text!partials/audiovideo.html', 'text!partials/
var peers = {}; var peers = {};
var events = $({}); var events = $({});
$scope.card = $element; $scope.container = $element.get(0);
$scope.container = $element.parent().get(0); $scope.layoutparent = $element.parent();
$scope.layoutparent = $element.parent().parent();
$scope.remoteVideos = $element.find("#remoteVideos").get(0); $scope.remoteVideos = $element.find(".remoteVideos").get(0);
$scope.localVideo = $element.find("#localVideo").get(0); $scope.localVideo = $element.find(".localVideo").get(0);
$scope.miniVideo = $element.find("#miniVideo").get(0); $scope.miniVideo = $element.find(".miniVideo").get(0);
$scope.mini = $element.find("#mini"); $scope.mini = $element.find(".miniContainer").get(0);
$scope.hasUsermedia = false; $scope.hasUsermedia = false;
$scope.isActive = false; $scope.isActive = false;
$scope.rendererName = $scope.defaultRendererName = "onepeople";
//console.log("audiovideo", localVideo, miniVideo); //console.log("audiovideo", localVideo, miniVideo);
$scope.addRemoteStream = function(stream, currentcall) { $scope.addRemoteStream = function(stream, currentcall) {
@ -82,12 +84,12 @@ define(['jquery', 'underscore', 'text!partials/audiovideo.html', 'text!partials/
}); });
} }
scope.$emit("active", currentcall); scope.$emit("active", currentcall);
$scope.resize(); $scope.redraw();
}, function() { }, function() {
peers[peerid] = scope; peers[peerid] = scope;
console.warn("We did not receive video data for remote stream", currentcall, stream, video); console.warn("We did not receive video data for remote stream", currentcall, stream, video);
scope.$emit("active", currentcall); scope.$emit("active", currentcall);
$scope.resize(); $scope.redraw();
}); });
scope.doChat = function() { scope.doChat = function() {
$scope.$emit("startchat", currentcall.id, {autofocus: true, restore: true}); $scope.$emit("startchat", currentcall.id, {autofocus: true, restore: true});
@ -107,7 +109,7 @@ define(['jquery', 'underscore', 'text!partials/audiovideo.html', 'text!partials/
subscope.element.remove(); subscope.element.remove();
} }
subscope.$destroy(); subscope.$destroy();
$scope.resize(); $scope.redraw();
} }
}; };
@ -133,7 +135,7 @@ define(['jquery', 'underscore', 'text!partials/audiovideo.html', 'text!partials/
if (!$scope.isActive) { if (!$scope.isActive) {
$scope.isActive = true; $scope.isActive = true;
$scope.remoteVideos.style.opacity = 1; $scope.remoteVideos.style.opacity = 1;
$scope.card.addClass("active"); $element.addClass("active");
//console.log("active 3"); //console.log("active 3");
_.delay(function() { _.delay(function() {
$scope.localVideo.style.opacity = 0; $scope.localVideo.style.opacity = 0;
@ -141,7 +143,7 @@ define(['jquery', 'underscore', 'text!partials/audiovideo.html', 'text!partials/
}, 500); }, 500);
_.delay(function() { _.delay(function() {
//console.log("active 4", $scope.mini); //console.log("active 4", $scope.mini);
$scope.mini.addClass("visible"); //.style.opacity = 1; $($scope.mini).addClass("visible");
}, 1000); }, 1000);
} }
@ -167,7 +169,7 @@ define(['jquery', 'underscore', 'text!partials/audiovideo.html', 'text!partials/
} }
if ($scope.localVideo.videoWidth > 0) { if ($scope.localVideo.videoWidth > 0) {
$scope.localVideo.style.opacity = 1; $scope.localVideo.style.opacity = 1;
$scope.resize(); $scope.redraw();
} else { } else {
count++; count++;
if (count < 100) { if (count < 100) {
@ -196,14 +198,15 @@ define(['jquery', 'underscore', 'text!partials/audiovideo.html', 'text!partials/
$scope.miniVideo.src = ''; $scope.miniVideo.src = '';
$($scope.remoteVideos).empty(); $($scope.remoteVideos).empty();
}, 1500); }, 1500);
$scope.mini.removeClass("visible"); $($scope.mini).removeClass("visible");
$scope.localVideo.style.opacity = 0; $scope.localVideo.style.opacity = 0;
$scope.remoteVideos.style.opacity = 0; $scope.remoteVideos.style.opacity = 0;
$scope.card.removeClass('active'); $element.removeClass('active');
_.each(peers, function(scope, k) { _.each(peers, function(scope, k) {
scope.$destroy(); scope.$destroy();
delete peers[k]; delete peers[k];
}); });
$scope.rendererName = $scope.defaultRendererName;
}); });
@ -237,38 +240,66 @@ define(['jquery', 'underscore', 'text!partials/audiovideo.html', 'text!partials/
//console.log("compile", arguments) //console.log("compile", arguments)
$(scope.card).on("doubletap dblclick", _.debounce(scope.toggleFullscreen, 100, true)); iElement.on("doubletap dblclick", _.debounce(scope.toggleFullscreen, 100, true));
//scope.rendererName = "conferencekiosk"; var rendererName = null;
scope.rendererName = "onepeople"; var getRendererName = function() {
// Return name of current renderer.
if (rendererName !== null) {
return rendererName;
} else {
return scope.rendererName;
}
};
var needsResize = false; scope.setRenderer = function(name) {
scope.resize = function() { scope.rendererName = name;
needsResize = true;
}; };
var resize = function() { var needsRedraw = false;
scope.redraw = function() {
needsRedraw = true;
};
var redraw = function() {
var size = { var size = {
width: scope.layoutparent.width(), width: scope.layoutparent.width(),
height: scope.layoutparent.height() height: scope.layoutparent.height()
} }
videoLayout.update(scope.rendererName, size, scope, controller); var again = videoLayout.update(getRendererName(), size, scope, controller);
if (again) {
// Layout needs a redraw.
needsRedraw = true;
}
}; };
$($window).on("resize", scope.resize); // Make sure we draw on resize.
scope.$on("mainresize", function() { $($window).on("resize", scope.redraw);
_.defer(scope.resize); scope.$on("mainresize", function(event, main) {
if (main) {
// Force smally renderer when we have a main view.
rendererName = "smally"
} else if (rendererName) {
rendererName = null;
}
_.defer(scope.redraw);
});
scope.redraw();
// Make sure we draw when the renderer was changed.
scope.$watch("rendererName", function() {
_.defer(scope.redraw);
}); });
scope.resize();
// Update function run in rendering thread.
var update = function() { var update = function() {
if (needsResize) { if (needsRedraw) {
needsResize =false; needsRedraw =false;
resize(); redraw();
} }
requestAnimationFrame(update); requestAnimationFrame(update);
} }
update(); _.defer(update);
} }

4
static/js/mediastream/peerconference.js

@ -183,12 +183,16 @@ define(['underscore', 'mediastream/peercall'], function(_, PeerCall) {
console.log("Conference peer connection state changed", iceConnectionState, currentcall); console.log("Conference peer connection state changed", iceConnectionState, currentcall);
switch (iceConnectionState) { switch (iceConnectionState) {
case "completed":
case "connected": case "connected":
if (!this.callsIn.hasOwnProperty(currentcall.id)) { if (!this.callsIn.hasOwnProperty(currentcall.id)) {
this.callsIn[currentcall.id] = true; this.callsIn[currentcall.id] = true;
this.pushUpdate(); this.pushUpdate();
} }
break; break;
case "failed":
console.warn("Conference peer connection state failed", currentcall);
break;
} }
this.webrtc.onConnectionStateChange(iceConnectionState, currentcall); this.webrtc.onConnectionStateChange(iceConnectionState, currentcall);

19
static/js/mediastream/webrtc.js

@ -90,18 +90,12 @@ define([
this.api.e.bind("received.offer received.candidate received.answer received.bye received.conference", _.bind(this.processReceived, this)); this.api.e.bind("received.offer received.candidate received.answer received.bye received.conference", _.bind(this.processReceived, this));
window.onbeforeunload = _.bind(function() { $(window).on("unload", _.bind(function() {
if (this.currentcall) { this.doHangup("unload");
this.currentcall.close();
this.api.sendBye(this.currentcall.id);
}
if (this.currentconference) {
this.currentconference.close();
}
if (this.api.connector) { if (this.api.connector) {
this.api.connector.disabled = true; this.api.connector.disabled = true;
} }
}, this); }, this));
// Create default media (audio/video). // Create default media (audio/video).
this.usermedia = new UserMedia(); this.usermedia = new UserMedia();
@ -443,6 +437,7 @@ define([
xfer.e.on("connectionStateChange", _.bind(function(event, iceConnectionState, currentxfer) { xfer.e.on("connectionStateChange", _.bind(function(event, iceConnectionState, currentxfer) {
console.log("Xfer state changed", iceConnectionState); console.log("Xfer state changed", iceConnectionState);
switch (iceConnectionState) { switch (iceConnectionState) {
case "completed":
case "connected": case "connected":
// Do nothing here, we wait for dataReady. // Do nothing here, we wait for dataReady.
break break
@ -516,6 +511,7 @@ define([
peerscreenshare.e.on("connectionStateChange", _.bind(function(event, iceConnectionState, currentscreenshare) { peerscreenshare.e.on("connectionStateChange", _.bind(function(event, iceConnectionState, currentscreenshare) {
console.log("Screen share state changed", iceConnectionState); console.log("Screen share state changed", iceConnectionState);
switch (iceConnectionState) { switch (iceConnectionState) {
case "completed":
case "connected": case "connected":
opts.connected(currentscreenshare); opts.connected(currentscreenshare);
break break
@ -645,7 +641,10 @@ define([
}; };
WebRTC.prototype.onConnectionStateChange = function(iceConnectionState, currentcall) { WebRTC.prototype.onConnectionStateChange = function(iceConnectionState, currentcall) {
this.e.triggerHandler('statechange', [iceConnectionState, currentcall]); // Defer this to allow native event handlers to complete before running more stuff.
_.defer(_.bind(function() {
this.e.triggerHandler('statechange', [iceConnectionState, currentcall]);
}, this));
}; };
WebRTC.prototype.onRemoteStreamAdded = function(stream, currentcall) { WebRTC.prototype.onRemoteStreamAdded = function(stream, currentcall) {

2
static/js/services/fileupload.js

@ -129,7 +129,7 @@ define(["jquery", "underscore", "webrtc.adapter"], function($, _) {
}; };
FileUpload.prototype.bindDrop = function(namespace, element, cb) { FileUpload.prototype.bindDrop = function(namespace, element, cb) {
console.log("Binding file upload drop to", namespace, element); //console.log("Binding file upload drop to", namespace, element);
// Helper to allow later modifications. // Helper to allow later modifications.
var binder = { var binder = {

74
static/js/services/videolayout.js

@ -42,7 +42,7 @@ define(["jquery", "underscore"], function($, _) {
// videoLayout // videoLayout
return ["$window", function($window) { return ["$window", function($window) {
// Video layout with all persons rendered the same size. // Video layout with all videos rendered the same size.
var OnePeople = function(container, scope, controller) { var OnePeople = function(container, scope, controller) {
}; };
@ -59,15 +59,6 @@ define(["jquery", "underscore"], function($, _) {
if (videos.length) { if (videos.length) {
var remoteSize = getRemoteVideoSize(videos, peers); var remoteSize = getRemoteVideoSize(videos, peers);
/*if (videos.length === 1) {
var remoteVideo = peers[videos[0]].element.find("video").get(0);
videoWidth = remoteVideo.videoWidth;
videoHeight = remoteVideo.videoHeight;
console.log("Remote video size: ", videoWidth, videoHeight);
} else {
videoWidth = 1920;
videoHeight = 1080;
}*/
videoWidth = remoteSize.width; videoWidth = remoteSize.width;
videoHeight = remoteSize.height; videoHeight = remoteSize.height;
} }
@ -94,18 +85,18 @@ define(["jquery", "underscore"], function($, _) {
} }
var aspectRatio = videoWidth/videoHeight; var aspectRatio = videoWidth/videoHeight;
var innerHeight = size.height; //scope.layoutparent.height(); var innerHeight = size.height;
var innerWidth = size.width; //scope.layoutparent.width(); var innerWidth = size.width;
//console.log("resize", innerHeight, innerWidth); //console.log("resize", innerHeight, innerWidth);
//console.log("resize", container, videos.length, aspectRatio, innerHeight, innerWidth); //console.log("resize", container, videos.length, aspectRatio, innerHeight, innerWidth);
var extraCSS = {};
if (videos.length === 1) { if (videos.length === 1) {
var newVideoWidth = innerWidth < aspectRatio * innerHeight ? innerWidth : aspectRatio * innerHeight; var newVideoWidth = innerWidth < aspectRatio * innerHeight ? innerWidth : aspectRatio * innerHeight;
var newVideoHeight = innerHeight < innerWidth / aspectRatio ? innerHeight : innerWidth / aspectRatio; var newVideoHeight = innerHeight < innerWidth / aspectRatio ? innerHeight : innerWidth / aspectRatio;
container.style.width = newVideoWidth + 'px'; container.style.width = newVideoWidth + 'px';
container.style.left = ((innerWidth - newVideoWidth) / 2) + 'px'; container.style.left = ((innerWidth - newVideoWidth) / 2) + 'px';
var extraCSS = {};
} else { } else {
var space = innerHeight*innerWidth; // square pixels var space = innerHeight*innerWidth; // square pixels
var videoSpace = space/videos.length; var videoSpace = space/videos.length;
@ -134,7 +125,7 @@ define(["jquery", "underscore"], function($, _) {
container.style.width = newContainerWidth + "px"; container.style.width = newContainerWidth + "px";
container.style.left = ((innerWidth - newContainerWidth) / 2) + 'px'; container.style.left = ((innerWidth - newContainerWidth) / 2) + 'px';
extraCSS = { extraCSS = {
"#remoteVideos": { ".renderer-onepeople .remoteVideos": {
">div": { ">div": {
width: singleVideoWidth+"px", width: singleVideoWidth+"px",
height: singleVideoHeight+"px" height: singleVideoHeight+"px"
@ -155,15 +146,27 @@ define(["jquery", "underscore"], function($, _) {
}; };
// Smally inherits from OnePeople
var Smally = function(container, scope, controller) {
// Call super.
OnePeople.call(this, container, scope, controller);
}
Smally.prototype = Object.create(OnePeople.prototype);
Smally.prototype.constructor = Smally;
Smally.prototype.name = "smally";
// A view with one selectable large video. The others are small.
var ConferenceKiosk = function(container, scope, controller) { var ConferenceKiosk = function(container, scope, controller) {
this.remoteVideos = $(container).find("#remoteVideos"); this.remoteVideos = $(container).find(".remoteVideos");
this.bigVideo = $("<div>").addClass("bigVideo").get(0); this.bigVideo = $("<div>").addClass("bigVideo").get(0);
this.remoteVideos.before(this.bigVideo); this.remoteVideos.before(this.bigVideo);
this.big = null; this.big = null;
this.remoteVideos.on("click", ".remoteVideo", _.bind(function(event) { this.remoteVideos.on("click", ".remoteVideo", _.bind(function(event) {
if ($(event.currentTarget).hasClass("remoteVideo")) { if ($(event.currentTarget).hasClass("remoteVideo")) {
event.stopPropagation();
this.makeBig($(event.currentTarget)); this.makeBig($(event.currentTarget));
} }
}, this)); }, this));
@ -212,6 +215,7 @@ define(["jquery", "underscore"], function($, _) {
var aspectRatio = remoteSize.width/remoteSize.height; var aspectRatio = remoteSize.width/remoteSize.height;
var innerHeight = size.height - 110; var innerHeight = size.height - 110;
var innerWidth = size.width; var innerWidth = size.width;
var extraCSS = {};
var bigVideoWidth = innerWidth < aspectRatio * innerHeight ? innerWidth : aspectRatio * innerHeight; var bigVideoWidth = innerWidth < aspectRatio * innerHeight ? innerWidth : aspectRatio * innerHeight;
var bigVideoHeight = innerHeight < innerWidth / aspectRatio ? innerHeight : innerWidth / aspectRatio; var bigVideoHeight = innerHeight < innerWidth / aspectRatio ? innerHeight : innerWidth / aspectRatio;
@ -219,19 +223,43 @@ define(["jquery", "underscore"], function($, _) {
this.bigVideo.style.width = bigVideoWidth + 'px'; this.bigVideo.style.width = bigVideoWidth + 'px';
this.bigVideo.style.height = bigVideoHeight + 'px'; this.bigVideo.style.height = bigVideoHeight + 'px';
// Make space for own video on the right if width goes low.
if (((size.width - (videos.length-1) * 192) / 2) < 192) {
extraCSS = {
".renderer-conferencekiosk .remoteVideos": {
"margin-right": "192px",
"overflow-x": "auto",
"overflow-y": "hidden"
}
};
}
$.injectCSS(extraCSS, {
truncateFirst: true,
containerName: dynamicCSSContainer
});
}; };
ConferenceKiosk.prototype.close = function(container, scope, controller) { ConferenceKiosk.prototype.close = function(container, scope, controller) {
this.closed = true; this.closed = true;
if (this.big) {
this.remoteVideos.append(this.big);
this.big.find("video").get(0).play();
}
this.big = null;
this.bigVideo.remove() this.bigVideo.remove()
this.bigVideo = null; this.bigVideo = null;
this.remoteVideos = null; this.remoteVideos = null;
}; };
// Register renderers. // Register renderers.
renderers[OnePeople.prototype.name] = OnePeople; renderers[OnePeople.prototype.name] = OnePeople;
renderers[Smally.prototype.name] = Smally;
renderers[ConferenceKiosk.prototype.name] = ConferenceKiosk; renderers[ConferenceKiosk.prototype.name] = ConferenceKiosk;
// Public api. // Public api.
var current = null; var current = null;
return { return {
@ -240,27 +268,33 @@ define(["jquery", "underscore"], function($, _) {
var videos = _.keys(controller.peers); var videos = _.keys(controller.peers);
var peers = controller.peers; var peers = controller.peers;
var container = scope.container; var container = scope.container;
var layoutparent = scope.layoutparent;
if (!current) { if (!current) {
current = new renderers[name](container, scope, controller) current = new renderers[name](container, scope, controller)
console.log("Created new video layout renderer", name, current); console.log("Created new video layout renderer", name, current);
$(container).addClass("renderer-"+name); $(layoutparent).addClass("renderer-"+name);
return true;
} else { } else {
if (current.name !== name) { if (current.name !== name) {
current.close(container, scope, controller); current.close(container, scope, controller);
$(container).removeAttr("style"); $(container).removeAttr("style");
$(container).removeClass("renderer-"+current.name); $(layoutparent).removeClass("renderer-"+current.name);
current = new renderers[name](container, scope, conroller) current = new renderers[name](container, scope, controller)
$(container).addClass("renderer-"+name); $(layoutparent).addClass("renderer-"+name);
console.log("Switched to new video layout renderer", name, current); console.log("Switched to new video layout renderer", name, current);
return true;
} }
} }
current.render(container, size, scope, videos, peers); return current.render(container, size, scope, videos, peers);
}, },
register: function(name, impl) { register: function(name, impl) {
renderers[name] = impl; renderers[name] = impl;
},
layouts: function() {
return _.keys(renderers);
} }
} }

24
static/partials/audiovideo.html

@ -1,12 +1,18 @@
<div id="card"> <div class="audiovideo">
<div id="local"> <div class="audiovideoBase">
<video id="localVideo" autoplay="autoplay" muted="true"></video> <div class="localContainer">
<div class="overlaylogo"></div> <video class="localVideo" autoplay="autoplay" muted="true"></video>
<div class="overlayLogo"></div>
</div> </div>
<div id="remote"> <div class="remoteContainer">
<div id="remoteVideos"></div> <div class="remoteVideos nicescroll"></div>
<div id="mini" class="localVideo" ng-class="{talking: talking}"> <div class="miniContainer" ng-class="{talking: talking}">
<video id="miniVideo" autoplay="autoplay" muted="true"></video> <video class="miniVideo" autoplay="autoplay" muted="true"></video>
</div> </div>
</div> </div>
</div> </div>
<div class="overlayActions">
<button class="btn btn-link" title="{{_('Standard view')}}" ng-click="setRenderer('onepeople')"><i class="fa fa-table" ></i></button>
<button class="btn btn-link" title="{{_('Kiosk view')}}" ng-click="setRenderer('conferencekiosk')"><i class="fa fa-user"></i></button>
</div>
</div>

6
static/partials/audiovideopeer.html

@ -1,9 +1,9 @@
<div class="remoteVideo" ng-class="{'withvideo': withvideo, 'onlyaudio': onlyaudio, 'talking': talking}"> <div class="remoteVideo" ng-class="{'withvideo': withvideo, 'onlyaudio': onlyaudio, 'talking': talking}">
<video autoplay="autoplay"></video> <video autoplay="autoplay"></video>
<div class="peerlabel">{{peerid|displayName}}</div> <div class="peerLabel">{{peerid|displayName}}</div>
<div class="peeractions"> <div class="peerActions">
<a title="{{_('Start chat')}}" ng-click="doChat()" class="btn btn-default"><i class="fa fa-comments-o"></i></a> <a title="{{_('Start chat')}}" ng-click="doChat()" class="btn btn-default"><i class="fa fa-comments-o"></i></a>
</div> </div>
<div class="onlyaudio"><i class="fa fa-eye-close"></i></div> <div class="onlyaudio"><i class="fa fa-eye-close"></i></div>
<div class="overlaylogo"></div> <div class="overlayLogo"></div>
</div> </div>

2
static/translation/messages-ja.json

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save