build 1.0.0-beta1

Esse commit está contido em:
sualko
2014-10-29 14:13:39 +01:00
commit 8cd97ec3e1
23 arquivos alterados com 25725 adições e 768 exclusões
+1
Ver Arquivo
@@ -1 +1,2 @@
node_modules
*.zip
+59 -8
Ver Arquivo
@@ -1,6 +1,11 @@
/* global module:false */
module.exports = function(grunt) {
var dep = grunt.file.readJSON('dep.json');
var dep_files = dep.map(function(el) {
return el.file;
});
// Project configuration.
grunt.initConfig({
app: grunt.file.readJSON('package.json'),
@@ -47,10 +52,54 @@ module.exports = function(grunt) {
} ]
}
},
concat: {
dep: {
options: {
banner: '/*!' +
' * <%= app.name %> v<%= app.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
' * \n' +
' * This file concatenates all dependencies of <%= app.name %>.\n' +
' * \n' +
' * For the list of concatenated files and there licenses please see <%= app.homepage %> @github.\n' +
' */\n\n',
process: function(src, filepath) {
if (filepath === 'build/lib/otr/build/dep/crypto.js') {
src += ';';
}
var data = dep[dep_files.indexOf(filepath)];
return '/*!\n * Source: ' + filepath + ', license: ' + data.license + ', url: ' + data.url + ' */\n' + src;
}
},
src: dep_files,
dest: 'build/jsxc.dep.js'
},
jsxc: {
options: {
banner: '/*! This file is concatenated for the browser. */\n\n'
},
src: ['build/jsxc.lib.js', 'build/jsxc.lib.webrtc.js'],
dest: 'build/jsxc.js'
}
},
uglify: {
jsxc: {
options: {
mangle: false,
sourceMap: true,
preserveComments: 'some'
},
files: {
'build/jsxc.dep.min.js': ['build/jsxc.dep.js'],
'build/jsxc.min.js': ['build/jsxc.js']
}
}
},
search: {
console: {
files: {
src: ['*.js']
src: [ '*.js' ]
},
options: {
searchString: /console\.log\((?!'[<>]|msg)/g,
@@ -60,13 +109,13 @@ module.exports = function(grunt) {
},
changelog: {
files: {
src: ['CHANGELOG.md']
src: [ 'CHANGELOG.md' ]
},
options: {
searchString: "<%= app.version %>",
logFormat: 'console',
onComplete: function(m) {
if(m.numMatches === 0) {
if (m.numMatches === 0) {
grunt.fail.fatal("No entry in README.md for current version found.");
}
}
@@ -97,6 +146,8 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-banner');
grunt.loadNpmTasks('grunt-text-replace');
grunt.loadNpmTasks('grunt-search');
@@ -104,14 +155,14 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-shell');
// Default task.
grunt.registerTask('default', [ 'jshint', 'search', 'clean', 'copy', 'usebanner', 'replace' ]);
grunt.registerTask('default', [ 'jshint', 'search', 'clean', 'copy', 'usebanner', 'replace', 'concat', 'uglify', 'compress' ]);
// Create alpha/beta build
grunt.registerTask('pre', ['jshint', 'search:console', 'clean', 'copy', 'usebanner', 'replace', 'compress' ]);
grunt.registerTask('pre', [ 'jshint', 'search:console', 'clean', 'copy', 'usebanner', 'replace', 'concat', 'uglify', 'compress' ]);
// before commit
grunt.registerTask('commit', [ 'jshint', 'search:console' ]);
// prepare pre-commit hook
grunt.registerTask('hookmeup', ['shell:hooks']);
grunt.registerTask('hookmeup', [ 'shell:hooks' ]);
};
+2 -1
Ver Arquivo
@@ -1,4 +1,4 @@
/**
/*!
* <%= app.name %> v<%= app.version %> - <%= grunt.template.today("yyyy-mm-dd") %>
*
* Copyright (c) <%= grunt.template.today("yyyy") %> <%= app.author %> <br>
@@ -8,4 +8,5 @@
*
* @author <%= app.author %>
* @version <%= app.version %>
* @license MIT
*/
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 9.3 KiB

+105
Ver Arquivo
@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="Layer_1"
xml:space="preserve"
height="147.22015"
viewBox="0 0 192.89149 147.21771"
width="192.89587"
version="1.1"
y="0px"
x="0px"
enable-background="new 0 0 176.486 181.437"
inkscape:version="0.91pre2 r"
sodipodi:docname="XMPP_logo.svg"><metadata
id="metadata41"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs39" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="640"
inkscape:window-height="480"
id="namedview37"
showgrid="false"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10"
inkscape:zoom="1.3007054"
inkscape:cx="71.332001"
inkscape:cy="72.501142"
inkscape:window-x="75"
inkscape:window-y="34"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" /><linearGradient
id="SVGID_1_"
y2="0.00048828"
gradientUnits="userSpaceOnUse"
x2="-1807.2"
gradientTransform="translate(1925.9953,9.9988343)"
y1="125.86"
x1="-1807.2"><stop
stop-color="#1b3967"
offset=".011"
id="stop4" /><stop
stop-color="#13b5ea"
offset=".467"
id="stop6" /><stop
stop-color="#002b5c"
offset=".9945"
id="stop8" /></linearGradient><path
d="m 146.28531,24.187835 c 0.077,1.313 -1.786,0.968 -1.786,2.293 0,38.551 -44.720002,96.831035 -89.847002,108.190035 l 0,1.182 C 114.60931,130.34287 181.38231,69.052835 182.89231,10.002834 l -36.6,14.189001 z"
id="path10"
style="fill:url(#SVGID_1_)"
inkscape:connector-curvature="0" /><path
d="m 130.22531,27.958835 c 0.077,1.313 0.121,2.633 0.121,3.958 0,38.551 -30.700002,90.497035 -75.827002,101.860035 l 0,1.637 c 59.065002,-3.823 105.810002,-63.023035 105.810002,-109.200035 0,-2.375 -0.125,-4.729 -0.371,-7.056 l -29.73,8.796 z"
id="path12"
inkscape:connector-curvature="0"
style="fill:#e96d1f" /><linearGradient
id="SVGID_2_"
y2="1.279e-13"
gradientUnits="userSpaceOnUse"
x2="-1073.2"
gradientTransform="matrix(-1,0,0,1,-998.20465,9.9988343)"
y1="126.85"
x1="-1073.2"><stop
stop-color="#1b3967"
offset=".011"
id="stop15" /><stop
stop-color="#13b5ea"
offset=".467"
id="stop17" /><stop
stop-color="#002b5c"
offset=".9945"
id="stop19" /></linearGradient><path
d="m 46.594308,24.187835 c -0.077,1.313 1.787,0.968 1.787,2.293 0,38.551 46.558,97.366035 91.688002,108.730035 l 0,1.639 C 80.116308,131.32987 11.509308,69.049835 9.9993079,9.9998343 L 46.598308,24.188835 Z"
id="path21"
style="fill:url(#SVGID_2_)"
inkscape:connector-curvature="0" /><path
d="m 64.726308,28.930835 c -0.076,1.313 -0.12,2.63 -0.12,3.957 0,38.551 30.699,90.497035 75.827002,101.860035 l 0,1.639 C 81.389308,133.59687 34.623308,73.362835 34.623308,27.186835 c 0,-2.375 0.128,-4.729 0.371,-7.056 l 29.73,8.798 z"
id="path23"
inkscape:connector-curvature="0"
style="fill:#a0ce67" /><path
d="m 34.708308,19.581835 7.617,2.722 c -0.041,0.962 -0.066,2.254 -0.066,3.225 0,41.219 37.271,98.204035 87.272002,107.120035 3.245,1.088 7.538,2.077 10.932,2.931 l 0,1.638 C 75.209308,131.65787 29.363308,65.351835 34.703308,19.577835 Z"
id="path25"
inkscape:connector-curvature="0"
style="fill:#439639" /><path
d="m 160.33531,18.758835 -7.833,2.625 c 0.041,0.963 0.191,2.203 0.191,3.173 0,41.219 -37.272,98.205035 -87.274002,107.120035 -3.243,1.089 -7.538,2.077 -10.93,2.932 l 0,1.639 C 122.83331,127.58787 165.66931,64.528835 160.32931,18.757835 Z"
id="path27"
inkscape:connector-curvature="0"
style="fill:#d9541e" /></svg>

Depois

Largura:  |  Altura:  |  Tamanho: 4.5 KiB

+111 -3
Ver Arquivo
@@ -31,7 +31,7 @@ p.jsxc_center {
}
li.jsxc_sep {
border-bottom: 1px solid black;
border-top: 1px solid black;
}
/*############################
@@ -154,6 +154,8 @@ li .jsxc_name:hover {
overflow: hidden;
position: relative;
font-family: Arial;
background-size: cover;
background-position: center center;
}
.jsxc_avatar img {
@@ -165,6 +167,35 @@ li .jsxc_name:hover {
left: 0px;
}
.jsxc_spot {
display: inline-block;
width: 10px;
height: 10px;
background-color: #fff;
border-radius: 5px;
text-indent: -99999em;
margin-top: 3px;
margin-right: 5px;
border: 1px solid #000 !important;
line-height: 100%;
}
.jsxc_spot.jsxc_online, .jsxc_spot.jsxc_chat {
background-color: #00BFFF;
}
.jsxc_spot.jsxc_away, .jsxc_spot.jsxc_xa {
background-color: orange;
}
.jsxc_spot.jsxc_dnd {
background-color: red;
}
.jsxc_spot.jsxc_offline {
background-color: #A4A4A4;
}
/*############################
*########## Roster ##########
*############################*/
@@ -449,6 +480,27 @@ ul#jsxc_buddylist .jsxc_name {
padding-right: 30px;
}
ul#jsxc_buddylist li .jsxc_name:before {
content: ' ';
width: 0px;
height: 0px;
display: inline-block;
opacity: 0.0;
-webkit-transition: width 0.5s, opacity 0.5s;
transition: width 0.5s, opacity 0.5s;
}
ul#jsxc_buddylist li.jsxc_unreadMsg .jsxc_name:before {
width: 12px;
height: 12px;
border-radius: 6px;
background-color: orange;
margin-right: 3px;
opacity: 1.0;
-webkit-transition: width 0.5s, opacity 0.5s;
transition: width 0.5s, opacity 0.5s;
}
ul#jsxc_buddylist li.jsxc_expand {
height: 54px;
}
@@ -763,6 +815,7 @@ div.jsxc_window .jsxc_emoticons ul {
padding: 3px;
border-radius: 3px;
z-index: 200;
list-style-type: none;
}
div.jsxc_window .jsxc_emoticons ul:after {
@@ -783,12 +836,15 @@ div.jsxc_window .jsxc_emoticons li {
cursor: pointer;
}
div.jsxc_window .jsxc_emoticons img {
div.jsxc_window .jsxc_emoticons div {
cursor: pointer;
border-radius: 3px;
background-size: 30px 30px;
width: 30px;
height: 30px;
}
div.jsxc_window .jsxc_emoticons img:hover {
div.jsxc_window .jsxc_emoticons div:hover {
background-color: rgba(255, 255, 255, 0.8);
}
@@ -858,6 +914,7 @@ div.jsxc_chatmessage a {
div.jsxc_chatmessage img {
width: 19px;
height: 19px;
background-size: 19px 19px;
}
/** incoming message */
@@ -1131,3 +1188,54 @@ img.jsxc_vCard {
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a4a4a4',
endColorstr='#00282323', GradientType=0); /* IE6-9 */
}
.jsxc_emoticon {
width: 19px;
height: 19px;
background-size: 19px 19px !important;
border: 0px;
}
.jsxc_angel { background: url('img/emotions/angel.png') }
.jsxc_angry { background: url('img/emotions/angry.png') }
.jsxc_smile { background: url('img/emotions/smile.png') }
.jsxc_grin { background: url('img/emotions/grin.png') }
.jsxc_sad { background: url('img/emotions/sad.png') }
.jsxc_wink { background: url('img/emotions/wink.png') }
.jsxc_tonguesmile { background: url('img/emotions/tonguesmile.png') }
.jsxc_surprised { background: url('img/emotions/surprised.png') }
.jsxc_kiss { background: url('img/emotions/kiss.png') }
.jsxc_sunglassess { background: url('img/emotions/sunglassess.png') }
.jsxc_crysad { background: url('img/emotions/crysad.png') }
.jsxc_doubt { background: url('img/emotions/doubt.png') }
.jsxc_zip { background: url('img/emotions/zip.png') }
.jsxc_thumbsup { background: url('img/emotions/thumbsup.png') }
.jsxc_thumbsdown { background: url('img/emotions/thumbsdown.png') }
.jsxc_beer { background: url('img/emotions/beer.png') }
.jsxc_devil { background: url('img/emotions/devil.png') }
.jsxc_kissing { background: url('img/emotions/kissing.png') }
.jsxc_rose { background: url('img/emotions/rose.png') }
.jsxc_music { background: url('img/emotions/music.png') }
.jsxc_love { background: url('img/emotions/love.png') }
.jsxc_tired { background: url('img/emotions/tired.png') }
+16644
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+67
Ver Arquivo
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+6923
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+1262 -675
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+192 -53
Ver Arquivo
@@ -1,5 +1,5 @@
/**
* jsxc v1.0.0-alpha1 - 2014-09-08
/*!
* jsxc v1.0.0-beta1 - 2014-10-29
*
* Copyright (c) 2014 Klaus Herberth <klaus@jsxc.org> <br>
* Released under the MIT license
@@ -7,15 +7,16 @@
* Please see http://www.jsxc.org/
*
* @author Klaus Herberth <klaus@jsxc.org>
* @version 1.0.0-alpha1
* @version 1.0.0-beta1
* @license MIT
*/
/* jsxc, Strophe, SDPUtil, getUserMediaWithConstraints, setupRTC, jQuery */
/* global jsxc, Strophe, SDPUtil, getUserMediaWithConstraints, setupRTC, jQuery, MediaStreamTrack */
var RTC = null, RTCPeerconnection = null;
jsxc.gui.template.incomingCall = '<h3>%%Incoming_call%%</h3>\
<p>%%Do_you_want_to_accept_the_call_from%% {{cid_name}}?</p>\
<p>%%Do_you_want_to_accept_the_call_from%% {{bid_name}}?</p>\
<p class="jsxc_right">\
<a href="#" class="button jsxc_reject">%%Reject%%</a> <a href="#" class="button creation jsxc_accept">%%Accept%%</a>\
</p>';
@@ -23,10 +24,28 @@ jsxc.gui.template.incomingCall = '<h3>%%Incoming_call%%</h3>\
jsxc.gui.template.allowMediaAccess = '<p>%%Please_allow_access_to_microphone_and_camera%%</p>';
jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
<div class="jsxc_chatarea">\
<ul></ul>\
</div>\
<div class="jsxc_videoContainer">\
<video class="jsxc_localvideo" autoplay></video>\
<video class="jsxc_remotevideo" autoplay></video>\
<div class="jsxc_status"></div>\
<div class="bubblingG">\
<span id="bubblingG_1">\
</span>\
<span id="bubblingG_2">\
</span>\
<span id="bubblingG_3">\
</span>\
</div>\
<div class="jsxc_noRemoteVideo">\
<div>\
<div></div>\
<p>%%No_video_signal%%</p>\
<div></div>\
</div>\
</div>\
</div>\
<div class="jsxc_controlbar">\
<button type="button" class="jsxc_hangUp">%%hang_up%%</button>\
@@ -44,9 +63,9 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
<div class="jsxc_snapshotbar">\
<p>No pictures yet!</p>\
</div>\n\
<div class="jsxc_chatarea">\
<!--<div class="jsxc_chatarea">\
<ul></ul>\
</div>\
</div>-->\
<div class="jsxc_infobar"></div>\
</div>\
</div>';
@@ -76,7 +95,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
AUTO_ACCEPT: false,
/** required disco features */
reqVideoFeatures: [ 'urn:xmpp:jingle:apps:rtp:video', 'urn:xmpp:jingle:apps:rtp:audio', 'urn:xmpp:jingle:transports:ice-udp:1' ],
reqVideoFeatures: [ 'urn:xmpp:jingle:apps:rtp:video', 'urn:xmpp:jingle:apps:rtp:audio', 'urn:xmpp:jingle:transports:ice-udp:1', 'urn:xmpp:jingle:apps:dtls:0' ],
/** bare jid to current jid mapping */
chatJids: {},
@@ -110,6 +129,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
self.conn.jingle.pc_constraints = RTC.pc_constraints;
$(document).on('message.jsxc', $.proxy(self.onMessage, self));
$(document).on('presence.jsxc', $.proxy(self.onPresence, self));
$(document).on('mediaready.jingle', $.proxy(self.onMediaReady, self));
$(document).on('mediafailure.jingle', $.proxy(self.onMediaFailure, self));
@@ -126,6 +146,10 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
jsxc.error('[JINGLE]', error);
});
if (self.conn.disco) {
self.conn.disco.addFeature('urn:xmpp:jingle:apps:dtls:0');
}
if (self.conn.caps) {
$(document).on('caps.strophe', $.proxy(self.onCaps, self));
}
@@ -172,18 +196,47 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
});
},
/**
* Return list of video capable resources.
*
* @memberOf jsxc.webrtc
* @param bid
* @returns {Array}
*/
getCapableRes: function(bid) {
var self = jsxc.webrtc;
var res = jsxc.storage.getUserItem('res', bid) || [];
var available = [];
$.each(res, function(r) {
if (self.conn.caps.hasFeatureByJid(bid + '/' + r, self.reqVideoFeatures)) {
available.push(r);
}
});
return available;
},
/**
* Add "video" button to roster
*
* @private
* @memberOf jsxc.webrtc
* @param event
* @param cid cid of roster item
* @param data data wich belongs to cid
* @param bid bid of roster item
* @param data data wich belongs to bid
* @param el the roster item
*/
onAddRosterItem: function(event, cid, data, el) {
onAddRosterItem: function(event, bid, data, el) {
var self = jsxc.webrtc;
if (!self.conn) {
$(document).one('connectionReady.jsxc', function() {
self.onAddRosterItem(null, bid, data, el);
});
return;
}
var videoIcon = $('<div class="jsxc_video jsxc_disabled" title="' + jsxc.l.Start_video_call + '"></div>');
videoIcon.click(function() {
@@ -194,7 +247,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
el.find('.jsxc_options.jsxc_left').append(videoIcon);
el.on('extra.jsxc', function() {
self.updateIcon(cid);
self.updateIcon(bid);
});
},
@@ -209,6 +262,8 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
initWindow: function(event, win) {
var self = jsxc.webrtc;
jsxc.debug('webrtc.initWindow');
if (!self.conn) {
$(document).one('connectionReady.jsxc', function() {
self.initWindow(null, win);
@@ -219,38 +274,43 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
var div = $('<div>').addClass('jsxc_video');
win.find('.jsxc_transfer:eq(1)').after(div);
self.updateIcon(jsxc.jidToCid(win.data('jid')));
self.updateIcon(jsxc.jidToBid(win.data('jid')));
},
/**
* Enable or disable "video" icon and assign full jid.
*
* @memberOf jsxc.webrtc
* @param cid CSS conform jid
* @param bid CSS conform jid
*/
updateIcon: function(cid) {
updateIcon: function(bid) {
jsxc.debug('Update icon', bid);
var self = jsxc.webrtc;
var win = jsxc.gui.getWindow(cid);
var jid = win.data('jid') || jsxc.storage.getUserItem('buddy_' + cid).jid;
var win = jsxc.gui.window.get(bid);
var jid = win.data('jid') || jsxc.storage.getUserItem('buddy', bid).jid;
var el = win.find('.jsxc_video').add('#' + cid + ' .jsxc_video');
var el = win.find('.jsxc_video').add(jsxc.gui.roster.getItem(bid).find('.jsxc_video'));
if (Strophe.getResourceFromJid(jid) === null) {
var capableRes = self.getCapableRes(jid);
var targetRes = Strophe.getResourceFromJid(jid);
var res = jsxc.storage.getUserItem('buddy_' + cid).res;
if (Array.isArray(res) && res.length === 1) {
jid += '/' + res[0];
}
if (targetRes === null) {
$.each(jsxc.storage.getUserItem('buddy', bid).res, function(index, val) {
if (capableRes.indexOf(val) > -1) {
targetRes = val;
return false;
}
});
}
el.off('click');
if (self.conn.caps.hasFeatureByJid(jid, self.reqVideoFeatures)) {
if (capableRes.indexOf(targetRes) > -1) {
el.click(function() {
self.startCall(jid);
self.startCall(jid + '/' + targetRes);
});
el.removeClass('jsxc_disabled');
el.attr('title', jsxc.translate('%%Start video call%%'));
@@ -271,14 +331,32 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
*/
onMessage: function(e, from) {
var self = jsxc.webrtc;
var bJid = Strophe.getBareJidFromJid(from);
var bid = jsxc.jidToBid(from);
if (self.chatJids[bJid] !== from) {
self.updateIcon(jsxc.jidToCid(bJid));
self.chatJids[bJid] = from;
jsxc.debug('webrtc.onmessage', from);
if (self.chatJids[bid] !== from) {
self.updateIcon(bid);
self.chatJids[bid] = from;
}
},
/**
* Update icon on presence.
*
* @memberOf
* @param ev
* @param status
* @private
*/
onPresence: function(ev, jid) {
var self = jsxc.webrtc;
jsxc.debug('webrtc.onpresence', jid);
self.updateIcon(jsxc.jidToBid(jid));
},
/**
* Display status message to user.
*
@@ -337,7 +415,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
onCaps: function(event, jid) {
var self = jsxc.webrtc;
self.updateIcon(jsxc.jidToCid(jid));
self.updateIcon(jsxc.jidToBid(jid));
},
/**
@@ -368,6 +446,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
self.setStatus((stream.getVideoTracks().length > 0) ? 'Use local video device.' : 'No local video device.');
jsxc.debug('using video device "' + stream.getVideoTracks()[i].label + '"');
$('#jsxc_dialog .jsxc_localvideo').show();
}
$(document).one('cleanup.dialog.jsxc', $.proxy(self.hangUp, self));
@@ -380,8 +459,11 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
* @private
* @memberOf jsxc.webrtc
*/
onMediaFailure: function() {
onMediaFailure: function(ev, err) {
this.setStatus('media failure');
jsxc.gui.window.postMessage(jsxc.jidToBid(jsxc.webrtc.last_caller), 'sys', jsxc.translate('%%Media failure%%: ') + err.name);
jsxc.debug('media failure: ' + err.name);
},
/**
@@ -397,12 +479,12 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
var self = this;
var sess = this.conn.jingle.sessions[sid];
var jid = jsxc.jidToCid(sess.peerjid);
var bid = jsxc.jidToBid(sess.peerjid);
jsxc.gui.window.postMessage(jid, 'sys', jsxc.translate('%%Incoming call.%%'));
jsxc.gui.window.postMessage(bid, 'sys', jsxc.translate('%%Incoming call.%%'));
// display notification
jsxc.notification.notify(jsxc.translate('%%Incoming call%%'), jsxc.translate('%%from%% ' + jid));
jsxc.notification.notify(jsxc.translate('%%Incoming call%%'), jsxc.translate('%%from%% ' + bid));
// send signal to partner
sess.sendRinging();
@@ -430,7 +512,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
return;
}
var dialog = jsxc.gui.dialog.open(jsxc.gui.template.get('incomingCall', jsxc.jidToCid(jid)), {
var dialog = jsxc.gui.dialog.open(jsxc.gui.template.get('incomingCall', bid), {
noClose: true
});
@@ -462,7 +544,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
onCallTerminated: function(event, sid, reason, text) {
this.setStatus('call terminated ' + sid + (reason ? (': ' + reason + ' ' + text) : ''));
var cid = jsxc.jidToCid(jsxc.webrtc.last_caller);
var bid = jsxc.jidToBid(jsxc.webrtc.last_caller);
if (this.localStream) {
this.localStream.stop();
@@ -477,13 +559,15 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
this.localStream = null;
this.remoteStream = null;
$('#jsxc_windowList > ul').prepend($('#jsxc_dialog .jsxc_chatarea > ul > li').detach());
var win = $('#jsxc_dialog .jsxc_chatarea > ul > li');
$('#jsxc_windowList > ul').prepend(win.detach());
win.find('.slimScrollDiv').resizable('enable');
$(document).off('cleanup.dialog.jsxc');
$(document).off('error.jingle');
jsxc.gui.dialog.close();
jsxc.gui.window.postMessage(cid, 'sys', jsxc.translate('%%Call terminated%%' + (reason ? (': %%' + reason + '%%') : '') + '.'));
jsxc.gui.window.postMessage(bid, 'sys', jsxc.translate('%%Call terminated%%' + (reason ? (': %%' + reason + '%%') : '') + '.'));
},
/**
@@ -525,7 +609,9 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
this.setStatus(isAudioDevice ? 'Use remote audio device.' : 'No remote audio device');
if ($('.jsxc_remotevideo').length) {
RTC.attachMediaStream($('.jsxc_remotevideo'), stream);
RTC.attachMediaStream($('#jsxc_dialog .jsxc_remotevideo'), stream);
$('#jsxc_dialog .jsxc_' + (isVideoDevice ? 'remotevideo' : 'noRemoteVideo')).addClass('jsxc_deviceAvailable');
}
},
@@ -559,7 +645,11 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
jsxc.debug('iceCon state for ' + sid, iceCon);
jsxc.debug('sig state for ' + sid, sigState);
if (sigState === 'stable' && iceCon === 'connected') {
if (sigState === 'stable' && (iceCon === 'connected' || iceCon === 'completed')) {
$('#jsxc_dialog .jsxc_deviceAvailable').show();
$('#jsxc_dialog .bubblingG').hide();
var localSDP = sess.peerconnection.localDescription.sdp;
var remoteSDP = sess.peerconnection.remoteDescription.sdp;
@@ -588,6 +678,15 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
text += '</p>';
$('#jsxc_dialog .jsxc_infobar').html(text);
} else if (iceCon === 'failed') {
jsxc.gui.window.postMessage(jsxc.jidToBid(sess.peerjid), 'sys', jsxc.translate('%%ICE connection failure%%.'));
$(document).off('cleanup.dialog.jsxc');
sess.sendTerminate('failed-transport');
sess.terminate();
$(document).trigger('callterminated.jingle');
}
},
@@ -606,8 +705,9 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
*
* @memberOf jsxc.webrtc
* @param jid full jid
* @param um requested user media
*/
startCall: function(jid) {
startCall: function(jid, um) {
var self = this;
if (Strophe.getResourceFromJid(jid) === null) {
@@ -621,7 +721,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
'finish.mediaready.jsxc': function() {
self.setStatus('Initiate call');
jsxc.gui.window.postMessage(jsxc.jidToCid(jid), 'sys', jsxc.translate('%%Call started.%%'));
jsxc.gui.window.postMessage(jsxc.jidToBid(jid), 'sys', jsxc.translate('%%Call started.%%'));
$(document).one('error.jingle', function(e, sid, error) {
if (error.source !== 'offer') {
@@ -641,7 +741,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
}
});
this.reqUserMedia();
self.reqUserMedia(um);
},
/**
@@ -649,10 +749,10 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
*
* @memberOf jsxc.webrtc
*/
hangUp: function() {
hangUp: function(reason, text) {
$(document).off('cleanup.dialog.jsxc');
jsxc.webrtc.conn.jingle.terminate(null);
jsxc.webrtc.conn.jingle.terminate(null, reason, text);
$(document).trigger('callterminated.jingle');
},
@@ -661,18 +761,35 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
*
* @memberOf jsxc.webrtc
*/
reqUserMedia: function() {
reqUserMedia: function(um) {
if (this.localStream) {
$(document).trigger('mediaready.jingle', [ this.localStream ]);
return;
}
um = um || [ 'video', 'audio' ];
jsxc.gui.dialog.open(jsxc.gui.template.get('allowMediaAccess'), {
noClose: true
});
this.setStatus('please allow access to microphone and camera');
getUserMediaWithConstraints([ 'video', 'audio' ]);
if (typeof MediaStreamTrack !== 'undefined' && typeof MediaStreamTrack.getSources !== 'undefined') {
MediaStreamTrack.getSources(function(sourceInfo) {
var availableDevices = sourceInfo.map(function(el) {
return el.kind;
});
um = um.filter(function(el) {
return availableDevices.indexOf(el) !== -1;
});
getUserMediaWithConstraints(um);
});
} else {
getUserMediaWithConstraints(um);
}
},
/**
@@ -701,7 +818,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
try {
url = canvas.toDataURL('image/jpeg');
} catch (err) {
console.warn('Error', err);
jsxc.warn('Error', err);
return;
}
@@ -762,6 +879,8 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
if (self.remoteStream) {
RTC.attachMediaStream(rv, self.remoteStream);
$('#jsxc_dialog .jsxc_' + (self.remoteStream.getVideoTracks().length > 0 ? 'remotevideo' : 'noRemoteVideo')).addClass('jsxc_deviceAvailable');
}
var toggleMulti = function(elem, open) {
@@ -778,7 +897,13 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
}
};
var win = jsxc.gui.window.open(jsxc.jidToCid(jid));
var win = jsxc.gui.window.open(jsxc.jidToBid(jid));
win.find('.slimScrollDiv').resizable('disable');
win.find('.jsxc_textarea').slimScroll({
height: 413
});
win.find('.jsxc_emoticons').css('top', (413 + 6) + 'px');
$('#jsxc_dialog .jsxc_chatarea ul').append(win.detach());
@@ -796,7 +921,21 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
});
$('#jsxc_dialog .jsxc_showchat').click(function() {
toggleMulti($('#jsxc_dialog .jsxc_chatarea'));
var chatarea = $('#jsxc_dialog .jsxc_chatarea');
if (chatarea.is(':hidden')) {
chatarea.show();
$('#jsxc_dialog .jsxc_webrtc').width('900');
jsxc.gui.dialog.resize({
width: '920px'
});
} else {
chatarea.hide();
$('#jsxc_dialog .jsxc_webrtc').width('650');
jsxc.gui.dialog.resize({
width: '660px'
});
}
});
$('#jsxc_dialog .jsxc_info').click(function() {
@@ -863,7 +1002,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
Remote_IP: 'Remote IP',
Local_Fingerprint: 'Local fingerprint',
Remote_Fingerprint: 'Remote fingerprint',
Video_call_not_possible: 'Video call not possible',
Video_call_not_possible: 'Video call not possible. Your buddy does not support video calls.',
Start_video_call: 'Start video call'
});
@@ -884,7 +1023,7 @@ jsxc.gui.template.videoWindow = '<div class="jsxc_webrtc">\
Remote_IP: 'Remote IP',
Local_Fingerprint: 'Lokaler Fingerprint',
Remote_Fingerprint: 'Remote Fingerprint',
Video_call_not_possible: 'Videoanruf nicht verfügbar',
Video_call_not_possible: 'Videoanruf nicht verfügbar. Dein Gesprächspartner unterstützt keine Videotelefonie.',
Start_video_call: 'Starte Videoanruf'
});
+30
Ver Arquivo
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+224 -13
Ver Arquivo
@@ -1,17 +1,34 @@
.jsxc_webrtc {
width: 900px;
}
.jsxc_webrtc li .jsxc_name {
cursor: auto;
}
.jsxc_webrtc li .jsxc_name:hover {
color: #939393;
}
.jsxc_videoContainer {
width: 640px;
height: 480px;
position: relative;
background-color: #a4a4a4;
}
.jsxc_remotevideo {
.jsxc_videoContainer video, .jsxc_videoContainer .jsxc_noRemoteVideo {
display: none;
}
.jsxc_remotevideo, .jsxc_noRemoteVideo {
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
z-index: 9000;
background-color: grey;
background-color: #a4a4a4;
}
.jsxc_localvideo {
@@ -25,6 +42,49 @@
cursor: pointer;
}
.jsxc_noRemoteVideo p {
position: absolute;
bottom: 0px;
left:0px;
text-align: center;
color: #fff;
width: 100%;
z-index: 100;
}
.jsxc_noRemoteVideo > div {
width: 200px;
height: 200px;
overflow: hidden;
position: absolute;
top: 50%;
margin-top: -100px;
left: 50%;
margin-left: -100px;
}
.jsxc_noRemoteVideo > div > div {
background-color: #717171;
}
.jsxc_noRemoteVideo > div > div:first-child {
width: 50%;
height: 50%;
border-radius: 50%;
position: absolute;
left: 50%;
margin-left: -25%;
top: 10%;
}
.jsxc_noRemoteVideo > div > div:last-child {
position: absolute;
bottom: -50%;
width: 100%;
height: 100%;
border-radius: 50%;
}
.jsxc_webrtc .jsxc_status {
z-index: 9999;
border-radius: 20px;
@@ -40,6 +100,10 @@
text-align: center;
}
.jsxc_webrtc .slimScrollDiv {
opacity: 1 !important;
}
div:-moz-full-screen {
width: 100%;
height: 100%;
@@ -84,6 +148,14 @@ div:fullscreen.jsxc_localvideo {
border: 1px solid white;
}
.jsxc_controlbar {
width: 640px;
}
.jsxc_controlbar .jsxc_showchat {
float: right;
}
.jsxc_multi>div {
display: none;
}
@@ -115,32 +187,33 @@ div:fullscreen.jsxc_localvideo {
.jsxc_chatarea {
position: relative;
height: 270px;
display: none;
float:right;
height: 480px;
width: 250px;
background-color: #A4A4A4;
}
.jsxc_chatarea > ul {
position: absolute;
left: 50%;
top: 0px;
width: 250px;
height: 270px;
height: 480px;
list-style: none;
padding: 0px;
margin: 0px 0px 0px -100px;
}
.jsxc_chatarea .jsxc_settings {
display: none;
display: none !important;
}
.jsxc_chatarea .jsxc_close {
display: none;
display: none !important;
}
.jsxc_chatarea .jsxc_video {
display: none !important;
}
.jsxc_chatarea .jsxc_bar {
display: none;
}
.jsxc_chatarea .jsxc_window {
@@ -165,4 +238,142 @@ div.jsxc_video:not(.jsxc_disabled):hover, #jsxc_buddylist .jsxc_options .jsxc_vi
#jsxc_buddylist .jsxc_options .jsxc_video.jsxc_disabled {
opacity: 0.2;
}
}
.bubblingG {
text-align: center;
width:129px;
height:80px;
position: absolute;
top: 40%;
left: 50%;
margin-left: -64px;
}
.bubblingG span {
display: inline-block;
vertical-align: middle;
width: 16px;
height: 16px;
margin: 40px auto;
background: #000000;
-moz-border-radius: 81px;
-moz-animation: bubblingG 1.3s infinite alternate;
-webkit-border-radius: 81px;
-webkit-animation: bubblingG 1.3s infinite alternate;
-ms-border-radius: 81px;
-ms-animation: bubblingG 1.3s infinite alternate;
-o-border-radius: 81px;
-o-animation: bubblingG 1.3s infinite alternate;
border-radius: 81px;
animation: bubblingG 1.3s infinite alternate;
}
#bubblingG_1 {
-moz-animation-delay: 0s;
-webkit-animation-delay: 0s;
-ms-animation-delay: 0s;
-o-animation-delay: 0s;
animation-delay: 0s;
}
#bubblingG_2 {
-moz-animation-delay: 0.39s;
-webkit-animation-delay: 0.39s;
-ms-animation-delay: 0.39s;
-o-animation-delay: 0.39s;
animation-delay: 0.39s;
}
#bubblingG_3 {
-moz-animation-delay: 0.78s;
-webkit-animation-delay: 0.78s;
-ms-animation-delay: 0.78s;
-o-animation-delay: 0.78s;
animation-delay: 0.78s;
}
@-moz-keyframes bubblingG {
0% {
width: 16px;
height: 16px;
background-color:#000000;
-moz-transform: translateY(0);
}
100% {
width: 39px;
height: 39px;
background-color:#FFFFFF;
-moz-transform: translateY(-34px);
}
}
@-webkit-keyframes bubblingG {
0% {
width: 16px;
height: 16px;
background-color:#000000;
-webkit-transform: translateY(0);
}
100% {
width: 39px;
height: 39px;
background-color:#FFFFFF;
-webkit-transform: translateY(-34px);
}
}
@-ms-keyframes bubblingG {
0% {
width: 16px;
height: 16px;
background-color:#000000;
-ms-transform: translateY(0);
}
100% {
width: 39px;
height: 39px;
background-color:#FFFFFF;
-ms-transform: translateY(-34px);
}
}
@-o-keyframes bubblingG {
0% {
width: 16px;
height: 16px;
background-color:#000000;
-o-transform: translateY(0);
}
100% {
width: 39px;
height: 39px;
background-color:#FFFFFF;
-o-transform: translateY(-34px);
}
}
@keyframes bubblingG {
0% {
width: 16px;
height: 16px;
background-color:#000000;
transform: translateY(0);
}
100% {
width: 39px;
height: 39px;
background-color:#FFFFFF;
transform: translateY(-34px);
}
}
+2
Ver Arquivo
@@ -7,6 +7,8 @@
*
* Authors: - Michael Weibel <michael.weibel@gmail.com> - Klaus Herberth <klaus@jsxc.org>
* Copyright: - Michael Weibel <michael.weibel@gmail.com>
*
* @license MIT
*/
(function($) {
@@ -376,7 +376,7 @@ getUserMediaWithConstraints = function(um, resolution, bandwidth, fps) {
},
function (error) {
console.warn('Failed to get access to local media. Error ', error);
$(document).trigger('mediafailure.jingle');
$(document).trigger('mediafailure.jingle', [error]);
});
} catch (e) {
console.error('GUM failed: ', e);
+7 -6
Ver Arquivo
@@ -3,9 +3,10 @@
* Klaus Herberth, 2014
*/
// 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
/*! 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+/=";
@@ -84,7 +85,7 @@ var Base64 = (function () {
return obj;
})();
/*
/*!
* A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
* in FIPS PUB 180-1
* Version 2.1a Copyright Paul Johnston 2000 - 2002.
@@ -261,7 +262,7 @@ function binb2b64(binarray)
return str;
}
/*
/*!
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
@@ -469,7 +470,7 @@ var MD5 = (function () {
return obj;
})();
/*
/*!
This program is distributed under the terms of the MIT license.
Please see the LICENSE file for details.
+72
Ver Arquivo
@@ -0,0 +1,72 @@
[
{
"file": "build/lib/strophe.js",
"license": "multiple",
"url": "http://strophe.im/strophejs/"
},
{
"file": "build/lib/strophe.muc.js",
"license": "MIT",
"url": "https://github.com/strophe/strophejs-plugins"
},
{
"file": "build/lib/strophe.disco.js",
"license": "MIT",
"url": "https://github.com/strophe/strophejs-plugins"
},
{
"file": "build/lib/strophe.caps.js",
"license": "MIT",
"url": "https://github.com/strophe/strophejs-plugins"
},
{
"file": "build/lib/strophe.vcard.js",
"license": "MIT",
"url": "https://github.com/strophe/strophejs-plugins"
},
{
"file": "build/lib/strophe.jingle/strophe.jingle.js",
"license": "MIT",
"url": "https://github.com/ESTOS/strophe.jingle"
},
{
"file": "build/lib/strophe.jingle/strophe.jingle.session.js",
"license": "MIT",
"url": "https://github.com/ESTOS/strophe.jingle"
},
{
"file": "build/lib/strophe.jingle/strophe.jingle.sdp.js",
"license": "MIT",
"url": "https://github.com/ESTOS/strophe.jingle"
},
{
"file": "build/lib/strophe.jingle/strophe.jingle.adapter.js",
"license": "MIT",
"url": "https://github.com/ESTOS/strophe.jingle"
},
{
"file": "build/lib/otr/build/dep/salsa20.js",
"license": "GPLv3",
"url": "https://github.com/neoatlantis/node-salsa20"
},
{
"file": "build/lib/otr/build/dep/bigint.js",
"license": "public domain",
"url": "www.leemon.com"
},
{
"file": "build/lib/otr/build/dep/crypto.js",
"license": "code.google.com/p/crypto-js/wiki/license",
"url": "code.google.com/p/crypto-js"
},
{
"file": "build/lib/otr/build/dep/eventemitter.js",
"license": "MIT",
"url": "http://git.io/ee"
},
{
"file": "build/lib/otr/build/otr.js",
"license": "MPL v2.0",
"url": "https://arlolra.github.io/otr/"
}
]
+9 -1
Ver Arquivo
@@ -20,6 +20,9 @@
<script src="../lib/jquery.colorbox-min.js"></script>
<script src="../lib/jquery.slimscroll.js"></script>
<script src="../lib/jquery.fullscreen.js"></script>
<!-- Nightly files:
<script src="../lib/strophe.js"></script>
<script src="../lib/strophe.muc.js"></script>
<script src="../lib/strophe.disco.js"></script>
@@ -35,7 +38,12 @@
<script src="../lib/otr/build/dep/eventemitter.js"></script>
<script src="../lib/otr/build/otr.js"></script>
<script src="../jsxc.lib.js"></script>
<script src="../jsxc.lib.webrtc.js"></script>
<script src="../jsxc.lib.webrtc.js"></script>
-->
<!-- Build files: -->
<script src="../build/jsxc.dep.js"></script>
<script src="../build/jsxc.js"></script>
<script src="js/example.js"></script>
+2
Ver Arquivo
@@ -7,6 +7,8 @@
*
* Authors: - Michael Weibel <michael.weibel@gmail.com> - Klaus Herberth <klaus@jsxc.org>
* Copyright: - Michael Weibel <michael.weibel@gmail.com>
*
* @license MIT
*/
(function($) {
+7 -6
Ver Arquivo
@@ -3,9 +3,10 @@
* Klaus Herberth, 2014
*/
// 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
/*! 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+/=";
@@ -84,7 +85,7 @@ var Base64 = (function () {
return obj;
})();
/*
/*!
* A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
* in FIPS PUB 180-1
* Version 2.1a Copyright Paul Johnston 2000 - 2002.
@@ -261,7 +262,7 @@ function binb2b64(binarray)
return str;
}
/*
/*!
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
@@ -469,7 +470,7 @@ var MD5 = (function () {
return obj;
})();
/*
/*!
This program is distributed under the terms of the MIT license.
Please see the LICENSE file for details.
+3 -1
Ver Arquivo
@@ -1,6 +1,6 @@
{
"name": "jsxc",
"version": "1.0.0-alpha1",
"version": "1.0.0-beta1",
"description": "Real-time chat app",
"homepage": "http://www.jsxc.org/",
"bugs": "https://github.com/sualko/jsxc/issues",
@@ -16,8 +16,10 @@
"grunt-cli": "~0.1.13",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-compress": "^0.10.0",
"grunt-contrib-concat": "^0.5.0",
"grunt-contrib-copy": "~0.5.0",
"grunt-contrib-jshint": "~0.9.2",
"grunt-contrib-uglify": "^0.6.0",
"grunt-search": "^0.1.6",
"grunt-shell": "^1.1.1",
"grunt-text-replace": "~0.3.11"