Commit be524c1f authored by Dele Olajide's avatar Dele Olajide Committed by dele

Implemented OF-703 (Auto-creation of shared groups in userservice plugin)

Demo application for Rayo plugin

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13752 b35dd754-fafc-0310-a699-88a17e54d16e
parent b7ac7a29
/*global io MediaServices Phono*/
(function () {
// Utils and references
var root = this,
att = {};
// global utils
var _ = att.util = {
_uuidCounter: 0,
uuid: function () {
return Math.random().toString(16).substring(2) + (_._uuidCounter++).toString(16);
},
slice: Array.prototype.slice,
isFunc: function (obj) {
return Object.prototype.toString.call(obj) == '[object Function]';
},
extend: function (obj) {
this.slice.call(arguments, 1).forEach(function (source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
},
each: function (obj, func) {
if (!obj) return;
if (obj instanceof Array) {
obj.forEach(func);
} else {
for (var key in obj) {
func(key, obj[key]);
}
}
},
getQueryParam: function (name) {
// query string parser
var cleaned = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"),
regexS = "[\\?&]" + cleaned + "=([^&#]*)",
regex = new RegExp(regexS),
results = regex.exec(window.location.search);
return (results) ? decodeURIComponent(results[1].replace(/\+/g, " ")) : undefined;
},
// used to try to determine whether they're using the ericsson leif browser
// this is not an ideal way to check, but I'm not sure how to do it since
// leif if pretty much just stock chromium.
h2sSupport: function () {
return !!window.webkitPeerConnection00 && window.navigator.userAgent.indexOf('Chrome/24') !== -1;
}
};
var phoneNumber = {};
phoneNumber.stringify = function (text) {
// strip all non numbers
var cleaned = phoneNumber.parse(text),
len = cleaned.length,
countryCode = (cleaned.charAt(0) === '1'),
arr = cleaned.split(''),
diff;
// if it's long just return it unformatted
if (len > (countryCode ? 11 : 10)) return cleaned;
// if it's too short to tell
if (!countryCode && len < 4) return cleaned;
// remove country code if we have it
if (countryCode) arr.splice(0, 1);
// the rules are different enough when we have
// country codes so we just split it out
if (countryCode) {
if (len > 1) {
diff = 4 - len;
diff = (diff > 0) ? diff : 0;
arr.splice(0, 0, " (");
// back fill with spaces
arr.splice(4, 0, (new Array(diff + 1).join(' ') + ") "));
if (len > 7) {
arr.splice(8, 0, '-');
}
}
} else {
if (len > 7) {
arr.splice(0, 0, "(");
arr.splice(4, 0, ") ");
arr.splice(8, 0, "-");
} else if (len > 3) {
arr.splice(3, 0, "-");
}
}
// join it back when we're done with the CC if it's there
return (countryCode ? '1' : '') + arr.join('');
};
phoneNumber.parse = function (input) {
return String(input)
.toUpperCase()
.replace(/[A-Z]/g, function (l) {
return (l.charCodeAt(0) - 65) / 3 + 2 - ("SVYZ".indexOf(l) > -1) | 0;
})
.replace(/\D/g, '');
};
phoneNumber.getCallable = function (input, countryAbr) {
var country = countryAbr || 'us',
cleaned = phoneNumber.parse(input);
if (cleaned.length === 10) {
if (country == 'us') {
return '1' + cleaned;
}
} else if (country == 'us' && cleaned.length === 11 && cleaned.charAt(0) === '1') {
return cleaned;
} else {
return false;
}
};
att.phoneNumber = phoneNumber;
// attach to window or export with commonJS
if (typeof exports !== 'undefined') {
module.exports = att;
} else {
// make sure we've got an "att" global
root.ATT || (root.ATT = {});
_.extend(root.ATT, att);
}
}).call(this);
// This code was written by Tyler Akins and has been placed in the
// public domain. It would be nice if you left this header intact.
// Base64 code from Tyler Akins -- http://rumkin.com
var Base64 = (function () {
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var obj = {
/**
* Encodes a string in base64
* @param {String} input The string to encode in base64.
*/
encode: function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) +
keyStr.charAt(enc3) + keyStr.charAt(enc4);
} while (i < input.length);
return output;
},
/**
* Decodes a base64 string.
* @param {String} input The string to decode.
*/
decode: function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
do {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
} while (i < input.length);
return output;
}
};
return obj;
})();
This diff is collapsed.
body {
position: relative;
-webkit-transition: top 1s; }
body.candybarVisible {
top: 100px; }
#callStatus {
position: fixed;
top: -120px;
left: 0px;
-webkit-transition: background-color 1s;
-webkit-transition: top 1s;
width: 100%;
height: 80px;
padding: 10px;
z-index: 1000; }
#callStatus.visible {
top: 0px; }
#callStatus.havatar .callActions {
left: 100px; }
#callStatus.havatar .caller {
margin-left: 90px; }
#callStatus.incoming {
background-color: #41ade0;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #63cfff;
border-bottom: 2px solid #00699c;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.incoming .callTime {
display: none; }
#callStatus.incoming .callerName:before {
content: "Incoming: "; }
#callStatus.waiting {
background-color: #f47820;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #ff9a42;
border-bottom: 2px solid #b03400;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.waiting .spinner div {
background-color: white; }
#callStatus.calling {
background-color: #41ade0;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #63cfff;
border-bottom: 2px solid #00699c;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.calling .callTime {
display: none; }
#callStatus.calling .callerName:before {
content: "Calling: "; }
#callStatus.active {
background-color: #77a803;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #99ca25;
border-bottom: 2px solid #336400;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.active .callerName:before {
content: "On Call: "; }
#callStatus.inactive {
background-color: white;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid white;
border-bottom: 2px solid #bbbbbb;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.remote {
background-color: #74e0ff;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #96ffff;
border-bottom: 2px solid #309cbb;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.remote .callTime {
display: none; }
#callStatus.ending {
background-color: #bbbbbb;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #dddddd;
border-bottom: 2px solid #777777;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.ending .callerName:before {
content: "Call ending with: "; }
#callStatus .callActions {
position: absolute;
left: 10px;
top: 50px;
display: block;
width: 100%; }
#callStatus nav {
float: left; }
#callStatus button {
min-width: auto;
background-color: rgba(255, 255, 255, 0.3);
background-image: rgba(255, 255, 255, 0.5);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.5)), color-stop(1, rgba(0, 0, 0, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 1px solid rgba(255, 255, 255, 0.6);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
border-left: 1px solid rgba(255, 255, 255, 0.2);
border-right: 1px solid rgba(255, 255, 255, 0.2);
width: 100px;
margin-right: 10px;
font-size: 16px;
color: rgba(0, 0, 0, 0.75);
text-shadow: rgba(255, 255, 255, 0.5) 0 1px 0px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(255, 255, 255, 0.5),Direction=135,Strength=0);
float: left;
-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px;
-moz-box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px;
box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.3),Direction=135,Strength=3); }
#callStatus button:hover {
background-color: rgba(255, 255, 255, 0.4); }
#callStatus button:active {
box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 3px;
-moz-box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 3px;
-webkit-box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 3px;
padding-top: 11px;
padding-bottom: 9px;
border-bottom: 1px solid white;
border-top: 1px solid rgba(0, 0, 0, 0.2); }
#callStatus .callerAvatar {
float: left;
width: 65px;
height: 65px;
border: 5px solid #eeeeee;
margin-right: 10px; }
#callStatus .callerName, #callStatus .callTime {
font-weight: bold;
color: white;
text-shadow: rgba(0, 0, 0, 0.7) 0 1px 0px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.7),Direction=135,Strength=0);
line-height: 1; }
#callStatus .caller {
margin-top: 0px;
margin-right: 30px;
margin-left: 0px;
font-size: 20px;
padding-bottom: 0px;
border-bottom: 2px groove rgba(255, 255, 255, 0.4); }
#callStatus .callerName {
display: inline; }
#callStatus .callerNumber {
display: inline;
margin-left: 10px; }
#callStatus .callTime {
position: absolute;
top: 12px;
right: 40px;
font-size: 20px;
margin: 0; }
This diff is collapsed.
article, aside, details, figcaption, figure, footer, header, hgroup, nav, section {
display: block; }
audio, canvas, video {
display: inline-block;
*display: inline;
*zoom: 1; }
audio:not([controls]) {
display: none; }
[hidden] {
display: none; }
html {
font-size: 100%;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%; }
html, button, input, select, textarea {
font-family: sans-serif;
color: #222222; }
body {
margin: 0;
font-size: 1em;
line-height: 1.4; }
a {
color: #0000ee; }
a:visited {
color: #551a8b; }
a:hover {
color: #0066ee; }
a:focus {
outline: thin dotted; }
a:hover, a:active {
outline: 0; }
abbr[title] {
border-bottom: 1px dotted; }
b, strong {
font-weight: bold; }
blockquote {
margin: 1em 40px; }
dfn {
font-style: italic; }
hr {
display: block;
height: 1px;
border: 0;
border-top: 1px solid #cccccc;
margin: 1em 0;
padding: 0; }
ins {
background: #ffff99;
color: black;
text-decoration: none; }
mark {
background: yellow;
color: black;
font-style: italic;
font-weight: bold; }
pre, code, kbd, samp {
font-family: monospace, serif;
_font-family: "courier new", monospace;
font-size: 1em; }
pre {
white-space: pre-wrap;
word-wrap: break-word; }
q {
quotes: none; }
q:before, q:after {
content: none; }
small {
font-size: 85%; }
sub, sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline; }
sup {
top: -0.5em; }
sub {
bottom: -0.25em; }
ul, ol {
margin: 1em 0;
padding: 0 0 0 40px; }
dd {
margin: 0 0 0 40px; }
nav ul, nav ol {
list-style: none;
list-style-image: none;
margin: 0;
padding: 0; }
img {
border: 0;
-ms-interpolation-mode: bicubic;
vertical-align: middle; }
svg:not(:root) {
overflow: hidden; }
figure {
margin: 0; }
form {
margin: 0; }
fieldset {
border: 0;
margin: 0;
padding: 0; }
label {
cursor: pointer; }
legend {
border: 0;
*margin-left: -7px;
padding: 0;
white-space: normal; }
button, input, select, textarea {
font-size: 100%;
margin: 0;
vertical-align: baseline;
*vertical-align: middle; }
button, input {
line-height: normal; }
button, input[type="button"], input[type="reset"], input[type="submit"] {
cursor: pointer;
-webkit-appearance: button;
*overflow: visible; }
button[disabled], input[disabled] {
cursor: default; }
input[type="checkbox"], input[type="radio"] {
box-sizing: border-box;
padding: 0;
*width: 13px;
*height: 13px; }
input[type="search"] {
-webkit-appearance: textfield;
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
box-sizing: content-box; }
input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none; }
button::-moz-focus-inner, input::-moz-focus-inner {
border: 0;
padding: 0; }
textarea {
overflow: auto;
vertical-align: top;
resize: vertical; }
input:invalid, textarea:invalid {
background-color: #f0dddd; }
table {
border-collapse: collapse;
border-spacing: 0; }
td {
vertical-align: top; }
.clearfix:before, .clearfix:after {
content: "\0020";
display: block;
height: 0;
visibility: hidden; }
.clearfix:after {
clear: both; }
.clearfix {
zoom: 1; }
body {
font-family: sans-serif; }
.dialerwrapper {
width: 280px;
height: 350px;
background-color: #cccccc; }
.dialerwrapper .numberEntry {
width: 100%;
height: 15%;
text-align: center;
text-shadow: 1px 1px 0 #555555;
font-size: 2em;
color: white;
outline: 0;
border: 0;
background: #959595;
background: -moz-linear-gradient(top, #959595 50%, #767676 50%, #565656 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(50%, #959595), color-stop(50%, #767676), color-stop(100%, #565656));
background: -webkit-linear-gradient(top, #959595 50%, #767676 50%, #565656 100%);
background: -o-linear-gradient(top, #959595 50%, #767676 50%, #565656 100%);
background: -ms-linear-gradient(top, #959595 50%, #767676 50%, #565656 100%);
background: linear-gradient(top, #959595 50%, #767676 50%, #565656 100%);
padding: 0;
margin: 0;
border-top: 1px solid #777777; }
.dialerwrapper .numberEntry:focus {
outline: 0; }
.dialerwrapper #dialpad {
width: 100%;
height: 100%;
list-style: none;
margin: 0;
padding: 0; }
.dialerwrapper #dialpad li {
width: inherit;
height: 21.25%;
white-space: nowrap;
font-size: 0;
margin: 0;
padding: 0; }
.dialerwrapper #dialpad button {
display: inline-block;
vertical-align: top;
width: 33.3%;
height: 100%;
background: #eeeeee;
font-size: 20px;
text-align: center;
outline: 0;
border-top: 1px solid white;
border-right: 1px solid white;
border-bottom: 1px solid #c7c7c7;
border-left: 1px solid #c7c7c7;
border-radius: 0;
margin: 0;
padding: 0;
color: #555555;
vertical-align: middle; }
.dialerwrapper #dialpad button:hover {
background-color: #cecece; }
.dialerwrapper #dialpad button p {
width: 100%;
display: inline-table;
font-size: 1.2em;
font-weight: 700;
margin: 0; }
.dialerwrapper #dialpad button div {
text-transform: uppercase;
font-size: 0.6em; }
.dialerwrapper #actions nav {
position: relative; }
.dialerwrapper #actions a {
width: 33.3%; }
.close_dialer {
background-color: #555555;
height: 25px;
width: 100%;
display: block;
position: relative; }
.cancel {
position: absolute;
top: 4px;
right: 10px;
padding: 3px;
line-height: 9px;
display: block;
color: white;
border: 1px solid white;
background-color: #666666; }
.cancel:hover {
color: white;
background-color: #bb0000;
cursor: pointer; }
.call {
cursor: hand;
}
#screen {
overflow: hidden;
padding: 15px 15px 0 15px;
z-index: 99; }
#screen header {
background-color: #555555; }
#screen header, #screen footer {
height: 40px;
width: 100%;
padding: 10px 0;
display: block; }
#screen header nav, #screen footer nav {
width: 100%;
display: table;
margin: 0 auto;
border: 1px solid #222222;
border-radius: 10px; }
#screen header nav a, #screen footer nav a {
display: table-cell;
width: 50%;
color: #eeeeee;
font-weight: 900;
text-decoration: none;
text-align: center;
text-shadow: 1px 1px 0 #222222;
letter-spacing: 0.3px;
padding: 10px 0;
border-left: 1px solid #222222;
border-right: 1px solid #777777;
background: #888888;
background: -moz-linear-gradient(top, #888888 0%, #555555 100%) repeat-x, #888888;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #888888) repeat-x, color-stop(100%, #555555)), #888888;
background: -webkit-linear-gradient(top, #888888 0%, #555555 100%) repeat-x, #888888;
background: -o-linear-gradient(top, #888888 0%, #555555 100%) repeat-x, #888888;
background: -ms-linear-gradient(top, #888888 0%, #555555 100%) repeat-x, #888888;
background: linear-gradient(top, #888888 0%, #555555 100%) repeat-x, #888888;
-moz-transition: background 1s linear;
-webkit-transition: all 0.3s linear 0;
-moz-transition-property: all;
-moz-transition-duration: 1s;
-moz-transition-timing-function: linear;
-moz-transition-delay: linear; }
#screen header nav a:first-child, #screen footer nav a:first-child {
border-left: 0;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px; }
#screen header nav a:last-child, #screen footer nav a:last-child {
margin: 0;
border-right: 0;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px; }
#screen header nav a:hover, #screen footer nav a:hover {
background-position: 0 15px;
cursor: pointer; }
#login #screen {
padding: 0;
margin: 0 10px 10px 10px;
position: relative; }
#login #screen:after {
content: "";
width: 280px;
height: 1px;
display: block;
position: absolute;
left: 1px;
bottom: 0px;
background: #cccccc;
z-index: 200000000; }
#login footer {
display: none; }
#login .dialerwrapper {
background-color: white;
border-right: 1px solid #cccccc; }
#login .numberEntry {
border-left: 0px solid #dddddd;
margin-left: 1px;
margin-right: 2px;
padding-top: 3px;
border-top: 1px solid #dddddd;
background-image: #eeeeee;
background-image: -moz-linear-gradient(top, #eeeeee, #dddddd);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #eeeeee), color-stop(1, #dddddd));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
color: #555555;
text-shadow: white 0 1px 0px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=white,Direction=135,Strength=0); }
This diff is collapsed.
<html>
<head>
<link rel="stylesheet" href="bootstrap.min.css">
<link rel="stylesheet" href="dialpad.css">
<link rel="stylesheet" href="candybar.css">
<script src="jquery_1_4_2.js"></script>
<script src="md5.js"></script>
<script src="base64.js"></script>
<script src="strophe.js"></script>
<script src="strophe-openfire.js"></script>
<script src="rayo-plugin.js"></script>
<script src="candybar.js"></script>
<script src="att.phonenumber.js"></script>
<script src="dialpad.js"></script>
<script>
var avatar = 'unknown.jpg';
var callerId = "unknown";
var domain = "81.201.82.25";
var prefix = "sip:";
var handsetOffhook = false;
var ringtone;
window.dialer = new Dialpad({
onPress: function (key) {
console.log('a key was pressed', key);
if (window.candybar.call) window.candybar.call.digit(key);
},
onCallableNumber: function (number) {
console.log('we have a number that seems callable', number);
//makeCall(number);
},
onHide: function () {
console.log('removed it');
},
onCall: function (number) {
if (window.dialer.getCallLabel() == "Call") {
console.log('The call button was pressed', number);
makeCall(number);
} else if (window.dialer.getCallLabel() == "Hangup") {
window.candybar.call.hangup();
} else {
window.candybar.call.answer();
}
}
});
window.candybar = new CandyBar();
$(document).ready(function()
{
document.getElementById("dialpadDiv").insertBefore(window.dialer.render());
window.candybar.render();
window.candybar.call = null;
if (urlParam("prefix")) prefix = urlParam("prefix");
if (urlParam("domain")) domain = urlParam("domain");
if (domain == "81.201.82.25" && prefix == "sip:") prefix = "sip:883510";
if (prefix == "tel:") domain = "";
var iNum = urlParam("inum");
if (!iNum)
{
iNum = Math.random().toString(36).substr(2,9);
}
window.connection = new Openfire.Connection(window.location.protocol + '//' + window.location.host + '/http-bind/');
window.connection.resource = iNum;
window.connection.addHandler(handlePresence, null,"presence", null, null, null);
window.connection.connect(window.location.hostname, null, function (status)
{
//console.log("XMPPConnection.connect");
//console.log(status);
if (status === Strophe.Status.CONNECTED)
{
$("#status").html("Ready");
setPresence();
$(window).unload( function() {
onhook();
window.connection.disconnect();
});
setPresence();
getContacts();
offhook();
}
});
var destination = urlParam("destination");
if (destination)
{
makeCall(destination);
}
})
function setPresence(chat)
{
//console.log("setPresence");
if (window.connection)
{
var presence = $pres();
if (chat) presence.c('show').t(chat).up();
window.connection.send(presence);
}
}
function offhook()
{
console.log("offhook()");
if (window.connection)
{
window.connection.rayo.offhook(
{
codec: "OPUS",
stereo: "0",
onReady: function() {
console.log('Handset is off hook');
$("#status").html("Off Hook");
handsetOffhook = true;
},
onUnready: function() {
console.log('Handset is on hook');
$("#status").html("On Hook");
handsetOffhook = false;
},
onEnd: function() {
console.log('Handset is disconnected');
$("#status").html("On Hook");
handsetOffhook = false;
},
onError: function(e) {
console.error(e);
}
});
}
}
function toggleHook()
{
console.log("onhook()");
if (window.connection)
{
if (handsetOffhook)
window.connection.rayo.onhook();
else
offhook()
}
}
function handlePresence(presence)
{
//console.log("handlePresence");
//console.log(presence);
var from = $(presence).attr('from');
var iNum = Strophe.getResourceFromJid(from);
var xquery = presence.getElementsByTagName("x");
if (xquery.length == 0)
{
var type = $(presence).attr('type');
if (type == "unavailable")
{
} else {
//var status = $(presence).find('status').text();
}
}
return true;
};
function getContacts ()
{
//console.log("getContacts ");
};
function makeCall(destination)
{
console.log("makeCall " + destination);
var sipUri = prefix + destination + "@" + domain
if (prefix == "tel:") sipUri = prefix + destination
window.candybar.call = window.connection.rayo.dial(
{
from: 'unknown',
to: sipUri,
onEnd: function() {
//console.log('ended...............');
window.candybar.endGently();
window.candybar.call = null;
window.dialer.setCallLabel('Call');
},
onAnswer: function() {
//console.log('answered...............');
window.candybar.setState('active');
window.dialer.setCallLabel('Hangup');
stopTone();
},
onRing: function() {
//console.log('ringing...............');
window.candybar.setState('calling');
startTone("ringback-uk");
},
onError: function(e) {
//console.log('dial error ' + e);
window.candybar.endGently();
window.candybar.call = null;
}
});
window.candybar.setUser({
name: callerId,
number: destination,
picUrl: 'unknown.jpg'
});
}
function onIncomingCall(event)
{
console.log(' call from ' + event.call.initiator);
if (window.candybar.call == null) // ignore when user has active call
{
var destination = Strophe.getNodeFromJid(event.call.initiator);
window.candybar.setUser({
name: destination,
number: destination,
picUrl: 'http://placekitten.com/100/100'
});
window.candybar.call = event.call;
window.candybar.setState('incoming');
window.dialer.setCallLabel('Answer');
/*
window.candybar.call.bind(
{
onHangup: function(event)
{
window.candybar.endGently();
//window.candybar.call = null;
window.dialer.setCallLabel('Call');
},
onAnswer: function(event)
{
window.candybar.setState('active');
window.dialer.setCallLabel('Hangup');
},
onError: function(event)
{
console.log('call error ' + event.reason);
}
});
*/
}
}
function urlParam(name)
{
var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (!results) { return undefined; }
return decodeURIComponent(results[1]) || undefined;
}
function startTone(name)
{
ringtone = new Audio();
ringtone.loop = true;
ringtone.src = "ringtones/" + name + ".mp3";
ringtone.play();
}
function stopTone()
{
ringtone.pause();
ringtone = null;
}
</script>
</head>
<body>
<div id="dialpadDiv" style="position: absolute; width: 500px: height: 300px;"/>
<span id="status" onClick="toggleHook()">Loading...</span>
</body>
</html>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -46,13 +46,17 @@ public class Handset extends BaseVerb {
@NotNull(message=Handset.MISSING_STEREO)
public String stereo;
public Handset(String cryptoSuite, String localCrypto, String remoteCrypto, String codec, String stereo)
@NotNull(message=Handset.MISSING_MIXER)
public String mixer;
public Handset(String cryptoSuite, String localCrypto, String remoteCrypto, String codec, String stereo, String mixer)
{
this.cryptoSuite = cryptoSuite;
this.localCrypto = localCrypto;
this.remoteCrypto = remoteCrypto;
this.codec = codec;
this.stereo = stereo;
this.mixer = mixer;
}
@Override
......@@ -66,6 +70,7 @@ public class Handset extends BaseVerb {
.append("remoteCrypto",remoteCrypto)
.append("codec",codec)
.append("stereo",stereo)
.append("stereo",mixer)
.toString();
}
}
......@@ -2,4 +2,13 @@ package com.rayo.core.verb;
public class OffHookCommand extends AbstractVerbCommand {
private Handset handset;
public void setHandset(Handset handset) {
this.handset = handset;
}
public Handset getHandset() {
return this.handset;
}
}
......@@ -43,10 +43,7 @@ public class HandsetProvider extends BaseProvider {
@Override
protected Object processElement(Element element) throws Exception
{
if (element.getName().equals("handset")) {
return buildHandset(element);
} else if (ONHOOK_QNAME.equals(element.getQName())) {
if (ONHOOK_QNAME.equals(element.getQName())) {
return buildOnHookCommand(element);
} else if (OFFHOOK_QNAME.equals(element.getQName())) {
......@@ -69,34 +66,33 @@ public class HandsetProvider extends BaseProvider {
return complete;
}
private Object buildHandset(Element element) throws URISyntaxException
{
Handset handset = new Handset( element.attributeValue("cryptoSuite"),
element.attributeValue("localCrypto"),
element.attributeValue("remoteCrypto"),
private Object buildOffHookCommand(Element element) throws URISyntaxException {
Handset handset = new Handset( element.attributeValue("cryptosuite"),
element.attributeValue("localcrypto"),
element.attributeValue("remotecrypto"),
element.attributeValue("codec"),
element.attributeValue("stereo"));
return handset;
element.attributeValue("stereo"),
element.attributeValue("mixer"));
OffHookCommand command = new OffHookCommand();
command.setHandset(handset);
return command;
}
private Object buildOnHookCommand(Element element) throws URISyntaxException {
return new OnHookCommand();
}
private Object buildOffHookCommand(Element element) throws URISyntaxException {
return new OffHookCommand();
}
// Object -> XML
// ================================================================================
@Override
protected void generateDocument(Object object, Document document) throws Exception {
if (object instanceof Handset) {
createHandset((Handset) object, document);
} else if (object instanceof OnHookCommand) {
if (object instanceof OnHookCommand) {
createOnHookCommand((OnHookCommand) object, document);
} else if (object instanceof OffHookCommand) {
......@@ -107,24 +103,23 @@ public class HandsetProvider extends BaseProvider {
}
}
private void createHandset(Handset handset, Document document) throws Exception {
Element root = document.addElement(new QName("handset", NAMESPACE));
private void createOffHookCommand(OffHookCommand command, Document document) throws Exception {
Handset handset = command.getHandset();
Element root = document.addElement(new QName("offhook", NAMESPACE));
root.addAttribute("cryptoSuite", handset.cryptoSuite);
root.addAttribute("localCrypto", handset.localCrypto);
root.addAttribute("remoteCrypto", handset.cryptoSuite);
root.addAttribute("codec", handset.codec);
root.addAttribute("stereo", handset.stereo);
root.addAttribute("mixer", handset.mixer);
}
private void createOnHookCommand(OnHookCommand command, Document document) throws Exception {
document.addElement(new QName("onhook", NAMESPACE));
}
private void createOffHookCommand(OffHookCommand command, Document document) throws Exception {
document.addElement(new QName("offhook", NAMESPACE));
}
private void createHandsetCompleteEvent(SayCompleteEvent event, Document document) throws Exception {
addCompleteElement(document, event, COMPLETE_NAMESPACE);
}
......
......@@ -338,10 +338,10 @@ public abstract class CallHandler extends Thread {
}
if (otherCall != null) {
Logger.println("Call " + cp + " forwarding dtmf key "
+ dtmfKeys + " to " + otherCall);
Logger.println("Call " + cp + " forwarding dtmf key " + dtmfKeys + " to " + otherCall);
otherCall.getMemberSender().setDtmfKeyToSend(dtmfKeys);
} else {
getMemberSender().setDtmfKeyToSend(dtmfKeys);
}
} else {
if (Logger.logLevel >= Logger.LOG_MOREINFO) {
......@@ -642,6 +642,30 @@ public abstract class CallHandler extends Thread {
cancel(callsToCancel, reason, false);
}
public static void hangupOwner(String ownerId, String reason) {
Vector callsToCancel = new Vector();
synchronized(activeCalls) {
/*
* Make a list of all the calls we want to cancel, then cancel them.
* We have to cancel them while not synchronized or
* we could deadlock.
*/
for (int i = 0; i < activeCalls.size(); i++) {
CallHandler call = (CallHandler)activeCalls.elementAt(i);
CallParticipant cp = call.getCallParticipant();
if (cp.getCallOwner().equals(ownerId)) {
callsToCancel.add(call);
}
}
}
cancel(callsToCancel, reason, false);
}
public static void suspendBridge() {
cancel(activeCalls, "bridge suspended", true);
}
......
......@@ -684,7 +684,7 @@ public class ConferenceManager {
leave(member, true); // leave the temporary conference
member.reinitialize(newConferenceManager);
member.reinitialize(newConferenceManager, false);
newConferenceManager.joinConference(member); // join the new conference
}
......
......@@ -584,6 +584,11 @@ public class ConferenceMember implements TreatmentDoneListener,
}
public void reinitialize(ConferenceManager conferenceManager) {
reinitialize(conferenceManager, true);
}
public void reinitialize(ConferenceManager conferenceManager, boolean initialize) {
synchronized (conferenceManager) {
Logger.println("Call " + this + " Reinitializing");
......@@ -615,10 +620,11 @@ public class ConferenceMember implements TreatmentDoneListener,
* When the call is transferred to the actual conference,
* the conference parameters may be different.
*/
initialize(callHandler, memberSender.getSendAddress(),
memberSender.getMediaInfo().getPayload(),
memberReceiver.getMediaInfo().getPayload(),
(byte) memberReceiver.getTelephoneEventPayload(), rtcpAddress);
if (initialize)
{
initialize(callHandler, memberSender.getSendAddress(), memberSender.getMediaInfo().getPayload(), memberReceiver.getMediaInfo().getPayload(), (byte) memberReceiver.getTelephoneEventPayload(), rtcpAddress);
}
}
}
conferenceManager.joinDistributedConference(this);
......
......@@ -515,7 +515,7 @@ public class IncomingCallHandler extends CallHandler
return ((IncomingCallHandler)callHandler).transferCall(conferenceId);
}
private static ConferenceManager transferCall(CallHandler callHandler, String conferenceId) throws IOException
public static ConferenceManager transferCall(CallHandler callHandler, String conferenceId) throws IOException
{
/*
* Get current conference manager and member.
......
......@@ -53,6 +53,9 @@ import com.rayo.core.verb.*;
import org.voicebridge.*;
import com.sun.voip.server.*;
import com.sun.voip.*;
public class RayoPlugin implements Plugin, SessionEventListener {
......@@ -331,6 +334,8 @@ public class RayoPlugin implements Plugin, SessionEventListener {
public void anonymousSessionDestroyed(Session session)
{
Log.debug("RayoPlugin anonymousSessionDestroyed "+ session.getAddress().toString() + "\n" + ((ClientSession) session).getPresence().toXML());
CallHandler.hangupOwner(session.getAddress().toString(), "User has ended session");
}
public void resourceBound(Session session)
......@@ -347,5 +352,6 @@ public class RayoPlugin implements Plugin, SessionEventListener {
{
Log.debug("RayoPlugin sessionDestroyed "+ session.getAddress().toString() + "\n" + ((ClientSession) session).getPresence().toXML());
CallHandler.hangupOwner(session.getAddress().toString(), "User has ended session");
}
}
......@@ -297,6 +297,33 @@ public class Config implements MUCEventListener {
}
}
public static void recordCall(String username, String addressFrom, String addressTo, long datetime, int duration, String calltype) {
String sql = "INSERT INTO ofSipPhoneLog (username, addressFrom, addressTo, datetime, duration, calltype) values (?, ?, ?, ?, ?, ?)";
Connection con = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try {
con = DbConnectionManager.getConnection();
psmt = con.prepareStatement(sql);
psmt.setString(1, username);
psmt.setString(2, addressFrom);
psmt.setString(3, addressTo);
psmt.setLong(4, datetime);
psmt.setInt(5, duration);
psmt.setString(6, calltype);
psmt.executeUpdate();
} catch (SQLException e) {
Log.debug(e.getMessage(), e);
} finally {
DbConnectionManager.closeConnection(rs, psmt, con);
}
}
public ProxyCredentials getProxyCredentialsByUser(String username)
{
......
......@@ -249,6 +249,10 @@ public class RelayChannel {
return callHandler;
}
public void setCallHandler(OutgoingCallHandler callHandler) {
this.callHandler = callHandler;
}
public Handset getHandset() {
return handset;
}
......
......@@ -43,6 +43,12 @@
<h1>
User Service Plugin Changelog
</h1>
<p><b>1.4.1</b> -- September ??th, 2013</p>
<ul>
<li>Added ability to create new shared groups from list of groups when adding or updating users to roster</li>
</ul>
<p><b>1.4.0</b> -- Sep 13, 2013</p>
<ul>
<li>Requires Openfire 3.9.0.</li>
......
......@@ -5,7 +5,7 @@
<name>User Service</name>
<description>Allows administration of users via HTTP requests.</description>
<author>Justin Hunt</author>
<version>1.4.0</version>
<version>1.4.1</version>
<date>09/13/2013</date>
<minServerVersion>3.9.0</minServerVersion>
......
......@@ -119,7 +119,10 @@ The following parameters can be passed into the request:<p>
</tr>
<tr>
<td class="name">groups</td><td>Optional</td>
<td>List of groups where the user is a member. Values are comma delimited.</td>
<td>
List of groups where the user is a member. Values are comma delimited. When used with types "add" or "update", it adds the user to shared groups and auto-creates new groups.<br/>
When used with 'add_roster' and 'update_roster', it adds the user to roster groups provided the group name does not clash with an existing shared group.
</td>
</tr>
<tr>
<td class="name">item_jid</td><td>Required for 'add_roster', 'update_roster', 'delete_roster' operations.</td>
......@@ -146,6 +149,16 @@ http://example.com:9090/plugins/userService/userservice?type=add&secret=bigsecre
</form>
</ul>
The following example adds a user, adds two shared groups (if not existing) and adds the user to both groups.
<ul>
<form>
<textarea cols=65 rows=3 wrap=virtual>
http://example.com:9090/plugins/userService/userservice?type=add&secret=bigsecret&username=kafka&password=drowssap&name=franz&email=franz@kafka.com&groups=support,finance
</textarea>
</form>
</ul>
The following example deletes a user
<ul>
......@@ -197,6 +210,16 @@ http://example.com:9090/plugins/userService/userservice?type=add_roster&secret=b
</form>
</ul>
The following example adds new roster item with subscription 'both' for user 'kafka' and adds kafka to roster groups 'family' and 'friends'
<ul>
<form>
<textarea cols=65 rows=3 wrap=virtual>
http://example.com:9090/plugins/userService/userservice?type=add_roster&secret=bigsecret&username=kafka&item_jid=franz@example.com&name=franz&subscription=3&groups=family,friends
</textarea>
</form>
</ul>
The following example updates existing roster item to subscription 'none' for user 'kafka'
<ul>
......
......@@ -34,6 +34,7 @@ import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupManager;
import org.jivesoftware.openfire.group.GroupNotFoundException;
import org.jivesoftware.openfire.group.GroupAlreadyExistsException;
import org.jivesoftware.openfire.lockout.LockOutManager;
import org.jivesoftware.openfire.roster.Roster;
import org.jivesoftware.openfire.roster.RosterItem;
......@@ -91,19 +92,32 @@ public class UserServicePlugin implements Plugin, PropertyEventListener {
}
public void createUser(String username, String password, String name, String email, String groupNames)
throws UserAlreadyExistsException
throws UserAlreadyExistsException, GroupAlreadyExistsException, UserNotFoundException, GroupNotFoundException
{
userManager.createUser(username, password, name, email);
userManager.getUser(username);
if (groupNames != null) {
Collection<Group> groups = new ArrayList<Group>();
StringTokenizer tkn = new StringTokenizer(groupNames, ",");
while (tkn.hasMoreTokens()) {
while (tkn.hasMoreTokens())
{
String groupName = tkn.nextToken();
Group group = null;
try {
groups.add(GroupManager.getInstance().getGroup(tkn.nextToken()));
GroupManager.getInstance().getGroup(groupName);
} catch (GroupNotFoundException e) {
// Ignore this group
// Create this group ;
GroupManager.getInstance().createGroup(groupName);
}
group = GroupManager.getInstance().getGroup(groupName);
group.getProperties().put("sharedRoster.showInRoster", "onlyGroup");
group.getProperties().put("sharedRoster.displayName", groupName);
group.getProperties().put("sharedRoster.groupList", "");
groups.add(group);
}
for (Group group : groups) {
group.getMembers().add(server.createJID(username, null));
......@@ -143,7 +157,7 @@ public class UserServicePlugin implements Plugin, PropertyEventListener {
}
public void updateUser(String username, String password, String name, String email, String groupNames)
throws UserNotFoundException
throws UserNotFoundException, GroupAlreadyExistsException
{
User user = getUser(username);
if (password != null) user.setPassword(password);
......@@ -153,12 +167,23 @@ public class UserServicePlugin implements Plugin, PropertyEventListener {
if (groupNames != null) {
Collection<Group> newGroups = new ArrayList<Group>();
StringTokenizer tkn = new StringTokenizer(groupNames, ",");
while (tkn.hasMoreTokens()) {
while (tkn.hasMoreTokens())
{
String groupName = tkn.nextToken();
Group group = null;
try {
newGroups.add(GroupManager.getInstance().getGroup(tkn.nextToken()));
group = GroupManager.getInstance().getGroup(groupName);
} catch (GroupNotFoundException e) {
// Ignore this group
// Create this group ;
group = GroupManager.getInstance().createGroup(groupName);
group.getProperties().put("sharedRoster.showInRoster", "onlyGroup");
group.getProperties().put("sharedRoster.displayName", groupName);
group.getProperties().put("sharedRoster.groupList", "");
}
newGroups.add(group);
}
Collection<Group> existingGroups = GroupManager.getInstance().getGroups(user);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment