Browse Source

Added audio support, big message splitting, improved styles

master
Igor Zhukov 11 years ago
parent
commit
48b3403568
  1. 158
      app/css/app.css
  2. BIN
      app/img/icons/IconsetW.png
  3. BIN
      app/img/icons/IconsetW_1x.png
  4. BIN
      app/img/icons/Location_Active.png
  5. BIN
      app/img/icons/VideoIcon.png
  6. 16
      app/index.html
  7. 2
      app/js/app.js
  8. 6
      app/js/controllers.js
  9. 29
      app/js/directives.js
  10. 4
      app/js/filters.js
  11. 23
      app/js/lib/mtproto.js
  12. 131
      app/js/services.js
  13. 6
      app/js/util.js
  14. 41
      app/partials/dialog.html
  15. 2
      app/partials/im.html
  16. 47
      app/partials/message.html
  17. 990
      app/vendor/ui-bootstrap/ui-bootstrap-custom-0.10.0.js
  18. 8
      app/vendor/ui-bootstrap/ui-bootstrap-custom-0.10.0.min.js
  19. 332
      app/vendor/ui-bootstrap/ui-bootstrap-custom-0.7.0.js
  20. 1
      app/vendor/ui-bootstrap/ui-bootstrap-custom-0.7.0.min.js
  21. 1024
      app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.10.0.js
  22. 8
      app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.10.0.min.js
  23. 353
      app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.7.0.js
  24. 1
      app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.7.0.min.js

158
app/css/app.css

