Updated Private -> RSA
[samples/web/CallLog.git] / js / app.ui.js
1 /*jslint devel: true*/
2 /*global tizen, document, $, jQuery, app, UiPanel, UiContact, TemplateManager, window, Helpers */
3
4 /**
5  * @class Ui
6  */
7
8 function Ui(contacts) {
9         'use strict';
10         this.init();
11 }
12
13 (function () { // strict mode wrapper
14         'use strict';
15         Ui.prototype = {
16                 /**
17                  * UI remove mode
18                  * @type {boolean}
19                  */
20                 removeMode: false,
21
22                 addressBook: tizen.contact.getDefaultAddressBook(),
23
24                 photoURIdefault: null,
25
26                 contactsLoaded: null,
27
28                 checkedLogs: [],
29
30                 /**
31                  * @type {TemplateManager}
32                  */
33                 templateManager: null,
34
35                 /**
36                  * UI Initializer
37                  */
38                 init: function Ui_init() {
39                         this.loadContacts();
40                         this.templateManager = new TemplateManager();
41                         this.helpers = new Helpers();
42                         $(document).ready(this.domInit.bind(this));
43                         $.mobile.tizen.disableSelection(document);
44                 },
45
46                 /**
47                  * When DOM is ready, initialise it (bind events)
48                  */
49                 domInit: function Ui_domInit() {
50                         this.templateManager.loadToCache(['callView',
51                                 'callerHistory',
52                                 'callItemRow',
53                                 'callerCallItemRow',
54                                 'messageWindow',
55                                 'errorWindow',
56                                 'dateRow'], this.initPages.bind(this));
57                 },
58
59                 /**
60                  * UI pages initializer
61                  */
62                 initPages: function Ui_initPages() {
63                         var pages = [], body = $('body');
64
65                         body.append(this.templateManager.get('messageWindow')).append(this.templateManager.get('errorWindow')).trigger('create');
66                         $('#callView').append($(this.templateManager.get('callView')).children()).trigger('pagecreate');
67                         pages.push(this.templateManager.get('callerHistory'));
68                         body.append(pages.join(''));
69                         this.removeSearchBarToHeader();
70
71                         this.addEvents();
72                         app.showCallHistory();
73
74                         this.photoURIdefault = $("#header .photo").css('background-image');
75                 },
76
77                 /**
78                  * Add UI events
79                  */
80                 addEvents: function Ui_addEvents() {
81                         var self = this;
82
83                         $(window).on('softkeyboardchange', function () {
84                                 $.mobile.activePage.page('refresh');
85                         });
86
87                         $('#callView').on('tap', '.ui-btn-back', app.exit.bind(app));
88
89                         $('#callView').on('pagebeforeshow', function () {
90                                 app.showCallHistory();
91                         });
92
93                         $('#historyForCallerView').on('tap', '.ui-btn-back', function (event) {
94                                 event.preventDefault();
95                                 event.stopPropagation();
96                                 $.mobile.changePage('#callView');
97                         });
98
99                         $('#historyForCallerView').on('pagebeforeshow', function () {
100                                 self.hideCheckboxes();
101                                 $('#historyForCallerView .ui-content.ui-scrollview-clip .ui-scrollview-view')
102                                         .css('-webkit-transform', 'translate3d(0px, 0px, 0px)');
103                                 $('#selectAllDetails').on('change', function () {
104                                         self.selectAll();
105                                 });
106                         });
107
108                         $('#historyForCallerView').on('pageshow', function () {
109                                 $('#content').css('top', '160px');
110                                 $('#header').css('height', '160px');
111                                 $('#delete-toolbar').css('width', '70px');
112                                 $('#callerListContainer')
113                                         .children('.ui-overflow-indicator-top', '.ui-overflow-indicator-bottom')
114                                         .hide();
115                         });
116
117                         $('#callerListContainer').on('scrollstart', function () {
118                                 $(this)
119                                         .children('.ui-overflow-indicator-top', '.ui-overflow-indicator-bottom')
120                                         .fadeIn(200);
121                         });
122
123                         $('#callerListContainer').on('scrollstop', function () {
124                                 $(this)
125                                         .children('.ui-overflow-indicator-top', '.ui-overflow-indicator-bottom')
126                                         .fadeOut(200);
127                         });
128
129                         $(window).on('resize', function () {
130                                 $('#delete-toolbar').css('width', '70px');
131                         });
132
133                         $('.selectAllBox').children().on('tap', function () {
134                                 self.selectAll();
135                                 self.selectAllDetailsEach();
136                         });
137
138                         $('#calllogList').on('tap', '.date', function (event) {
139                                 event.stopPropagation();
140                         });
141
142                         $('#calllogList').on('click', '.call', function onCalllogEntryClick(event) {
143                                 if ($(this).data('entries')[0].remoteParties[0].remoteParty !== "") {
144                                         app.showHistoryForCaller($(this).data('entries')[0].remoteParties[0].remoteParty);
145                                         $.mobile.changePage('#historyForCallerView');
146                                 }
147                         });
148
149                         $('#smsActionBtn').on('tap', function (event) {
150                                 event.preventDefault();
151                                 event.stopPropagation();
152                                 if ($(this).data('activated')) {
153                                         return false;
154                                 }
155
156                                 self.lockOptionButtons();
157                                 self.hideCheckboxes();
158                                 app.sendSms($('#forCallerList').data('remoteParty'));
159                         });
160
161                         $('#callActionBtn').on("tap", function (event) {
162                                 event.preventDefault();
163                                 event.stopPropagation();
164                                 if ($(this).data('activated')) {
165                                         return false;
166                                 }
167
168                                 self.lockOptionButtons();
169                                 self.hideCheckboxes();
170                                 app.makeCall($('#forCallerList').data('remoteParty'));
171
172                                 return false;
173                         });
174
175                         $('#deleteActionBtn').on('click', function () {
176                                 self.changeDetailsToRemoveState();
177                         });
178
179                         $('.selectAllBox').on('tap', 'li', function () {
180                                 var checkbox = $(this).find(':checkbox');
181                                 if (self.removeMode === true) {
182                                         if (checkbox.attr('checked')) {
183                                                 checkbox.attr('checked', false)
184                                                         .data('checkboxradio')
185                                                         .refresh();
186                                         } else {
187                                                 checkbox.attr('checked', true)
188                                                         .data('checkboxradio')
189                                                         .refresh();
190                                         }
191                                         self.setSelectAllDetails();
192                                 }
193                         });
194
195                         $('#popup')
196                                 .on('click', '#popupCancelActionBtn', this.closePopup.bind(this))
197                                 .on('click', '#popupSubmitActionBtn', this.deleteCheckedLogs.bind(this));
198
199                         $('#errorPopup').on('tap', '#errorPopupOkBtn', this.closeErrorPopup);
200
201                         $(window).keyup(function(e){
202                                 if (e.which === 13) {
203                                         $('input:focus').blur();
204                                 }
205                         });
206
207                         document.addEventListener('tizenhwkey', function(e) {
208                                 if (e.keyName == "back") {
209                                         if ($.mobile.activePage.attr('id') === 'callView') {
210                                                         tizen.application.getCurrentApplication().exit();
211                                         } else {
212                                                 history.back();
213                                         }
214                                 }
215                         });
216
217                         self.onVisibilityChange();
218                 },
219
220                 lockOptionButtons: function () {
221                         var buttons = $('#callActionBtn, #smsActionBtn');
222                         buttons.data('activated', true);
223                         setTimeout(function () {
224                                 buttons.data('activated', false);
225                         }, 2000);
226                 },
227
228                 addEventsForCallerListCheckboxes: function Ui_addEventsForCallerListCheckboxes() {
229                         var self = this;
230                         $('#forCallerList :checkbox').on('change', function (event) {
231                                 if ($(this).attr('checked')) {
232                                         $(this).attr('checked', true);
233                                 } else {
234                                         $(this).attr('checked', false);
235                                 }
236                                 self.setSelectAllDetails();
237                         });
238                 },
239
240                 selectAll: function () {
241                         if ($('#selectAllDetails').attr('checked')) {
242                                 this.selectCheckbox($('#selectAllDetails'), true);
243                         } else {
244                                 this.selectCheckbox($('#selectAllDetails'), false);
245                         }
246                         this.selectAllDetailsEach();
247                 },
248
249                 selectCheckbox: function (obj, state) {
250                         obj.attr('checked', state)
251                                 .data('checkboxradio')
252                                 .refresh();
253                 },
254
255                 /**
256                  * Returns number of selected call logs
257                  * @return {number} length
258                  */
259                 getCountSelectedLogEntries: function Ui_getCountSelectedLogEntries() {
260                         return $('#forCallerList li .toRemove label.ui-checkbox-on').length;
261                 },
262
263                 selectAllDetailsEach: function Ui_selectAllDetailsEach() {
264                         var self = this;
265                         $('#forCallerList').find('input').each(function () {
266                                 if ($('#selectAllDetails').attr('checked')) {
267                                         self.selectCheckbox($(this), true);
268                                 } else {
269                                         self.selectCheckbox($(this), false);
270                                 }
271                         });
272                 },
273
274                 /**
275                  * Hides checkboxes
276                  */
277                 hideCheckboxes: function Ui_hideCheckboxes() {
278                         var self = this;
279                         this.selectCheckbox($('#selectAllDetails'), false);
280
281                         $('#forCallerList').find('input').each(function () {
282                                 self.selectCheckbox($(this), false);
283                         });
284                         this.changeDetailsToRemoveState('hide');
285                 },
286
287                 /**
288                  * Returns css classes for specified entry
289                  *
290                  * @param {CallHistoryEntry} entry
291                  * @returns {string}
292                  */
293                 cssClassesForEntry: function Ui_cssClassesForEntry(entry) {
294                         return 'call dir_' + entry.direction.toLowerCase() + ' type_' + entry.type.replace('.', '-').toLowerCase();
295                 },
296
297                 setSelectAllDetails: function Ui_setSelectAllDetails() {
298                         if ($('#forCallerList input[type="checkbox"]').length ===
299                                 $('#forCallerList input[checked="checked"]').length) {
300                                 this.selectCheckbox($('#selectAllDetails'), true);
301                         } else {
302                                 this.selectCheckbox($('#selectAllDetails'), false);
303                         }
304                 },
305
306                 /**
307                  * Shows popup with specified message
308                  * @param {string} message
309                  */
310                 showPopup: function Ui_showPopup(message) {
311                         $('#popupMessage').html(message);
312                         $('#popup').popup('open', {'positionTo': 'window'});
313                 },
314
315                 /**
316                  * Hides popup
317                  */
318                 closePopup: function Ui_closePopup() {
319                         $('#popup').popup('close');
320                 },
321
322                 showErrorPopup: function Ui_showErrorPopup(message) {
323                         $('#errorPopupMessage').html(message);
324                         $('#errorPopup').popup('open', {'positionTo': 'window'});
325                 },
326
327                 closeErrorPopup: function Ui_closeErrorPopup() {
328                         $('#errorPopup').popup('close');
329                 },
330
331                 /**
332                  * Deletes checked log entries
333                  */
334                 deleteCheckedLogs: function Ui_deleteCheckedLogs(e) {
335                         this.closePopup();
336
337                         this.selectCheckbox($('#selectAllDetails'), false);
338
339                         $('#forCallerList li.call').each(function () {
340                                 if ($(this).find('form label').hasClass('ui-checkbox-on')) {
341                                         app.deleteLog($(this).data('entries')[0]);
342                                         $(this).remove();
343                                 }
344                         });
345
346                         if ($('#forCallerList li.call').length > 0) {
347                                 this.updateCallerHeaderNumberOfEntries($('#forCallerList li.call').length);
348                         } else {
349                                 e.preventDefault();
350                                 $('.ui-listview-filter .ui-input-text').val('');
351                                 $('.ui-listview-filter .ui-input-text').trigger('change');
352                                 $.mobile.changePage('#callView');
353                         }
354
355                         this.changeDetailsToRemoveState(true);
356                         this.scrollToBottom();
357                 },
358
359                 changeDetailsToRemoveState: function Ui_changeDetailsToRemoveState(set) {
360                         var counter = this.getCountSelectedLogEntries();
361                         if (set !== undefined) {
362                                 this.removeMode = false;
363                         } else if (counter === 0) {
364                                 this.removeMode = !this.removeMode;
365                         }
366
367                         if (counter === 0) {
368                                 if (this.removeMode) {
369                                         $('#historyForCallerView .toRemove').removeClass('hidden');
370                                         $('#historyForCallerView .selectAllBox').removeClass('hidden');
371                                 } else {
372                                         $('#historyForCallerView .toRemove').addClass('hidden');
373                                         $('#historyForCallerView .selectAllBox').addClass('hidden');
374                                 }
375                         } else if (counter > 1) {
376                                 this.showPopup('Are you sure you want to delete selected logs?');
377                         } else {
378                                 this.showPopup('Are you sure you want to delete selected log?');
379                         }
380                         this.refreshScrollView();
381                 },
382
383                 /**
384                  * Renders call history list
385                  *
386                  * @param {CallHistoryEntry[]} callEntries
387                  */
388                 showCallHistory: function Ui_showCallHistory(callEntries) {
389                         var self = this,
390                                 pdate = null, // previous date
391                                 date = '',
392                                 elements = [], // list elements
393                                 len = callEntries.length, // entries length
394                                 tempLength = 0, // length of temporary table;
395                                 i, // loop counter
396                                 j, // loop counter
397                                 current, // current entry object
398                                 today = this.helpers.getShortDate(new Date()), // today short date
399                                 entryShortDate,
400                                 filterResult,
401                                 groupsOfDays = [],
402                                 dayLog,
403                                 index = 0;
404
405                         function filterForSameEntry(element) {
406                                 return self.getNumber(current) === self.getNumber(element)
407                                         && current.direction === element.direction;
408                         }
409
410                         $('.selectedCount').hide();
411
412                         for (i = 0; i < len; i = i + 1) {
413                                 current = callEntries[i];
414                                 date = current.startTime.toLocaleDateString();
415
416                                 // if date is changed create new deyLog;
417                                 if (date !== pdate) {
418                                         dayLog = {};
419                                         dayLog.date = date;
420                                         dayLog.entries = [];
421                                         dayLog.counters = [];
422                                         groupsOfDays.push(dayLog);
423                                         pdate = date;
424                                 }
425
426                                 // group entries by remote Party;
427                                 filterResult = dayLog.entries.filter(filterForSameEntry);
428                                 if (filterResult.length) {
429                                         index = dayLog.entries.indexOf(filterResult[0]);
430                                         dayLog.counters[index] += 1;
431                                 } else {
432                                         dayLog.entries.push(current);
433                                         dayLog.counters[dayLog.entries.length - 1] = 1;
434                                 }
435                         }
436                         // Create UL list with dividers;
437                         len = groupsOfDays.length;
438                         for (i = 0; i < len; i += 1) {
439                                 dayLog = groupsOfDays[i];
440                                 tempLength = dayLog.entries.length;
441                                 entryShortDate = this.helpers.getShortDate(dayLog.entries[0].startTime);
442
443                                 // add date header;
444                                 elements.push($(this.templateManager.get('dateRow', {
445                                         'date': today === entryShortDate ? 'Today' : dayLog.date
446                                 })).get(0));
447
448                                 for (j = 0; j < tempLength; j = j + 1) {
449                                         elements.push(this.getCallItemRow(dayLog.entries[j], dayLog.counters[j]));
450                                 }
451                         }
452
453                         $('#calllogList')
454                                 .empty()
455                                 .append(elements);
456
457                         /* workaround for registering call during working app in removeMode */
458                         if (this.removeMode) {
459                                 if ($('#selectAllDetails').attr('checked')){
460                                         this.selectAll();
461                                         $('.toRemove, .selectAllBox', $('#historyForCallerView'))
462                                                 .removeClass('hidden');
463                                 }
464                                 $('#forCallerList .toRemove').show()
465                         }
466
467                         /* workaround for UIFW & webkit scroll*/
468                         $.mobile.activePage.css('min-height', 0);
469                 },
470
471                 /**
472                  * @param: {CallHistoryEntry} entry
473                  */
474                 getNumber: function (entry) {
475                         return entry.remoteParties[0].remoteParty;
476                 },
477
478                 /**
479                  * Returns HTML for single log entry
480                  *
481                  * @param {CallHistoryEntry} entry
482                  * @param {number} counter
483                  * @returns {HTMLPartial}
484                  */
485                 getCallItemRow: function Ui_getCallItemRow(entry, counter) {
486                         var party = entry.remoteParties[0],
487                                 name = this.getNameByNumber(party.remoteParty),
488                                 tpl;
489
490                         if (counter > 1) {
491                                 name += ' (' + counter + ')';
492                         }
493
494                         tpl = this.templateManager.get('callItemRow', {
495                                 'name': name,
496                                 'callTime': entry.startTime.toLocaleTimeString(),
497                                 'cssClasses': this.cssClassesForEntry(entry),
498                                 'uid': entry.uid
499                         });
500
501                         return $(tpl)
502                                 .data('remoteParty', entry.remoteParties[0].remoteParty)
503                                 .data('entries', [entry])
504                                 .get(0); // return clean DOM element so array of those could be appended at once*/
505                 },
506
507                 getNameByNumber: function (number) {
508                         var i, j, contact, name;
509                         for (i in this.contactsLoaded) {
510                                 if (this.contactsLoaded.hasOwnProperty(i)) {
511                                         contact = this.contactsLoaded[i];
512                                         for (j in contact.phoneNumbers) {
513                                                 if (contact.phoneNumbers.hasOwnProperty(j)) {
514                                                         if (contact.phoneNumbers[j].number.substr(-9)
515                                                                         === number.substr(-9)) {
516                                                                 name = contact.name.displayName;
517                                                                 break;
518                                                         }
519                                                 }
520                                         }
521                                 }
522                         }
523                         return name || number;
524                 },
525
526                 /**
527                  * Returns HTML for single caller log entry
528                  *
529                  * @param {CallHistoryEntry} entry
530                  * @returns {HTMLElement}
531                  */
532                 getCallerCallLogRow: function Ui_getCallerCallLogRow(entry) {
533                         return $(this.templateManager.get('callerCallItemRow', {
534                                 'cssClass': this.cssClassesForEntry(entry),
535                                 'callTime': entry.startTime.toLocaleTimeString(),
536                                 'callDuration': this.helpers.secondsToHours(entry.duration),
537                                 'uid': entry.uid
538                         })).data('entries', [entry]).get(0);
539                 },
540
541                 /**
542                  * Renders call log list for specified caller
543                  *
544                  * @param {string} remoteParty
545                  * @param {CallHistoryEntry[]} entries
546                  */
547                 showHistoryForCaller: function Ui_showHistoryForCaller(remoteParty, entries) {
548                         var pdate = '',
549                                 date = '',
550                                 elements = [],
551                                 len = entries.length,
552                                 i;
553
554                         if (len) {
555                                 this.updateCallerHeader(entries[0], entries.length);
556                         } else {
557                                 // if last call log has been removed
558                                 this.removedLastLog();
559                                 this.app.lastViewedCaller = 0;
560                         }
561
562                         $('#forCallerList')
563                                 .data('remoteParty', remoteParty)
564                                 .data('modified', false)
565                                 .empty();
566
567                         // group caller log entries by date
568                         for (i = 0; i < len; i = i + 1) {
569                                 date = entries[i].startTime.toLocaleDateString();
570
571                                 // if date is changed render new header
572                                 if (date !== pdate) {
573                                         elements.push($(this.templateManager.get('dateRow', {'date': date})).get(0));
574                                         pdate = date;
575                                 }
576                                 elements.push(this.getCallerCallLogRow(entries[i]));
577                         }
578
579                         $('#forCallerList')
580                                 .empty()
581                                 .append(elements);
582
583                         if (elements.length > 0) {
584                                 $('li#delete > a').addClass('ui-btn-active');
585                         } else {
586                                 $('li#delete > a').removeClass('ui-btn-active');
587                         }
588
589                         $('#forCallerList').trigger('create');
590
591                         // change to remove mode if it was active before registering call
592                         if (this.removeMode) {
593                                 this.removeMode = !this.removeMode;
594                                 this.changeDetailsToRemoveState();
595                                 // check previous checked entries
596                                 this.checkedLogs.forEach(function(logUid){
597                                         var checkbox = $('#forCallerList li.call[data-uid="' + logUid + '"]')
598                                                 .find(':checkbox');
599                                         checkbox.attr('checked', true)
600                                                 .data('checkboxradio')
601                                                 .refresh();
602                                 });
603                                 this.checkedLogs = [];
604                         }
605                         this.addEventsForCallerListCheckboxes();
606                 },
607
608                 /**
609                  * Update accoundId
610                  * @param {string} accountId
611                  */
612                 updateCallerHeaderAccountId: function Ui_updateCallerHeaderAccountId(accountId) {
613                         $('.infoContainer .accountId').html(accountId);
614                 },
615
616                 /**
617                  * Update number of entries
618                  * @param numberOfEntries
619                  */
620                 updateCallerHeaderNumberOfEntries: function Ui_updateCallerHeaderNumberOfEntries(numberOfEntries) {
621                         $('.infoContainer .numberOfEntries').html(numberOfEntries);
622                 },
623
624                 /**
625                  * Updates caller main info
626                  * @param {CallHistoryEntry} entry
627                  * @param {number} numberOfEntries
628                  */
629                 updateCallerHeader: function Ui_updateCallerHeader(entry, numberOfEntries) {
630                         var name = '', party, imgPath, personId;
631
632                         $('#header .photo').css('background-image', this.photoURIdefault);
633
634                         if (entry.remoteParties !== null) {
635                                 party = entry.remoteParties[0];
636                                 personId = parseInt(party.personId, 10);
637                                 name = this.getNameByNumber(party.remoteParty);
638                                 if (party.displayName) {
639                                         this.updateCallerHeaderAccountId(party.remoteParty);
640                                 }
641                                 this.updateCallerHeaderNumberOfEntries(numberOfEntries);
642                                 if (personId !== 0) {
643                                         imgPath = app.getPhotoURIForContact(personId);
644                                         if (imgPath !== false) {
645                                                 $('#header .photo').css('background-image', 'url(' + imgPath + ')');
646                                         }
647                                 }
648                         } else if (entry.contactId !== null) {
649                                 name = entry.accountId;
650                                 this.updateCallerHeaderAccountId(entry.accountId);
651                                 this.updateCallerHeaderNumberOfEntries(numberOfEntries);
652                         } else {
653                                 name = entry.accountId;
654                                 this.updateCallerHeaderAccountId('');
655                                 this.updateCallerHeaderNumberOfEntries(numberOfEntries);
656                         }
657                         $('.contact > .infoContainer > .name').html(name);
658
659                 },
660
661                 loadContacts: function Model_loadContacts(callback) {
662                         var contactsFoundCB, errorCB;
663
664                         this.contactsLoaded = null;
665
666                         contactsFoundCB = function (contacts) {
667                                 this.contactsLoaded = contacts;
668                                 callback();
669                         };
670
671                         errorCB = function (error) {
672                                 console.error('Model_loadContacts, problem with find() method: ' + error.message);
673                         };
674
675                         this.addressBook.find(contactsFoundCB.bind(this), errorCB);
676                 },
677
678                 /**
679                  * Remove search filter from content and appends it to header
680                  */
681                 removeSearchBarToHeader: function () {
682                         $('#page-header').append($('#callView .ui-listview-filter'));
683                         $.mobile.activePage.page('refresh');
684                         $('.ui-input-cancel').remove(); // patch for WebUI bug;
685                 },
686
687                 scrollToBottom: function () {
688                         var scrollView = $(".ui-scrollview-view");
689                         scrollView.css("-webkit-transform", "translate3d(0px, -" + scrollView.height() + "px, 0px)");
690                 },
691
692                 onVisibilityChange: function () {
693                         var self = this;
694                         document.addEventListener('webkitvisibilitychange', function () {
695                                 if (document.webkitVisibilityState === 'hidden') {
696                                         $('#forCallerList li.call').each(function () {
697                                                 if ($(this).find('form label').hasClass('ui-checkbox-on')) {
698                                                         var checkedEntry = $(this).data('entries')[0];
699                                                         self.checkedLogs.push(checkedEntry.uid);
700                                                 }
701                                         });
702                                 }
703                         });
704                 },
705
706                 /**
707                  * WORKAROUND;
708                  * Patch for UI, bad refresh scrollView
709                  */
710                 refreshScrollView: function () {
711                         var scrollView = $('.ui-scrollview-view'),
712                                 show = function () {
713                                         scrollView.show();
714                                 };
715                         scrollView.hide();
716                         setTimeout(show, 0);
717                 },
718
719                 removedLastLog: function () {
720                         this.hideCheckboxes();
721                         $(".ui-popup").popup('close');
722                         $.mobile.changePage('#callView');
723                 }
724         };
725
726 }());