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