@ -400,8 +400,8 @@ fieldset[disabled] .btn-tg.active { @@ -400,8 +400,8 @@ fieldset[disabled] .btn-tg.active {
.im_dialogs_search_field {
font-size: 12px;
line-height: normal;
background: #F2F2F2 url(../img/icons/IconsetW.png) -6px -205px no-repeat;
background-size: 42px 280px;
background: #F2F2F2 url(../img/icons/IconsetW.png?1) -6px -205px no-repeat;
background-size: 42px 430px;
border: 1px solid #F2F2F2;
border-radius: 3px;
padding: 6px 6px 6px 30px;
@ -409,7 +409,7 @@ fieldset[disabled] .btn-tg.active { @@ -409,7 +409,7 @@ fieldset[disabled] .btn-tg.active {
margin: 0;
}
.is_1x .im_dialogs_search_field {
background-image: url(../img/icons/IconsetW_1x.png);
background-image: url(../img/icons/IconsetW_1x.png?1);
}
.im_dialogs_search_field:focus,
@ -425,12 +425,12 @@ fieldset[disabled] .btn-tg.active { @@ -425,12 +425,12 @@ fieldset[disabled] .btn-tg.active {
width: 13px;
height: 13px;
vertical-align: text-top;
background: url(../img/icons/IconsetW.png) -15px -192px no-repeat;
background-size: 42px 280px;
background: url(../img/icons/IconsetW.png?1) -15px -192px no-repeat;
background-size: 42px 430px;
opacity: 0.6;
}
.is_1x .im_dialogs_search_clear {
background-image: url(../img/icons/IconsetW_1x.png);
background-image: url(../img/icons/IconsetW_1x.png?1);
}
.im_dialogs_search_clear:hover {
opacity: 1;
@ -521,9 +521,37 @@ a.im_dialog:hover .im_dialog_message_text { @@ -521,9 +521,37 @@ a.im_dialog:hover .im_dialog_message_text {
color: #428bca;
background-color: #fff;
}
.im_dialog_unread {
background: #c1d6e5;
display: inline-block;
float: right;
width: 10px;
height: 10px;
border-radius: 7px;
overflow: hidden;
/*position: absolute;*/
margin: 8px 0 0;
}
a.im_dialog:hover .im_dialog_unread {
background: #a3c0d4;
}
.active .im_dialog_unread {
background-color: #a4c4dd;
}
.im_dialog_date {
font-size: 0.8em;
visibility: hidden;
color: #b3b3b3;
font-size: 0.85em;
}
a.im_dialog:hover .im_dialog_date {
color: #91a6ba;
}
.active .im_dialog_date,
.active a.im_dialog:hover .im_dialog_date {
color: #b8d1e4;
}
.im_dialog_service {
@ -777,18 +805,22 @@ div.im_message_video_thumb { @@ -777,18 +805,22 @@ div.im_message_video_thumb {
display: inline-block;
top: 50%;
left: 50%;
margin-left: -5px;
margin-top: -7px;
width: 10px;
height: 14px;
margin-left: -8px;
margin-top: -10px;
width: 15px;
height: 19px;
background: url(../img/icons/Location_Active.png?1) 0 0 no-repeat;
background-size: 10px 14px;
background: url(../img/icons/IconsetW.png?1) -14px -389px no-repeat;
background-size: 42px 430px;
}
.is_1x .icon-geo-point {
background-image: url(../img/icons/IconsetW_1x.png?1);
}
.im_message_document,
.im_message_audio,
.im_message_upload_file {
margin-top: 3px;
border-radius: 3px;
@ -797,20 +829,24 @@ div.im_message_video_thumb { @@ -797,20 +829,24 @@ div.im_message_video_thumb {
width: 340px;
}
.icon-document {
.icon-document,
.icon-photo,
.icon-video {
display: block;
float: left;
width: 38px;
height: 38px;
vertical-align: text-top;
background: #F2F2F2 url(../img/icons/IconsetW.png) -2px -229px no-repeat;
background-size: 42px 280px;
background: #F2F2F2 url(../img/icons/IconsetW.png?1) -2px -229px no-repeat;
background-size: 42px 430px;
border-radius: 3px;
margin-right: 10px;
}
.is_1x .icon-document {
background-image: url(../img/icons/IconsetW_1x.png);
.is_1x .icon-document,
.is_1x .icon-photo,
.is_1x .icon-video {
background-image: url(../img/icons/IconsetW_1x.png?1);
}
.im_message_document_info {
@ -840,6 +876,53 @@ div.im_message_video_thumb { @@ -840,6 +876,53 @@ div.im_message_video_thumb {
padding-left: 2px;
}
.icon-audio {
display: block;
float: left;
width: 38px;
height: 38px;
vertical-align: text-top;
background: #F2F2F2 url(../img/icons/IconsetW.png?1) -2px -277px no-repeat;
background-size: 42px 430px;
border-radius: 3px;
margin-right: 10px;
}
.is_1x .icon-audio {
background-image: url(../img/icons/IconsetW_1x.png?1);
}
.im_message_audio_info {
float: left;
width: 292px;
}
.im_message_audio_name_wrap {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 290px;
padding: 0 0 1px;
}
.im_message_audio_name {
color: #222;
display: inline-block;
font-weight: bold;
max-width: 200px;
overflow: hidden;
vertical-align: text-top;
white-space: nowrap;
text-overflow: ellipsis;
}
.im_message_audio_duration,
.im_message_audio_size {
color: #999;
padding-left: 2px;
}
.im_message_audio_info audio {
height: 38px;
line-height: 38px;
}
.im_message_upload_progress_wrap,
.im_message_download_progress_wrap {
margin-top: 5px;
@ -906,9 +989,6 @@ div.im_message_video_thumb { @@ -906,9 +989,6 @@ div.im_message_video_thumb {
margin-left: -27px;
margin-top: 14px;
opacity: 0;
-webkit-transition: opacity ease-in-out 0.15s;
transition: opacity ease-in-out 0.15s;
}
.icon-message-status-unread {
opacity: 1.0;
@ -916,9 +996,13 @@ div.im_message_video_thumb { @@ -916,9 +996,13 @@ div.im_message_video_thumb {
.icon-message-status-pending {
opacity: 0.5;
}
/*.icon-message-status-done {
opacity: 0;
}*/
.icon-message-status-error {
background: #da564d;
opacity: 0.85;
}
.icon-message-status-error:hover {
opacity: 1;
}
.icon-message-status-tick {
/*display: inline-block;*/
@ -1036,12 +1120,12 @@ textarea.im_message_field { @@ -1036,12 +1120,12 @@ textarea.im_message_field {
width: 19px;
height: 23px;
vertical-align: text-top;
background: url(../img/icons/IconsetW.png) -12px -68px no-repeat;
background-size: 42px 280px;
background: url(../img/icons/IconsetW.png?1) -12px -68px no-repeat;
background-size: 42px 430px;
opacity: 0.8;
}
.is_1x .icon-paperclip {
background-image: url(../img/icons/IconsetW_1x.png);
background-image: url(../img/icons/IconsetW_1x.png?1);
}
.im_attach:hover .icon-paperclip {
opacity: 1;
@ -1065,12 +1149,12 @@ textarea.im_message_field { @@ -1065,12 +1149,12 @@ textarea.im_message_field {
width: 23px;
height: 23px;
vertical-align: text-top;
background: url(../img/icons/IconsetW.png) -10px -4px no-repeat;
background-size: 42px 280px;
background: url(../img/icons/IconsetW.png?1) -10px -4px no-repeat;
background-size: 42px 430px;
opacity: 0.8;
}
.is_1x .icon-emoji {
background-image: url(../img/icons/IconsetW_1x.png);
background-image: url(../img/icons/IconsetW_1x.png?1);
}
.im_emoji_btn:hover .icon-emoji {
opacity: 1;
@ -1113,12 +1197,12 @@ textarea.im_message_field { @@ -1113,12 +1197,12 @@ textarea.im_message_field {
width: 25px;
height: 21px;
vertical-align: text-top;
background: url(../img/icons/IconsetW.png) -9px -132px no-repeat;
background-size: 42px 280px;
background: url(../img/icons/IconsetW.png?1) -9px -132px no-repeat;
background-size: 42px 430px;
opacity: 0.8;
}
.is_1x .icon-camera {
background-image: url(../img/icons/IconsetW_1x.png);
background-image: url(../img/icons/IconsetW_1x.png?1);
}
.im_media_attach:hover .icon-camera {
opacity: 1;
@ -1360,14 +1444,14 @@ img.img_fullsize { @@ -1360,14 +1444,14 @@ img.img_fullsize {
}
.emoji-menu-tail {
background: url(../img/icons/IconsetW.png) -14px -268px no-repeat;
background-size: 42px 280px;
background: url(../img/icons/IconsetW.png?1) -14px -268px no-repeat;
background-size: 42px 430px;
width: 14px;
height: 7px;
margin: 0 83px;
}
.is_1x .emoji-menu-tail {
background-image: url(../img/icons/IconsetW_1x.png);
background-image: url(../img/icons/IconsetW_1x.png?1);
}

BIN
app/img/icons/IconsetW.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
app/img/icons/IconsetW_1x.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
app/img/icons/Location_Active.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

BIN
app/img/icons/VideoIcon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

16
app/index.html

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
<link rel="stylesheet" href="vendor/angular/angular-csp.css"/>
<link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.css"/>
<link rel="stylesheet" href="vendor/jquery.nanoscroller/nanoscroller.css"/>
<link rel="stylesheet" href="css/app.css?20"/>
<link rel="stylesheet" href="css/app.css?21"/>
<link rel="icon" href="favicon.ico" type="image/x-icon" />
<meta property="og:title" content="Webogram">
@ -40,7 +40,7 @@ @@ -40,7 +40,7 @@
<script type="text/javascript" src="vendor/angular/angular-route.js?1"></script>
<script type="text/javascript" src="vendor/angular/angular-animate.js?1"></script>
<script type="text/javascript" src="vendor/angular/angular-sanitize.js?1"></script>
<script type="text/javascript" src="vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.7.0.js?3"></script>
<script type="text/javascript" src="vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.10.0.js"></script>
<script type="text/javascript" src="vendor/jsbn/jsbn_combined.js"></script>
<script type="text/javascript" src="vendor/cryptoJS/crypto.js?1"></script>
@ -48,14 +48,14 @@ @@ -48,14 +48,14 @@
<script type="text/javascript" src="js/lib/config.js"></script>
<script type="text/javascript" src="js/lib/mtproto.js?17"></script>
<script type="text/javascript" src="js/lib/mtproto.js?18"></script>
<script type="text/javascript" src="js/util.js"></script>
<script type="text/javascript" src="js/app.js?14"></script>
<script type="text/javascript" src="js/services.js?17"></script>
<script type="text/javascript" src="js/controllers.js?25"></script>
<script type="text/javascript" src="js/util.js?1"></script>
<script type="text/javascript" src="js/app.js?15"></script>
<script type="text/javascript" src="js/services.js?18"></script>
<script type="text/javascript" src="js/controllers.js?26"></script>
<script type="text/javascript" src="js/filters.js?3"></script>
<script type="text/javascript" src="js/directives.js?16"></script>
<script type="text/javascript" src="js/directives.js?17"></script>
</body>
</html>

2
app/js/app.js

@ -57,7 +57,7 @@ config(['$locationProvider', '$routeProvider', '$compileProvider', function($loc @@ -57,7 +57,7 @@ config(['$locationProvider', '$routeProvider', '$compileProvider', function($loc
// $locationProvider.html5Mode(true);
$routeProvider.when('/', {templateUrl: 'partials/welcome.html?3', controller: 'AppWelcomeController'});
$routeProvider.when('/login', {templateUrl: 'partials/login.html?4', controller: 'AppLoginController'});
$routeProvider.when('/im', {templateUrl: 'partials/im.html?11', controller: 'AppIMController', reloadOnSearch: false});
$routeProvider.when('/im', {templateUrl: 'partials/im.html?12', controller: 'AppIMController', reloadOnSearch: false});
$routeProvider.otherwise({redirectTo: '/'});
}]);

6
app/js/controllers.js

@ -497,7 +497,11 @@ angular.module('myApp.controllers', []) @@ -497,7 +497,11 @@ angular.module('myApp.controllers', [])
return all;
});
AppMessagesManager.sendText($scope.curDialog.peerID, text);
do {
AppMessagesManager.sendText($scope.curDialog.peerID, text.substr(0, 4096));
text = text.substr(4096);
} while (text.length);
resetDraft();
$scope.$broadcast('ui_message_send');
});

29
app/js/directives.js

@ -16,7 +16,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -16,7 +16,7 @@ angular.module('myApp.directives', ['myApp.filters'])
restrict: 'AE',
scope: true,
translude: false,
templateUrl: 'partials/dialog.html?4'
templateUrl: 'partials/dialog.html?5'
};
})
@ -25,7 +25,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -25,7 +25,7 @@ angular.module('myApp.directives', ['myApp.filters'])
restrict: 'AE',
scope: true,
translude: false,
templateUrl: 'partials/message.html?6'
templateUrl: 'partials/message.html?7'
};
})
@ -340,7 +340,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -340,7 +340,7 @@ angular.module('myApp.directives', ['myApp.filters'])
var lastTyping = 0;
$(editorElement).on('keyup', function (e) {
var now = +new Date();
var now = tsNow();
if (now - lastTyping < 5000) {
return;
}
@ -690,7 +690,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -690,7 +690,7 @@ angular.module('myApp.directives', ['myApp.filters'])
function link (scope, element, attrs) {
var promise = $interval(function () {
var time = +new Date(),
var time = tsNow(),
cnt = 3;
if (time % 1000 <= 200) {
@ -708,3 +708,24 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -708,3 +708,24 @@ angular.module('myApp.directives', ['myApp.filters'])
});
}
})
.directive('myAudioAutoplay', function() {
return {
link: link,
scope: {
audio: '='
}
};
function link (scope, element, attrs) {
scope.$watch('audio.autoplay', function (autoplay) {
if (autoplay) {
element.autoplay = true;
element[0].play();
} else {
element.autoplay = false;
}
});
}
})

4
app/js/filters.js

@ -61,7 +61,7 @@ angular.module('myApp.filters', []) @@ -61,7 +61,7 @@ angular.module('myApp.filters', [])
}
var ticks = timestamp * 1000,
diff = Math.abs(+new Date() - ticks),
diff = Math.abs(tsNow() - ticks),
format = 'HH:mm';
if (diff > 518400000) { // 6 days
@ -142,7 +142,7 @@ angular.module('myApp.filters', []) @@ -142,7 +142,7 @@ angular.module('myApp.filters', [])
.filter('relativeTime', ['$filter', function($filter) {
return function (timestamp) {
var ticks = timestamp * 1000,
diff = Math.abs(+new Date() - ticks);
diff = Math.abs(tsNow() - ticks);
if (diff < 60000) {
return 'just now';

23
app/js/lib/mtproto.js

@ -1011,7 +1011,7 @@ factory('MtpMessageIdGenerator', function (AppConfigManager) { @@ -1011,7 +1011,7 @@ factory('MtpMessageIdGenerator', function (AppConfigManager) {
});
function generateMessageID () {
var timeTicks = 1 * (new Date()),
var timeTicks = tsNow(),
timeSec = Math.floor(timeTicks / 1000) + timeOffset,
timeMSec = timeTicks % 1000,
random = nextRandomInt(0xFFFF);
@ -1031,7 +1031,7 @@ factory('MtpMessageIdGenerator', function (AppConfigManager) { @@ -1031,7 +1031,7 @@ factory('MtpMessageIdGenerator', function (AppConfigManager) {
};
function applyServerTime (serverTime, localTime) {
var newTimeOffset = serverTime - Math.floor((localTime || +new Date()) / 1000),
var newTimeOffset = serverTime - Math.floor((localTime || tsNow()) / 1000),
changed = Math.abs(timeOffset - newTimeOffset) > 10;
AppConfigManager.set({server_time_offset: newTimeOffset});
@ -1224,7 +1224,7 @@ factory('MtpAuthorizer', function (MtpDcConfigurator, MtpRsaKeysManager, MtpSecu @@ -1224,7 +1224,7 @@ factory('MtpAuthorizer', function (MtpDcConfigurator, MtpRsaKeysManager, MtpSecu
};
function mtpDecryptServerDhDataAnswer (auth, encryptedAnswer) {
auth.localTime = +new Date();
auth.localTime = tsNow();
auth.tmpAesKey = sha1Hash(auth.newNonce.concat(auth.serverNonce)).concat(sha1Hash(auth.serverNonce.concat(auth.newNonce)).slice(0, 12));
auth.tmpAesIv = sha1Hash(auth.serverNonce.concat(auth.newNonce)).slice(12).concat(sha1Hash([].concat(auth.newNonce, auth.newNonce)), auth.newNonce.slice(0, 4));
@ -1667,8 +1667,8 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato @@ -1667,8 +1667,8 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
MtpNetworker.prototype.checkLongPoll = function(force) {
var isClean = this.cleanupSent();
// console.log('Check lp', this.longPollPending, (new Date().getTime()));
if (this.longPollPending && (new Date().getTime()) < this.longPollPending) {
// console.log('Check lp', this.longPollPending, tsNow());
if (this.longPollPending && tsNow() < this.longPollPending) {
return false;
}
var self = this;
@ -1683,8 +1683,8 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato @@ -1683,8 +1683,8 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
MtpNetworker.prototype.sendLongPoll = function() {
var maxWait = 25000;
this.longPollPending = (new Date().getTime()) + maxWait;
// console.log('Set lp', this.longPollPending, (new Date().getTime()));
this.longPollPending = tsNow() + maxWait;
// console.log('Set lp', this.longPollPending, tsNow());
this.wrapMtpCall('http_wait', {max_delay: 0, wait_after: 0, max_wait: maxWait}, {noResponse: true}).
then((function () {
@ -1707,7 +1707,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato @@ -1707,7 +1707,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
};
MtpNetworker.prototype.pushResend = function(messageID, delay) {
var value = delay ? (new Date()).getTime() + delay : 0;
var value = delay ? tsNow() + delay : 0;
var sentMessage = this.sentMessages[messageID];
if (sentMessage.container) {
for (var i = 0; i < sentMessage.inner.length; i++) {
@ -1766,7 +1766,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato @@ -1766,7 +1766,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
var messages = [],
message,
messagesByteLen = 0,
currentTime = (new Date()).getTime(),
currentTime = tsNow(),
self = this;
angular.forEach(this.pendingMessages, function (value, messageID) {
@ -1970,7 +1970,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato @@ -1970,7 +1970,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
};
MtpNetworker.prototype.sheduleRequest = function (delay) {
var nextReq = new Date() + delay;
var nextReq = tsNow() + delay;
if (delay && this.nextReq && this.nextReq <= nextReq) {
return false;
@ -2465,6 +2465,9 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) { @@ -2465,6 +2465,9 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
case 'inputDocumentFileLocation':
return 'doc' + location.id;
case 'inputAudioFileLocation':
return 'audio' + location.id;
}
return location.volume_id + '_' + location.local_id + '_' + location.secret + '.jpg';
};

131
app/js/services.js

@ -503,7 +503,7 @@ angular.module('myApp.services', []) @@ -503,7 +503,7 @@ angular.module('myApp.services', [])
}
})
.service('AppMessagesManager', function ($q, $rootScope, $filter, $sanitize, $location, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, SearchIndexManager) {
.service('AppMessagesManager', function ($q, $rootScope, $filter, $sanitize, $location, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppAudioManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, SearchIndexManager) {
var messagesStorage = {};
var messagesForHistory = {};
@ -776,6 +776,9 @@ angular.module('myApp.services', []) @@ -776,6 +776,9 @@ angular.module('myApp.services', [])
if (apiMessage.media && apiMessage.media._ == 'messageMediaDocument') {
AppDocsManager.saveDoc(apiMessage.media.document);
}
if (apiMessage.media && apiMessage.media._ == 'messageMediaAudio') {
AppAudioManager.saveAudio(apiMessage.media.audio);
}
if (apiMessage.action && apiMessage.action._ == 'messageActionChatEditPhoto') {
AppPhotosManager.savePhoto(apiMessage.action.photo);
}
@ -802,14 +805,30 @@ angular.module('myApp.services', []) @@ -802,14 +805,30 @@ angular.module('myApp.services', [])
to_id: AppPeersManager.getOutputPeer(peerID),
out: true,
unread: true,
date: (+new Date()) / 1000,
date: tsNow() / 1000,
message: text,
media: {_: 'messageMediaEmpty'},
random_id: randomIDS,
pending: true
};
var toggleError = function (on) {
var historyMessage = messagesForHistory[messageID];
if (on) {
message.error = true;
if (historyMessage) {
historyMessage.error = true;
}
} else {
delete message.error;
if (historyMessage) {
delete historyMessage.error;
}
}
}
message.send = function () {
toggleError(false);
MtpApiManager.invokeApi('messages.sendMessage', {
peer: inputPeer,
message: text,
@ -830,6 +849,8 @@ angular.module('myApp.services', []) @@ -830,6 +849,8 @@ angular.module('myApp.services', [])
pts: result.pts
});
}
}, function (error) {
toggleError(true);
});
};
@ -860,10 +881,14 @@ angular.module('myApp.services', []) @@ -860,10 +881,14 @@ angular.module('myApp.services', [])
attachType = 'photo';
} else if (file.type.substr(0, 6) == 'video/') {
attachType = 'video';
} else if (file.type == 'audio/mpeg' || file.type == 'audio/mp3') {
attachType = 'audio';
} else {
attachType = 'doc';
}
console.log(11, options.isMedia, file.type, attachType);
if (historyStorage === undefined) {
historyStorage = historiesStorage[peerID] = {count: null, history: [], pending: []};
}
@ -884,7 +909,7 @@ angular.module('myApp.services', []) @@ -884,7 +909,7 @@ angular.module('myApp.services', [])
to_id: AppPeersManager.getOutputPeer(peerID),
out: true,
unread: true,
date: (+new Date()) / 1000,
date: tsNow() / 1000,
message: '',
media: media,
random_id: randomIDS,
@ -903,6 +928,10 @@ angular.module('myApp.services', []) @@ -903,6 +928,10 @@ angular.module('myApp.services', [])
inputMedia = {_: 'inputMediaUploadedVideo', file: inputFile, duration: 0, w: 0, h: 0};
break;
case 'audio':
inputMedia = {_: 'inputMediaUploadedAudio', file: inputFile, duration: 0};
break;
case 'doc':
default:
inputMedia = {_: 'inputMediaUploadedDocument', file: inputFile, file_name: file.name, mime_type: file.type};
@ -930,8 +959,12 @@ angular.module('myApp.services', []) @@ -930,8 +959,12 @@ angular.module('myApp.services', [])
});
}
}, function (error) {
toggleError(true);
});
}, null, function (progress) {
}, function (error) {
toggleError(true);
}, function (progress) {
// console.log('upload progress', progress);
var historyMessage = messagesForHistory[messageID],
percent = Math.max(1, Math.floor(100 * progress.done / progress.total));
@ -950,9 +983,7 @@ angular.module('myApp.services', []) @@ -950,9 +983,7 @@ angular.module('myApp.services', [])
historyStorage.pending.unshift(messageID);
$rootScope.$broadcast('history_append', {peerID: peerID, messageID: messageID, my: true});
// setTimeout(function () {
message.send();
// }, 5000);
message.send();
});
pendingByRandomID[randomIDS] = [peerID, messageID];
@ -982,6 +1013,7 @@ angular.module('myApp.services', []) @@ -982,6 +1013,7 @@ angular.module('myApp.services', [])
if (message = messagesStorage[tempID]) {
delete message.pending;
delete message.error;
delete message.random_id;
delete message.send;
}
@ -989,6 +1021,7 @@ angular.module('myApp.services', []) @@ -989,6 +1021,7 @@ angular.module('myApp.services', [])
if (historyMessage = messagesForHistory[tempID]) {
messagesForHistory[finalMessage.id] = angular.extend(historyMessage, wrapForHistory(finalMessage.id));
delete historyMessage.pending;
delete historyMessage.error;
delete historyMessage.random_id;
delete historyMessage.send;
}
@ -1070,6 +1103,10 @@ angular.module('myApp.services', []) @@ -1070,6 +1103,10 @@ angular.module('myApp.services', [])
case 'messageMediaDocument':
message.media.document = AppDocsManager.wrapForHistory(message.media.document.id);
break;
case 'messageMediaAudio':
message.media.audio = AppAudioManager.wrapForHistory(message.media.audio.id);
break;
}
if (message.media.user_id) {
@ -1510,7 +1547,7 @@ angular.module('myApp.services', []) @@ -1510,7 +1547,7 @@ angular.module('myApp.services', [])
}
})
.service('AppDocsManager', function ($rootScope, $modal, $window, $timeout, MtpApiFileManager, AppUsersManager) {
.service('AppDocsManager', function ($rootScope, $modal, $window, $timeout, MtpApiFileManager) {
var docs = {};
var docsForHistory = {};
@ -1624,6 +1661,66 @@ angular.module('myApp.services', []) @@ -1624,6 +1661,66 @@ angular.module('myApp.services', [])
}
})
.service('AppAudioManager', function ($rootScope, $modal, $window, $timeout, $sce, MtpApiFileManager) {
var audios = {};
var audiosForHistory = {};
function saveAudio (apiAudio) {
audios[apiAudio.id] = apiAudio;
};
function wrapForHistory (audioID) {
if (audiosForHistory[audioID] !== undefined) {
return audiosForHistory[audioID];
}
var audio = angular.copy(audios[audioID]);
return audiosForHistory[audioID] = audio;
}
function openAudio (audioID, accessHash) {
var audio = audios[audioID],
historyAudio = audiosForHistory[audioID] || audio || {},
inputFileLocation = {
_: 'inputAudioFileLocation',
id: audioID,
access_hash: accessHash || audio.access_hash
};
historyAudio.progress = {enabled: true, percent: 1, total: audio.size};
function updateDownloadProgress (progress) {
console.log('dl progress', progress);
historyAudio.progress.done = progress.done;
historyAudio.progress.percent = Math.max(1, Math.floor(100 * progress.done / progress.total));
$rootScope.$broadcast('history_update');
}
MtpApiFileManager.downloadFile(audio.dc_id, inputFileLocation, audio.size).then(function (url) {
delete historyAudio.progress;
historyAudio.url = $sce.trustAsResourceUrl(url);
historyAudio.autoplay = true;
$timeout(function () {
console.log('disable autoplay');
delete historyAudio.autoplay;
$rootScope.$broadcast('history_update');
}, 1000);
}, function (e) {
console.log('document download failed', e);
historyDoc.progress.enabled = false;
}, updateDownloadProgress);
}
$rootScope.openAudio = openAudio;
return {
saveAudio: saveAudio,
wrapForHistory: wrapForHistory,
openAudio: openAudio
}
})
.service('ExternalResourcesManager', function ($q, $http) {
var urlPromises = {};
@ -2042,7 +2139,7 @@ angular.module('myApp.services', []) @@ -2042,7 +2139,7 @@ angular.module('myApp.services', [])
}
function sendUpdateStatusReq(offline) {
var date = (1 * new Date());
var date = tsNow();
if (offline && !lastOnlineUpdated ||
!offline && (date - lastOnlineUpdated) < 50000) {
return;
@ -2094,7 +2191,7 @@ angular.module('myApp.services', []) @@ -2094,7 +2191,7 @@ angular.module('myApp.services', [])
titleBackup = document.title;
titlePromise = $interval(function () {
var time = +new Date();
var time = tsNow();
if (!notificationsCount || time % 2000 > 1000) {
document.title = titleBackup;
$('link[rel="icon"]').replaceWith(faviconBackupEl);
@ -2114,7 +2211,8 @@ angular.module('myApp.services', []) @@ -2114,7 +2211,8 @@ angular.module('myApp.services', [])
notify: notify,
cancel: notificationCancel,
clear: notificationsClear,
getPeerSettings: getPeerSettings
getPeerSettings: getPeerSettings,
getPeerMuted: getPeerMuted
};
function getPeerSettings (peerID) {
@ -2127,10 +2225,13 @@ angular.module('myApp.services', []) @@ -2127,10 +2225,13 @@ angular.module('myApp.services', [])
_: 'inputNotifyPeer',
peer: AppPeersManager.getInputPeerByID(peerID)
}
}).then(function (peerNotifySettings) {
// console.log('got settings', peerID, peerNotifySettings);
});
}
function getPeerMuted (peerID) {
return getPeerSettings(peerID).then(function (peerNotifySettings) {
return peerNotifySettings._ == 'peerNotifySettings' &&
peerNotifySettings.mute_until * 1000 > (+new Date());
peerNotifySettings.mute_until * 1000 > tsNow();
});
}
@ -2196,7 +2297,7 @@ angular.module('myApp.services', []) @@ -2196,7 +2297,7 @@ angular.module('myApp.services', [])
notification.onclose = function () {
delete notificationsShown[key];
// lastClosed.push(+new Date());
// lastClosed.push(tsNow());
notificationsClear();
};

6
app/js/util.js

@ -52,4 +52,8 @@ function onCtrlEnter (textarea, cb) { @@ -52,4 +52,8 @@ function onCtrlEnter (textarea, cb) {
function onContentLoaded (cb) {
setTimeout(cb, 0);
};
};
function tsNow () {
return +new Date();
}

41
app/partials/dialog.html

@ -9,6 +9,10 @@ @@ -9,6 +9,10 @@
ng-show="dialogMessage.unreadCount > 0"
ng-bind="dialogMessage.unreadCount"
></span>
<i
class="im_dialog_unread"
ng-show="dialogMessage.out &amp;&amp; dialogMessage.unread"
></i>
</div>
<div class="im_dialog_photo pull-left">
@ -41,36 +45,19 @@ @@ -41,36 +45,19 @@
</span>
<span class="im_dialog_message_media" ng-if="dialogMessage.media &amp;&amp; dialogMessage.media._ != 'messageMediaEmpty'" ng-switch="dialogMessage.media._">
<span ng-switch-when="messageMediaPhoto">
<span class="glyphicon glyphicon-camera"></span> Photo
</span>
<span ng-switch-when="messageMediaVideo">
<span class="glyphicon glyphicon-facetime-video"></span> Video
</span>
<span ng-switch-when="messageMediaDocument">
<span class="glyphicon glyphicon-document"></span> Document
</span>
<span ng-switch-when="messageMediaGeo">
<span class="glyphicon glyphicon-map-marker"></span> Location
</span>
<span ng-switch-when="messageMediaContact">
<span class="glyphicon glyphicon-user"></span> Contact
</span>
<span ng-switch-when="messageMediaPhoto">Photo</span>
<span ng-switch-when="messageMediaVideo">Video</span>
<span ng-switch-when="messageMediaDocument">Document</span>
<span ng-switch-when="messageMediaAudio">Audio</span>
<span ng-switch-when="messageMediaGeo">Location</span>
<span ng-switch-when="messageMediaContact">Contact</span>
</span>
<span class="im_dialog_message_service" ng-if="dialogMessage._ == 'messageService'" ng-switch="dialogMessage.action._">
<span ng-switch-when="messageActionChatCreate">
created the group
</span>
<span ng-switch-when="messageActionChatEditTitle">
changed group name
</span>
<span ng-switch-when="messageActionChatEditPhoto">
changed group photo
</span>
<span ng-switch-when="messageActionChatDeletePhoto">
removed group photo
</span>
<span ng-switch-when="messageActionChatCreate"> created the group </span>
<span ng-switch-when="messageActionChatEditTitle">changed group name</span>
<span ng-switch-when="messageActionChatEditPhoto">changed group photo</span>
<span ng-switch-when="messageActionChatDeletePhoto">removed group photo</span>
<span ng-switch-when="messageActionChatAddUser">
<span ng-if="dialogMessage.from_id != dialogMessage.action.user_id">

2
app/partials/im.html

@ -120,7 +120,7 @@ @@ -120,7 +120,7 @@
</div>
<div class="im_media_attach pull-right">
<input type="file" class="im_media_attach_input" size="28" multiple="true" accept="image/x-png, image/png, image/gif, image/jpeg, video/*" />
<input type="file" class="im_media_attach_input" size="28" multiple="true" accept="image/x-png, image/png, image/gif, image/jpeg, video/*, audio/mpeg, audio/mp3" />
<i class="icon icon-camera"></i>
</div>

47
app/partials/message.html

@ -55,7 +55,10 @@ @@ -55,7 +55,10 @@
</div>
<div class="im_content_message_wrap" ng-if="historyMessage._ != 'messageService'">
<i class="icon-message-status" ng-class="{'icon-message-status-unread': historyMessage.unread, 'icon-message-status-pending': historyMessage.pending}"></i>
<a ng-click="historyMessage.send()" ng-if="historyMessage.error">
<i class="icon-message-status icon-message-status-error" tooltip="Try again"></i>
</a>
<i class="icon-message-status" ng-class="{'icon-message-status-unread': historyMessage.unread, 'icon-message-status-pending': historyMessage.pending}" ng-if="!historyMessage.error"></i>
<a ng-click="openUser(historyMessage.from_id)" class="im_message_from_photo pull-left">
<img
@ -137,6 +140,46 @@ @@ -137,6 +140,46 @@
</div>
</div>
<div ng-switch-when="messageMediaAudio" class="im_message_document">
<a href="" ng-click="openAudio(historyMessage.media.audio.id)" ng-if="!historyMessage.media.audio.progress.enabled &amp;&amp; !historyMessage.media.audio.url">
<i class="icon icon-audio"></i>
</a>
<i class="icon icon-audio" ng-if="historyMessage.media.audio.progress.enabled || historyMessage.media.audio.url"></i>
<div class="im_message_audio_info">
<div class="im_message_audio_name_wrap" ng-if="!historyMessage.media.audio.url">
<span class="im_message_audio_name">
Voice message
</span>
<span class="im_message_audio_duration" ng-if="!historyMessage.media.audio.progress.enabled">
{{historyMessage.media.audio.duration | duration}}
</span>
<span class="im_message_audio_size" ng-if="historyMessage.media.audio.progress.enabled">
{{historyMessage.media.audio.progress | formatSizeProgress}}
</span>
</div>
<div class="im_message_audio_actions" ng-if="!historyMessage.media.audio.progress.enabled &amp;&amp; !historyMessage.media.audio.url">
<a href="" ng-click="openAudio(historyMessage.media.audio.id)">Play</a>
</div>
<div class="im_message_download_progress_wrap" ng-if="historyMessage.media.audio.progress.enabled">
<div class="progress tg_down_progress">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{historyMessage.media.audio.progress.percent}}" aria-valuemin="0" aria-valuemax="100" style="width: {{historyMessage.media.audio.progress.percent}}%">
<span class="sr-only">
{{historyMessage.media.audio.progress.percent}}% Complete (success)
</span>
</div>
</div>
</div>
<div class="im_message_audio_player_wrap" ng-if="historyMessage.media.audio.url">
<audio my-audio-autoplay audio="historyMessage.media.audio" controls="controls">
<source ng-src="{{historyMessage.media.audio.url}}" type="audio/mpeg" />
<embed hidden="true" autostart="true" loop="false" src="{{historyMessage.media.audio.url}}" />
</audio>
</div>
</div>
</div>
<a ng-switch-when="messageMediaGeo" my-map-point point="historyMessage.media.geo" class="im_message_geopoint">
<i class="icon icon-geo-point"></i>
</a>
@ -158,7 +201,7 @@ @@ -158,7 +201,7 @@
</div>
<div ng-switch-when="messageMediaPending" class="im_message_upload_file im_message_upload_{{historyMessage.media.type}}">
<i class="icon icon-document"></i>
<i class="icon icon-{{historyMessage.media.type}}"></i>
<div class="im_message_document_info">
<div class="im_message_document_name_wrap">
<span class="im_message_document_name">

990
app/vendor/ui-bootstrap/ui-bootstrap-custom-0.10.0.js vendored

@ -0,0 +1,990 @@ @@ -0,0 +1,990 @@
/*
* angular-ui-bootstrap
* http://angular-ui.github.io/bootstrap/
* Version: 0.10.0 - 2014-01-28
* License: MIT
*/
angular.module("ui.bootstrap", ["ui.bootstrap.dropdownToggle","ui.bootstrap.transition","ui.bootstrap.modal","ui.bootstrap.position","ui.bootstrap.bindHtml","ui.bootstrap.tooltip"]);
/*
* dropdownToggle - Provides dropdown menu functionality in place of bootstrap js
* @restrict class or attribute
* @example:
<li class="dropdown">
<a class="dropdown-toggle">My Dropdown Menu</a>
<ul class="dropdown-menu">
<li ng-repeat="choice in dropChoices">
<a ng-href="{{choice.href}}">{{choice.text}}</a>
</li>
</ul>
</li>
*/
angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle', ['$document', '$location', function ($document, $location) {
var openElement = null,
closeMenu = angular.noop;
return {
restrict: 'CA',
link: function(scope, element, attrs) {
scope.$watch('$location.path', function() { closeMenu(); });
element.parent().bind('click', function() { closeMenu(); });
element.bind('click', function (event) {
var elementWasOpen = (element === openElement);
event.preventDefault();
event.stopPropagation();
if (!!openElement) {
closeMenu();
}
if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
element.parent().addClass('open');
openElement = element;
closeMenu = function (event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
$document.unbind('click', closeMenu);
element.parent().removeClass('open');
closeMenu = angular.noop;
openElement = null;
};
$document.bind('click', closeMenu);
}
});
}
};
}]);
angular.module('ui.bootstrap.transition', [])
/**
* $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
* @param {DOMElement} element The DOMElement that will be animated.
* @param {string|object|function} trigger The thing that will cause the transition to start:
* - As a string, it represents the css class to be added to the element.
* - As an object, it represents a hash of style attributes to be applied to the element.
* - As a function, it represents a function to be called that will cause the transition to occur.
* @return {Promise} A promise that is resolved when the transition finishes.
*/
.factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
var $transition = function(element, trigger, options) {
options = options || {};
var deferred = $q.defer();
var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
var transitionEndHandler = function(event) {
$rootScope.$apply(function() {
element.unbind(endEventName, transitionEndHandler);
deferred.resolve(element);
});
};
if (endEventName) {
element.bind(endEventName, transitionEndHandler);
}
// Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
$timeout(function() {
if ( angular.isString(trigger) ) {
element.addClass(trigger);
} else if ( angular.isFunction(trigger) ) {
trigger(element);
} else if ( angular.isObject(trigger) ) {
element.css(trigger);
}
//If browser does not support transitions, instantly resolve
if ( !endEventName ) {
deferred.resolve(element);
}
});
// Add our custom cancel function to the promise that is returned
// We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
// i.e. it will therefore never raise a transitionEnd event for that transition
deferred.promise.cancel = function() {
if ( endEventName ) {
element.unbind(endEventName, transitionEndHandler);
}
deferred.reject('Transition cancelled');
};
return deferred.promise;
};
// Work out the name of the transitionEnd event
var transElement = document.createElement('trans');
var transitionEndEventNames = {
'WebkitTransition': 'webkitTransitionEnd',
'MozTransition': 'transitionend',
'OTransition': 'oTransitionEnd',
'transition': 'transitionend'
};
var animationEndEventNames = {
'WebkitTransition': 'webkitAnimationEnd',
'MozTransition': 'animationend',
'OTransition': 'oAnimationEnd',
'transition': 'animationend'
};
function findEndEventName(endEventNames) {
for (var name in endEventNames){
if (transElement.style[name] !== undefined) {
return endEventNames[name];
}
}
}
$transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
$transition.animationEndEventName = findEndEventName(animationEndEventNames);
return $transition;
}]);
angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
/**
* A helper, internal data structure that acts as a map but also allows getting / removing
* elements in the LIFO order
*/
.factory('$$stackedMap', function () {
return {
createNew: function () {
var stack = [];
return {
add: function (key, value) {
stack.push({
key: key,
value: value
});
},
get: function (key) {
for (var i = 0; i < stack.length; i++) {
if (key == stack[i].key) {
return stack[i];
}
}
},
keys: function() {
var keys = [];
for (var i = 0; i < stack.length; i++) {
keys.push(stack[i].key);
}
return keys;
},
top: function () {
return stack[stack.length - 1];
},
remove: function (key) {
var idx = -1;
for (var i = 0; i < stack.length; i++) {
if (key == stack[i].key) {
idx = i;
break;
}
}
return stack.splice(idx, 1)[0];
},
removeTop: function () {
return stack.splice(stack.length - 1, 1)[0];
},
length: function () {
return stack.length;
}
};
}
};
})
/**
* A helper directive for the $modal service. It creates a backdrop element.
*/
.directive('modalBackdrop', ['$timeout', function ($timeout) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/modal/backdrop.html',
link: function (scope) {
scope.animate = false;
//trigger CSS transitions
$timeout(function () {
scope.animate = true;
});
}
};
}])
.directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
return {
restrict: 'EA',
scope: {
index: '@',
animate: '='
},
replace: true,
transclude: true,
templateUrl: 'template/modal/window.html',
link: function (scope, element, attrs) {
scope.windowClass = attrs.windowClass || '';
$timeout(function () {
// trigger CSS transitions
scope.animate = true;
// focus a freshly-opened modal
element[0].focus();
});
scope.close = function (evt) {
var modal = $modalStack.getTop();
if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {
evt.preventDefault();
evt.stopPropagation();
$modalStack.dismiss(modal.key, 'backdrop click');
}
};
}
};
}])
.factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap',
function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) {
var OPENED_MODAL_CLASS = 'modal-open';
var backdropDomEl, backdropScope;
var openedWindows = $$stackedMap.createNew();
var $modalStack = {};
function backdropIndex() {
var topBackdropIndex = -1;
var opened = openedWindows.keys();
for (var i = 0; i < opened.length; i++) {
if (openedWindows.get(opened[i]).value.backdrop) {
topBackdropIndex = i;
}
}
return topBackdropIndex;
}
$rootScope.$watch(backdropIndex, function(newBackdropIndex){
if (backdropScope) {
backdropScope.index = newBackdropIndex;
}
});
function removeModalWindow(modalInstance) {
var body = $document.find('body').eq(0);
var modalWindow = openedWindows.get(modalInstance).value;
//clean up the stack
openedWindows.remove(modalInstance);
//remove window DOM element
removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, 300, checkRemoveBackdrop);
body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0);
}
function checkRemoveBackdrop() {
//remove backdrop if no longer needed
if (backdropDomEl && backdropIndex() == -1) {
var backdropScopeRef = backdropScope;
removeAfterAnimate(backdropDomEl, backdropScope, 150, function () {
backdropScopeRef.$destroy();
backdropScopeRef = null;
});
backdropDomEl = undefined;
backdropScope = undefined;
}
}
function removeAfterAnimate(domEl, scope, emulateTime, done) {
// Closing animation
scope.animate = false;
var transitionEndEventName = $transition.transitionEndEventName;
if (transitionEndEventName) {
// transition out
var timeout = $timeout(afterAnimating, emulateTime);
domEl.bind(transitionEndEventName, function () {
$timeout.cancel(timeout);
afterAnimating();
scope.$apply();
});
} else {
// Ensure this call is async
$timeout(afterAnimating, 0);
}
function afterAnimating() {
if (afterAnimating.done) {
return;
}
afterAnimating.done = true;
domEl.remove();
if (done) {
done();
}
}
}
$document.bind('keydown', function (evt) {
var modal;
if (evt.which === 27) {
modal = openedWindows.top();
if (modal && modal.value.keyboard) {
$rootScope.$apply(function () {
$modalStack.dismiss(modal.key);
});
}
}
});
$modalStack.open = function (modalInstance, modal) {
openedWindows.add(modalInstance, {
deferred: modal.deferred,
modalScope: modal.scope,
backdrop: modal.backdrop,
keyboard: modal.keyboard
});
var body = $document.find('body').eq(0),
currBackdropIndex = backdropIndex();
if (currBackdropIndex >= 0 && !backdropDomEl) {
backdropScope = $rootScope.$new(true);
backdropScope.index = currBackdropIndex;
backdropDomEl = $compile('<div modal-backdrop></div>')(backdropScope);
body.append(backdropDomEl);
}
var angularDomEl = angular.element('<div modal-window></div>');
angularDomEl.attr('window-class', modal.windowClass);
angularDomEl.attr('index', openedWindows.length() - 1);
angularDomEl.attr('animate', 'animate');
angularDomEl.html(modal.content);
var modalDomEl = $compile(angularDomEl)(modal.scope);
openedWindows.top().value.modalDomEl = modalDomEl;
body.append(modalDomEl);
body.addClass(OPENED_MODAL_CLASS);
};
$modalStack.close = function (modalInstance, result) {
var modalWindow = openedWindows.get(modalInstance).value;
if (modalWindow) {
modalWindow.deferred.resolve(result);
removeModalWindow(modalInstance);
}
};
$modalStack.dismiss = function (modalInstance, reason) {
var modalWindow = openedWindows.get(modalInstance).value;
if (modalWindow) {
modalWindow.deferred.reject(reason);
removeModalWindow(modalInstance);
}
};
$modalStack.dismissAll = function (reason) {
var topModal = this.getTop();
while (topModal) {
this.dismiss(topModal.key, reason);
topModal = this.getTop();
}
};
$modalStack.getTop = function () {
return openedWindows.top();
};
return $modalStack;
}])
.provider('$modal', function () {
var $modalProvider = {
options: {
backdrop: true, //can be also false or 'static'
keyboard: true
},
$get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack',
function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
var $modal = {};
function getTemplatePromise(options) {
return options.template ? $q.when(options.template) :
$http.get(options.templateUrl, {cache: $templateCache}).then(function (result) {
return result.data;
});
}
function getResolvePromises(resolves) {
var promisesArr = [];
angular.forEach(resolves, function (value, key) {
if (angular.isFunction(value) || angular.isArray(value)) {
promisesArr.push($q.when($injector.invoke(value)));
}
});
return promisesArr;
}
$modal.open = function (modalOptions) {
var modalResultDeferred = $q.defer();
var modalOpenedDeferred = $q.defer();
//prepare an instance of a modal to be injected into controllers and returned to a caller
var modalInstance = {
result: modalResultDeferred.promise,
opened: modalOpenedDeferred.promise,
close: function (result) {
$modalStack.close(modalInstance, result);
},
dismiss: function (reason) {
$modalStack.dismiss(modalInstance, reason);
}
};
//merge and clean up options
modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
modalOptions.resolve = modalOptions.resolve || {};
//verify options
if (!modalOptions.template && !modalOptions.templateUrl) {
throw new Error('One of template or templateUrl options is required.');
}
var templateAndResolvePromise =
$q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
var modalScope = (modalOptions.scope || $rootScope).$new();
modalScope.$close = modalInstance.close;
modalScope.$dismiss = modalInstance.dismiss;
var ctrlInstance, ctrlLocals = {};
var resolveIter = 1;
//controllers
if (modalOptions.controller) {
ctrlLocals.$scope = modalScope;
ctrlLocals.$modalInstance = modalInstance;
angular.forEach(modalOptions.resolve, function (value, key) {
ctrlLocals[key] = tplAndVars[resolveIter++];
});
ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
}
$modalStack.open(modalInstance, {
scope: modalScope,
deferred: modalResultDeferred,
content: tplAndVars[0],
backdrop: modalOptions.backdrop,
keyboard: modalOptions.keyboard,
windowClass: modalOptions.windowClass
});
}, function resolveError(reason) {
modalResultDeferred.reject(reason);
});
templateAndResolvePromise.then(function () {
modalOpenedDeferred.resolve(true);
}, function () {
modalOpenedDeferred.reject(false);
});
return modalInstance;
};
return $modal;
}]
};
return $modalProvider;
});
angular.module('ui.bootstrap.position', [])
/**
* A set of utility methods that can be use to retrieve position of DOM elements.
* It is meant to be used where we need to absolute-position DOM elements in
* relation to other, existing elements (this is the case for tooltips, popovers,
* typeahead suggestions etc.).
*/
.factory('$position', ['$document', '$window', function ($document, $window) {
function getStyle(el, cssprop) {
if (el.currentStyle) { //IE
return el.currentStyle[cssprop];
} else if ($window.getComputedStyle) {
return $window.getComputedStyle(el)[cssprop];
}
// finally try and get inline style
return el.style[cssprop];
}
/**
* Checks if a given element is statically positioned
* @param element - raw DOM element
*/
function isStaticPositioned(element) {
return (getStyle(element, "position") || 'static' ) === 'static';
}
/**
* returns the closest, non-statically positioned parentOffset of a given element
* @param element
*/
var parentOffsetEl = function (element) {
var docDomEl = $document[0];
var offsetParent = element.offsetParent || docDomEl;
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || docDomEl;
};
return {
/**
* Provides read-only equivalent of jQuery's position function:
* http://api.jquery.com/position/
*/
position: function (element) {
var elBCR = this.offset(element);
var offsetParentBCR = { top: 0, left: 0 };
var offsetParentEl = parentOffsetEl(element[0]);
if (offsetParentEl != $document[0]) {
offsetParentBCR = this.offset(angular.element(offsetParentEl));
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
var boundingClientRect = element[0].getBoundingClientRect();
return {
width: boundingClientRect.width || element.prop('offsetWidth'),
height: boundingClientRect.height || element.prop('offsetHeight'),
top: elBCR.top - offsetParentBCR.top,
left: elBCR.left - offsetParentBCR.left
};
},
/**
* Provides read-only equivalent of jQuery's offset function:
* http://api.jquery.com/offset/
*/
offset: function (element) {
var boundingClientRect = element[0].getBoundingClientRect();
return {
width: boundingClientRect.width || element.prop('offsetWidth'),
height: boundingClientRect.height || element.prop('offsetHeight'),
top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
};
}
};
}]);
angular.module('ui.bootstrap.bindHtml', [])
.directive('bindHtmlUnsafe', function () {
return function (scope, element, attr) {
element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe);
scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) {
element.html(value || '');
});
};
});
/**
* The following features are still outstanding: animation as a
* function, placement as a function, inside, support for more triggers than
* just mouse enter/leave, html tooltips, and selector delegation.
*/
angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap.bindHtml' ] )
/**
* The $tooltip service creates tooltip- and popover-like directives as well as
* houses global options for them.
*/
.provider( '$tooltip', function () {
// The default options tooltip and popover.
var defaultOptions = {
placement: 'top',
animation: true,
popupDelay: 0
};
// Default hide triggers for each show trigger
var triggerMap = {
'mouseenter': 'mouseleave',
'click': 'click',
'focus': 'blur'
};
// The options specified to the provider globally.
var globalOptions = {};
/**
* `options({})` allows global configuration of all tooltips in the
* application.
*
* var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
* // place tooltips left instead of top by default
* $tooltipProvider.options( { placement: 'left' } );
* });
*/
this.options = function( value ) {
angular.extend( globalOptions, value );
};
/**
* This allows you to extend the set of trigger mappings available. E.g.:
*
* $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
*/
this.setTriggers = function setTriggers ( triggers ) {
angular.extend( triggerMap, triggers );
};
/**
* This is a helper function for translating camel-case to snake-case.
*/
function snake_case(name){
var regexp = /[A-Z]/g;
var separator = '-';
return name.replace(regexp, function(letter, pos) {
return (pos ? separator : '') + letter.toLowerCase();
});
}
/**
* Returns the actual instance of the $tooltip service.
* TODO support multiple triggers
*/
this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $parse, $document, $position, $interpolate ) {
return function $tooltip ( type, prefix, defaultTriggerShow ) {
var options = angular.extend( {}, defaultOptions, globalOptions );
/**
* Returns an object of show and hide triggers.
*
* If a trigger is supplied,
* it is used to show the tooltip; otherwise, it will use the `trigger`
* option passed to the `$tooltipProvider.options` method; else it will
* default to the trigger supplied to this directive factory.
*
* The hide trigger is based on the show trigger. If the `trigger` option
* was passed to the `$tooltipProvider.options` method, it will use the
* mapped trigger from `triggerMap` or the passed trigger if the map is
* undefined; otherwise, it uses the `triggerMap` value of the show
* trigger; else it will just use the show trigger.
*/
function getTriggers ( trigger ) {
var show = trigger || options.trigger || defaultTriggerShow;
var hide = triggerMap[show] || show;
return {
show: show,
hide: hide
};
}
var directiveName = snake_case( type );
var startSym = $interpolate.startSymbol();
var endSym = $interpolate.endSymbol();
var template =
'<div '+ directiveName +'-popup '+
'title="'+startSym+'tt_title'+endSym+'" '+
'content="'+startSym+'tt_content'+endSym+'" '+
'placement="'+startSym+'tt_placement'+endSym+'" '+
'animation="tt_animation" '+
'is-open="tt_isOpen"'+
'>'+
'</div>';
return {
restrict: 'EA',
scope: true,
compile: function (tElem, tAttrs) {
var tooltipLinker = $compile( template );
return function link ( scope, element, attrs ) {
var tooltip;
var transitionTimeout;
var popupTimeout;
var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
var triggers = getTriggers( undefined );
var hasRegisteredTriggers = false;
var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
var positionTooltip = function (){
var position,
ttWidth,
ttHeight,
ttPosition;
// Get the position of the directive element.
position = appendToBody ? $position.offset( element ) : $position.position( element );
// Get the height and width of the tooltip so we can center it.
ttWidth = tooltip.prop( 'offsetWidth' );
ttHeight = tooltip.prop( 'offsetHeight' );
// Calculate the tooltip's top and left coordinates to center it with
// this directive.
switch ( scope.tt_placement ) {
case 'right':
ttPosition = {
top: position.top + position.height / 2 - ttHeight / 2,
left: position.left + position.width
};
break;
case 'bottom':
ttPosition = {
top: position.top + position.height,
left: position.left + position.width / 2 - ttWidth / 2
};
break;
case 'left':
ttPosition = {
top: position.top + position.height / 2 - ttHeight / 2,
left: position.left - ttWidth
};
break;
default:
ttPosition = {
top: position.top - ttHeight,
left: position.left + position.width / 2 - ttWidth / 2
};
break;
}
ttPosition.top += 'px';
ttPosition.left += 'px';
// Now set the calculated positioning.
tooltip.css( ttPosition );
};
// By default, the tooltip is not open.
// TODO add ability to start tooltip opened
scope.tt_isOpen = false;
function toggleTooltipBind () {
if ( ! scope.tt_isOpen ) {
showTooltipBind();
} else {
hideTooltipBind();
}
}
// Show the tooltip with delay if specified, otherwise show it immediately
function showTooltipBind() {
if(hasEnableExp && !scope.$eval(attrs[prefix+'Enable'])) {
return;
}
if ( scope.tt_popupDelay ) {
popupTimeout = $timeout( show, scope.tt_popupDelay, false );
popupTimeout.then(function(reposition){reposition();});
} else {
show()();
}
}
function hideTooltipBind () {
scope.$apply(function () {
hide();
});
}
// Show the tooltip popup element.
function show() {
// Don't show empty tooltips.
if ( ! scope.tt_content ) {
return angular.noop;
}
createTooltip();
// If there is a pending remove transition, we must cancel it, lest the
// tooltip be mysteriously removed.
if ( transitionTimeout ) {
$timeout.cancel( transitionTimeout );
}
// Set the initial positioning.
tooltip.css({ top: 0, left: 0, display: 'block' });
// Now we add it to the DOM because need some info about it. But it's not
// visible yet anyway.
if ( appendToBody ) {
$document.find( 'body' ).append( tooltip );
} else {
element.after( tooltip );
}
positionTooltip();
// And show the tooltip.
scope.tt_isOpen = true;
scope.$digest(); // digest required as $apply is not called
// Return positioning function as promise callback for correct
// positioning after draw.
return positionTooltip;
}
// Hide the tooltip popup element.
function hide() {
// First things first: we don't show it anymore.
scope.tt_isOpen = false;
//if tooltip is going to be shown after delay, we must cancel this
$timeout.cancel( popupTimeout );
// And now we remove it from the DOM. However, if we have animation, we
// need to wait for it to expire beforehand.
// FIXME: this is a placeholder for a port of the transitions library.
if ( scope.tt_animation ) {
transitionTimeout = $timeout(removeTooltip, 500);
} else {
removeTooltip();
}
}
function createTooltip() {
// There can only be one tooltip element per directive shown at once.
if (tooltip) {
removeTooltip();
}
tooltip = tooltipLinker(scope, function () {});
// Get contents rendered into the tooltip
scope.$digest();
}
function removeTooltip() {
if (tooltip) {
tooltip.remove();
tooltip = null;
}
}
/**
* Observe the relevant attributes.
*/
attrs.$observe( type, function ( val ) {
scope.tt_content = val;
if (!val && scope.tt_isOpen ) {
hide();
}
});
attrs.$observe( prefix+'Title', function ( val ) {
scope.tt_title = val;
});
attrs.$observe( prefix+'Placement', function ( val ) {
scope.tt_placement = angular.isDefined( val ) ? val : options.placement;
});
attrs.$observe( prefix+'PopupDelay', function ( val ) {
var delay = parseInt( val, 10 );
scope.tt_popupDelay = ! isNaN(delay) ? delay : options.popupDelay;
});
var unregisterTriggers = function() {
if (hasRegisteredTriggers) {
element.unbind( triggers.show, showTooltipBind );
element.unbind( triggers.hide, hideTooltipBind );
}
};
attrs.$observe( prefix+'Trigger', function ( val ) {
unregisterTriggers();
triggers = getTriggers( val );
if ( triggers.show === triggers.hide ) {
element.bind( triggers.show, toggleTooltipBind );
} else {
element.bind( triggers.show, showTooltipBind );
element.bind( triggers.hide, hideTooltipBind );
}
hasRegisteredTriggers = true;
});
var animation = scope.$eval(attrs[prefix + 'Animation']);
scope.tt_animation = angular.isDefined(animation) ? !!animation : options.animation;
attrs.$observe( prefix+'AppendToBody', function ( val ) {
appendToBody = angular.isDefined( val ) ? $parse( val )( scope ) : appendToBody;
});
// if a tooltip is attached to <body> we need to remove it on
// location change as its parent scope will probably not be destroyed
// by the change.
if ( appendToBody ) {
scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () {
if ( scope.tt_isOpen ) {
hide();
}
});
}
// Make sure tooltip is destroyed and removed.
scope.$on('$destroy', function onDestroyTooltip() {
$timeout.cancel( transitionTimeout );
$timeout.cancel( popupTimeout );
unregisterTriggers();
removeTooltip();
});
};
}
};
};
}];
})
.directive( 'tooltipPopup', function () {
return {
restrict: 'EA',
replace: true,
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
templateUrl: 'template/tooltip/tooltip-popup.html'
};
})
.directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {
return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );
}])
.directive( 'tooltipHtmlUnsafePopup', function () {
return {
restrict: 'EA',
replace: true,
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
};
})
.directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );
}]);

8
app/vendor/ui-bootstrap/ui-bootstrap-custom-0.10.0.min.js vendored

File diff suppressed because one or more lines are too long

332
app/vendor/ui-bootstrap/ui-bootstrap-custom-0.7.0.js vendored

@ -1,332 +0,0 @@ @@ -1,332 +0,0 @@
angular.module("ui.bootstrap", ["ui.bootstrap.modal"]);
angular.module('ui.bootstrap.modal', [])
/**
* A helper, internal data structure that acts as a map but also allows getting / removing
* elements in the LIFO order
*/
.factory('$$stackedMap', function () {
return {
createNew: function () {
var stack = [];
return {
add: function (key, value) {
stack.push({
key: key,
value: value
});
},
get: function (key) {
for (var i = 0; i < stack.length; i++) {
if (key == stack[i].key) {
return stack[i];
}
}
},
keys: function() {
var keys = [];
for (var i = 0; i < stack.length; i++) {
keys.push(stack[i].key);
}
return keys;
},
top: function () {
return stack[stack.length - 1];
},
remove: function (key) {
var idx = -1;
for (var i = 0; i < stack.length; i++) {
if (key == stack[i].key) {
idx = i;
break;
}
}
return stack.splice(idx, 1)[0];
},
removeTop: function () {
return stack.splice(stack.length - 1, 1)[0];
},
length: function () {
return stack.length;
}
};
}
};
})
/**
* A helper directive for the $modal service. It creates a backdrop element.
*/
.directive('modalBackdrop', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/modal/backdrop.html',
link: function (scope, element, attrs) {
//trigger CSS transitions
$timeout(function () {
scope.animate = true;
});
scope.close = function (evt) {
var modal = $modalStack.getTop();
if (modal && modal.value.backdrop && modal.value.backdrop != 'static') {
evt.preventDefault();
evt.stopPropagation();
$modalStack.dismiss(modal.key, 'backdrop click');
}
};
}
};
}])
.directive('modalWindow', ['$timeout', function ($timeout) {
return {
restrict: 'EA',
scope: {
index: '@'
},
replace: true,
transclude: true,
templateUrl: 'template/modal/window.html',
link: function (scope, element, attrs) {
scope.windowClass = attrs.windowClass || '';
//trigger CSS transitions
$timeout(function () {
scope.animate = true;
});
scope.close = function (evt) {
var modal = $modalStack.getTop();
if (modal) {
$modalStack.dismiss(modal.key, 'close click');
}
};
}
};
}])
.factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap',
function ($document, $compile, $rootScope, $$stackedMap) {
var backdropjqLiteEl, backdropDomEl;
var backdropScope = $rootScope.$new(true);
var body = $document.find('body').eq(0);
var openedWindows = $$stackedMap.createNew();
var $modalStack = {};
function backdropIndex() {
var topBackdropIndex = -1;
var opened = openedWindows.keys();
for (var i = 0; i < opened.length; i++) {
if (openedWindows.get(opened[i]).value.backdrop) {
topBackdropIndex = i;
}
}
return topBackdropIndex;
}
$rootScope.$watch(backdropIndex, function(newBackdropIndex){
backdropScope.index = newBackdropIndex;
});
function removeModalWindow(modalInstance) {
var modalWindow = openedWindows.get(modalInstance).value;
//clean up the stack
openedWindows.remove(modalInstance);
//remove window DOM element
modalWindow.modalDomEl.remove();
//remove backdrop if no longer needed
if (backdropDomEl && backdropIndex() == -1) {
backdropDomEl.remove();
backdropDomEl = undefined;
}
//destroy scope
modalWindow.modalScope.$destroy();
}
$document.bind('keydown', function (evt) {
var modal;
if (evt.which === 27) {
modal = openedWindows.top();
if (modal && modal.value.keyboard) {
$rootScope.$apply(function () {
$modalStack.dismiss(modal.key);
});
}
}
});
$modalStack.open = function (modalInstance, modal) {
openedWindows.add(modalInstance, {
deferred: modal.deferred,
modalScope: modal.scope,
backdrop: modal.backdrop,
keyboard: modal.keyboard
});
var angularDomEl = angular.element('<div modal-window></div>');
angularDomEl.attr('window-class', modal.windowClass);
angularDomEl.attr('index', openedWindows.length() - 1);
angularDomEl.html(modal.content);
var modalDomEl = $compile(angularDomEl)(modal.scope);
openedWindows.top().value.modalDomEl = modalDomEl;
body.append(modalDomEl);
if (backdropIndex() >= 0 && !backdropDomEl) {
backdropjqLiteEl = angular.element('<div modal-backdrop></div>');
backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
body.append(backdropDomEl);
}
};
$modalStack.close = function (modalInstance, result) {
var modal = openedWindows.get(modalInstance);
if (modal) {
modal.value.deferred.resolve(result);
removeModalWindow(modalInstance);
}
};
$modalStack.dismiss = function (modalInstance, reason) {
console.trace();
var modalWindow = openedWindows.get(modalInstance).value;
if (modalWindow) {
modalWindow.deferred.reject(reason);
removeModalWindow(modalInstance);
}
};
$modalStack.getTop = function () {
return openedWindows.top();
};
$modalStack.dismissAll = function () {
var modal;
while (modal = openedWindows.top()) {
$rootScope.$apply(function () {
$modalStack.dismiss(modal.key);
});
}
}
return $modalStack;
}])
.provider('$modal', function () {
var $modalProvider = {
options: {
backdrop: true, //can be also false or 'static'
keyboard: true
},
$get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack',
function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
var $modal = {};
function getTemplatePromise(options) {
return options.template ? $q.when(options.template) :
$http.get(options.templateUrl, {cache: $templateCache}).then(function (result) {
return result.data;
});
}
function getResolvePromises(resolves) {
var promisesArr = [];
angular.forEach(resolves, function (value, key) {
if (angular.isFunction(value) || angular.isArray(value)) {
promisesArr.push($q.when($injector.invoke(value)));
}
});
return promisesArr;
}
$modal.open = function (modalOptions) {
var modalResultDeferred = $q.defer();
var modalOpenedDeferred = $q.defer();
//prepare an instance of a modal to be injected into controllers and returned to a caller
var modalInstance = {
result: modalResultDeferred.promise,
opened: modalOpenedDeferred.promise,
close: function (result) {
$modalStack.close(modalInstance, result);
},
dismiss: function (reason) {
$modalStack.dismiss(modalInstance, reason);
}
};
//merge and clean up options
modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
modalOptions.resolve = modalOptions.resolve || {};
//verify options
if (!modalOptions.template && !modalOptions.templateUrl) {
throw new Error('One of template or templateUrl options is required.');
}
var templateAndResolvePromise =
$q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
var modalScope = (modalOptions.scope || $rootScope).$new();
modalScope.$close = modalInstance.close;
modalScope.$dismiss = modalInstance.dismiss;
var ctrlInstance, ctrlLocals = {};
var resolveIter = 1;
//controllers
if (modalOptions.controller) {
ctrlLocals.$scope = modalScope;
ctrlLocals.$modalInstance = modalInstance;
angular.forEach(modalOptions.resolve, function (value, key) {
ctrlLocals[key] = tplAndVars[resolveIter++];
});
ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
}
$modalStack.open(modalInstance, {
scope: modalScope,
deferred: modalResultDeferred,
content: tplAndVars[0],
backdrop: modalOptions.backdrop,
keyboard: modalOptions.keyboard,
windowClass: modalOptions.windowClass
});
}, function resolveError(reason) {
modalResultDeferred.reject(reason);
});
templateAndResolvePromise.then(function () {
modalOpenedDeferred.resolve(true);
}, function () {
modalOpenedDeferred.reject(false);
});
return modalInstance;
};
return $modal;
}]
};
return $modalProvider;
});

1
app/vendor/ui-bootstrap/ui-bootstrap-custom-0.7.0.min.js vendored

@ -1 +0,0 @@ @@ -1 +0,0 @@
angular.module("ui.bootstrap",["ui.bootstrap.modal"]),angular.module("ui.bootstrap.modal",[]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c<a.length;c++)if(b==a[c].key)return a[c]},keys:function(){for(var b=[],c=0;c<a.length;c++)b.push(a[c].key);return b},top:function(){return a[a.length-1]},remove:function(b){for(var c=-1,d=0;d<a.length;d++)if(b==a[d].key){c=d;break}return a.splice(c,1)[0]},removeTop:function(){return a.splice(a.length-1,1)[0]},length:function(){return a.length}}}}}).directive("modalBackdrop",["$modalStack","$timeout",function(a,b){return{restrict:"EA",replace:!0,templateUrl:"template/modal/backdrop.html",link:function(c){b(function(){c.animate=!0}),c.close=function(b){var c=a.getTop();c&&c.value.backdrop&&"static"!=c.value.backdrop&&(b.preventDefault(),b.stopPropagation(),a.dismiss(c.key,"backdrop click"))}}}}]).directive("modalWindow",["$timeout",function(a){return{restrict:"EA",scope:{index:"@"},replace:!0,transclude:!0,templateUrl:"template/modal/window.html",link:function(b,c,d){b.windowClass=d.windowClass||"",a(function(){b.animate=!0})}}}]).factory("$modalStack",["$document","$compile","$rootScope","$$stackedMap",function(a,b,c,d){function e(){for(var a=-1,b=k.keys(),c=0;c<b.length;c++)k.get(b[c]).value.backdrop&&(a=c);return a}function f(a){var b=k.get(a).value;k.remove(a),b.modalDomEl.remove(),h&&-1==e()&&(h.remove(),h=void 0),b.modalScope.$destroy()}var g,h,i=c.$new(!0),j=a.find("body").eq(0),k=d.createNew(),l={};return c.$watch(e,function(a){i.index=a}),a.bind("keydown",function(a){var b;27===a.which&&(b=k.top(),b&&b.value.keyboard&&c.$apply(function(){l.dismiss(b.key)}))}),l.open=function(a,c){k.add(a,{deferred:c.deferred,modalScope:c.scope,backdrop:c.backdrop,keyboard:c.keyboard});var d=angular.element("<div modal-window></div>");d.attr("window-class",c.windowClass),d.attr("index",k.length()-1),d.html(c.content);var f=b(d)(c.scope);k.top().value.modalDomEl=f,j.append(f),e()>=0&&!h&&(g=angular.element("<div modal-backdrop></div>"),h=b(g)(i),j.append(h))},l.close=function(a,b){var c=k.get(a);c&&(c.value.deferred.resolve(b),f(a))},l.dismiss=function(a,b){var c=k.get(a).value;c&&(c.deferred.reject(b),f(a))},l.getTop=function(){return k.top()},l}]).provider("$modal",function(){var a={options:{backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$http","$templateCache","$controller","$modalStack",function(b,c,d,e,f,g,h){function i(a){return a.template?d.when(a.template):e.get(a.templateUrl,{cache:f}).then(function(a){return a.data})}function j(a){var c=[];return angular.forEach(a,function(a){(angular.isFunction(a)||angular.isArray(a))&&c.push(d.when(b.invoke(a)))}),c}var k={};return k.open=function(b){var e=d.defer(),f=d.defer(),k={result:e.promise,opened:f.promise,close:function(a){h.close(k,a)},dismiss:function(a){h.dismiss(k,a)}};if(b=angular.extend({},a.options,b),b.resolve=b.resolve||{},!b.template&&!b.templateUrl)throw new Error("One of template or templateUrl options is required.");var l=d.all([i(b)].concat(j(b.resolve)));return l.then(function(a){var d=(b.scope||c).$new();d.$close=k.close,d.$dismiss=k.dismiss;var f,i={},j=1;b.controller&&(i.$scope=d,i.$modalInstance=k,angular.forEach(b.resolve,function(b,c){i[c]=a[j++]}),f=g(b.controller,i)),h.open(k,{scope:d,deferred:e,content:a[0],backdrop:b.backdrop,keyboard:b.keyboard,windowClass:b.windowClass})},function(a){e.reject(a)}),l.then(function(){f.resolve(!0)},function(){f.reject(!1)}),k},k}]};return a});

1024
app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.10.0.js vendored

File diff suppressed because it is too large Load Diff

8
app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.10.0.min.js vendored

File diff suppressed because one or more lines are too long

353
app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.7.0.js vendored

@ -1,353 +0,0 @@ @@ -1,353 +0,0 @@
angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.modal"]);
angular.module("ui.bootstrap.tpls", ["template/modal/backdrop.html","template/modal/window.html"]);
angular.module('ui.bootstrap.modal', [])
/**
* A helper, internal data structure that acts as a map but also allows getting / removing
* elements in the LIFO order
*/
.factory('$$stackedMap', function () {
return {
createNew: function () {
var stack = [];
return {
add: function (key, value) {
stack.push({
key: key,
value: value
});
},
get: function (key) {
for (var i = 0; i < stack.length; i++) {
if (key == stack[i].key) {
return stack[i];
}
}
},
keys: function() {
var keys = [];
for (var i = 0; i < stack.length; i++) {
keys.push(stack[i].key);
}
return keys;
},
top: function () {
return stack[stack.length - 1];
},
remove: function (key) {
var idx = -1;
for (var i = 0; i < stack.length; i++) {
if (key == stack[i].key) {
idx = i;
break;
}
}
return stack.splice(idx, 1)[0];
},
removeTop: function () {
return stack.splice(stack.length - 1, 1)[0];
},
length: function () {
return stack.length;
}
};
}
};
})
/**
* A helper directive for the $modal service. It creates a backdrop element.
*/
.directive('modalBackdrop', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/modal/backdrop.html',
link: function (scope, element, attrs) {
//trigger CSS transitions
$timeout(function () {
scope.animate = true;
});
scope.close = function (evt) {
var modal = $modalStack.getTop();
if (modal && modal.value.backdrop && modal.value.backdrop != 'static') {
evt.preventDefault();
evt.stopPropagation();
$modalStack.dismiss(modal.key, 'backdrop click');
}
};
}
};
}])
.directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
return {
restrict: 'EA',
scope: {
index: '@'
},
replace: true,
transclude: true,
templateUrl: 'template/modal/window.html',
link: function (scope, element, attrs) {
scope.windowClass = attrs.windowClass || '';
//trigger CSS transitions
$timeout(function () {
scope.animate = true;
});
scope.close = function (evt) {
var modal = $modalStack.getTop();
if (modal) {
evt.preventDefault();
evt.stopPropagation();
$modalStack.dismiss(modal.key, 'backdrop click');
}
};
}
};
}])
.factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap',
function ($document, $compile, $rootScope, $$stackedMap) {
var backdropjqLiteEl, backdropDomEl;
var backdropScope = $rootScope.$new(true);
var body = $document.find('body').eq(0);
var openedWindows = $$stackedMap.createNew();
var $modalStack = {};
function backdropIndex() {
var topBackdropIndex = -1;
var opened = openedWindows.keys();
for (var i = 0; i < opened.length; i++) {
if (openedWindows.get(opened[i]).value.backdrop) {
topBackdropIndex = i;
}
}
return topBackdropIndex;
}
$rootScope.$watch(backdropIndex, function(newBackdropIndex){
backdropScope.index = newBackdropIndex;
});
function removeModalWindow(modalInstance) {
var modalWindow = openedWindows.get(modalInstance).value;
//clean up the stack
openedWindows.remove(modalInstance);
//remove window DOM element
modalWindow.modalDomEl.remove();
//remove backdrop if no longer needed
if (backdropDomEl && backdropIndex() == -1) {
backdropDomEl.remove();
backdropDomEl = undefined;
}
//destroy scope
modalWindow.modalScope.$destroy();
}
$document.bind('keydown', function (evt) {
var modal;
if (evt.which === 27) {
modal = openedWindows.top();
if (modal && modal.value.keyboard) {
$rootScope.$apply(function () {
$modalStack.dismiss(modal.key);
});
}
}
});
$modalStack.open = function (modalInstance, modal) {
openedWindows.add(modalInstance, {
deferred: modal.deferred,
modalScope: modal.scope,
backdrop: modal.backdrop,
keyboard: modal.keyboard
});
var angularDomEl = angular.element('<div modal-window></div>');
angularDomEl.attr('window-class', modal.windowClass);
angularDomEl.attr('index', openedWindows.length() - 1);
angularDomEl.html(modal.content);
var modalDomEl = $compile(angularDomEl)(modal.scope);
openedWindows.top().value.modalDomEl = modalDomEl;
body.append(modalDomEl);
if (backdropIndex() >= 0 && !backdropDomEl) {
backdropjqLiteEl = angular.element('<div modal-backdrop></div>');
backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
body.append(backdropDomEl);
}
};
$modalStack.close = function (modalInstance, result) {
var modal = openedWindows.get(modalInstance);
if (modal) {
modal.value.deferred.resolve(result);
removeModalWindow(modalInstance);
}
};
$modalStack.dismiss = function (modalInstance, reason) {
var modalWindow = openedWindows.get(modalInstance).value;
if (modalWindow) {
modalWindow.deferred.reject(reason);
removeModalWindow(modalInstance);
}
};
$modalStack.dismissAll = function () {
var modal;
while (openedWindows.length()) {
if (modal = openedWindows.top()) {
$modalStack.dismiss(modal.key);
}
}
}
$modalStack.getTop = function () {
return openedWindows.top();
};
return $modalStack;
}])
.provider('$modal', function () {
var $modalProvider = {
options: {
backdrop: true, //can be also false or 'static'
keyboard: true
},
$get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack',
function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
var $modal = {};
function getTemplatePromise(options) {
return options.template ? $q.when(options.template) :
$http.get(options.templateUrl, {cache: $templateCache}).then(function (result) {
return result.data;
});
}
function getResolvePromises(resolves) {
var promisesArr = [];
angular.forEach(resolves, function (value, key) {
if (angular.isFunction(value) || angular.isArray(value)) {
promisesArr.push($q.when($injector.invoke(value)));
}
});
return promisesArr;
}
$modal.open = function (modalOptions) {
var modalResultDeferred = $q.defer();
var modalOpenedDeferred = $q.defer();
//prepare an instance of a modal to be injected into controllers and returned to a caller
var modalInstance = {
result: modalResultDeferred.promise,
opened: modalOpenedDeferred.promise,
close: function (result) {
$modalStack.close(modalInstance, result);
},
dismiss: function (reason) {
$modalStack.dismiss(modalInstance, reason);
}
};
//merge and clean up options
modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
modalOptions.resolve = modalOptions.resolve || {};
//verify options
if (!modalOptions.template && !modalOptions.templateUrl) {
throw new Error('One of template or templateUrl options is required.');
}
var templateAndResolvePromise =
$q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
var modalScope = (modalOptions.scope || $rootScope).$new();
modalScope.$close = modalInstance.close;
modalScope.$dismiss = modalInstance.dismiss;
var ctrlInstance, ctrlLocals = {};
var resolveIter = 1;
//controllers
if (modalOptions.controller) {
ctrlLocals.$scope = modalScope;
ctrlLocals.$modalInstance = modalInstance;
angular.forEach(modalOptions.resolve, function (value, key) {
ctrlLocals[key] = tplAndVars[resolveIter++];
});
ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
}
$modalStack.open(modalInstance, {
scope: modalScope,
deferred: modalResultDeferred,
content: tplAndVars[0],
backdrop: modalOptions.backdrop,
keyboard: modalOptions.keyboard,
windowClass: modalOptions.windowClass
});
}, function resolveError(reason) {
modalResultDeferred.reject(reason);
});
templateAndResolvePromise.then(function () {
modalOpenedDeferred.resolve(true);
}, function () {
modalOpenedDeferred.reject(false);
});
return modalInstance;
};
return $modal;
}]
};
return $modalProvider;
});
angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template/modal/backdrop.html",
"<div class=\"modal-backdrop fade\" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1040 + index*10}\" ng-click=\"close($event)\"></div>");
}]);
angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template/modal/window.html",
"<div class=\"modal fade {{ windowClass }}\" tabindex=\"-1\" role=\"dialog\" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\
<div class=\"modal_close_wrap\" ng-click=\"close($event)\">\
<div class=\"modal_close\"></div>\
</div>\
<div class=\"modal-dialog\">\
<div class=\"modal-content\" ng-transclude>\
</div>\
</div>\
</div>");
}]);

1
app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.7.0.min.js vendored

@ -1 +0,0 @@ @@ -1 +0,0 @@
angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.modal"]),angular.module("ui.bootstrap.tpls",["template/modal/backdrop.html","template/modal/window.html"]),angular.module("ui.bootstrap.modal",[]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c<a.length;c++)if(b==a[c].key)return a[c]},keys:function(){for(var b=[],c=0;c<a.length;c++)b.push(a[c].key);return b},top:function(){return a[a.length-1]},remove:function(b){for(var c=-1,d=0;d<a.length;d++)if(b==a[d].key){c=d;break}return a.splice(c,1)[0]},removeTop:function(){return a.splice(a.length-1,1)[0]},length:function(){return a.length}}}}}).directive("modalBackdrop",["$modalStack","$timeout",function(a,b){return{restrict:"EA",replace:!0,templateUrl:"template/modal/backdrop.html",link:function(c){b(function(){c.animate=!0}),c.close=function(b){var c=a.getTop();c&&c.value.backdrop&&"static"!=c.value.backdrop&&(b.preventDefault(),b.stopPropagation(),a.dismiss(c.key,"backdrop click"))}}}}]).directive("modalWindow",["$timeout",function(a){return{restrict:"EA",scope:{index:"@"},replace:!0,transclude:!0,templateUrl:"template/modal/window.html",link:function(b,c,d){b.windowClass=d.windowClass||"",a(function(){b.animate=!0})}}}]).factory("$modalStack",["$document","$compile","$rootScope","$$stackedMap",function(a,b,c,d){function e(){for(var a=-1,b=k.keys(),c=0;c<b.length;c++)k.get(b[c]).value.backdrop&&(a=c);return a}function f(a){var b=k.get(a).value;k.remove(a),b.modalDomEl.remove(),h&&-1==e()&&(h.remove(),h=void 0),b.modalScope.$destroy()}var g,h,i=c.$new(!0),j=a.find("body").eq(0),k=d.createNew(),l={};return c.$watch(e,function(a){i.index=a}),a.bind("keydown",function(a){var b;27===a.which&&(b=k.top(),b&&b.value.keyboard&&c.$apply(function(){l.dismiss(b.key)}))}),l.open=function(a,c){k.add(a,{deferred:c.deferred,modalScope:c.scope,backdrop:c.backdrop,keyboard:c.keyboard});var d=angular.element("<div modal-window></div>");d.attr("window-class",c.windowClass),d.attr("index",k.length()-1),d.html(c.content);var f=b(d)(c.scope);k.top().value.modalDomEl=f,j.append(f),e()>=0&&!h&&(g=angular.element("<div modal-backdrop></div>"),h=b(g)(i),j.append(h))},l.close=function(a,b){var c=k.get(a);c&&(c.value.deferred.resolve(b),f(a))},l.dismiss=function(a,b){var c=k.get(a).value;c&&(c.deferred.reject(b),f(a))},l.getTop=function(){return k.top()},l}]).provider("$modal",function(){var a={options:{backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$http","$templateCache","$controller","$modalStack",function(b,c,d,e,f,g,h){function i(a){return a.template?d.when(a.template):e.get(a.templateUrl,{cache:f}).then(function(a){return a.data})}function j(a){var c=[];return angular.forEach(a,function(a){(angular.isFunction(a)||angular.isArray(a))&&c.push(d.when(b.invoke(a)))}),c}var k={};return k.open=function(b){var e=d.defer(),f=d.defer(),k={result:e.promise,opened:f.promise,close:function(a){h.close(k,a)},dismiss:function(a){h.dismiss(k,a)}};if(b=angular.extend({},a.options,b),b.resolve=b.resolve||{},!b.template&&!b.templateUrl)throw new Error("One of template or templateUrl options is required.");var l=d.all([i(b)].concat(j(b.resolve)));return l.then(function(a){var d=(b.scope||c).$new();d.$close=k.close,d.$dismiss=k.dismiss;var f,i={},j=1;b.controller&&(i.$scope=d,i.$modalInstance=k,angular.forEach(b.resolve,function(b,c){i[c]=a[j++]}),f=g(b.controller,i)),h.open(k,{scope:d,deferred:e,content:a[0],backdrop:b.backdrop,keyboard:b.keyboard,windowClass:b.windowClass})},function(a){e.reject(a)}),l.then(function(){f.resolve(!0)},function(){f.reject(!1)}),k},k}]};return a}),angular.module("template/modal/backdrop.html",[]).run(["$templateCache",function(a){a.put("template/modal/backdrop.html",'<div class="modal-backdrop fade" ng-class="{in: animate}" ng-style="{\'z-index\': 1040 + index*10}" ng-click="close($event)"></div>')}]),angular.module("template/modal/window.html",[]).run(["$templateCache",function(a){a.put("template/modal/window.html",'<div class="modal fade {{ windowClass }}" ng-class="{in: animate}" ng-style="{\'z-index\': 1050 + index*10}" ng-transclude></div>')}]);
Loading…
Cancel
Save