Initial commit of the Phone app 00/17400/2 accepted/tizen/ivi/20140307.031731 submit/tizen/20140307.000454 submit/tizen/20140307.000903
authorbrianjjones <brian.j.jones@intel.com>
Thu, 6 Mar 2014 23:29:50 +0000 (15:29 -0800)
committerbrianjjones <brian.j.jones@intel.com>
Thu, 6 Mar 2014 23:55:00 +0000 (15:55 -0800)
Change-Id: Ief600f67c9b101aa5634858a8606ed48d513d462

19 files changed:
Makefile [new file with mode: 0644]
config.xml [new file with mode: 0644]
css/contacts_library.css [new file with mode: 0644]
css/images/null.png [new file with mode: 0644]
css/phone_style.css [new file with mode: 0644]
data/contacts.json [new file with mode: 0644]
data/history.json [new file with mode: 0644]
data/photos/nophoto.png [new file with mode: 0644]
icon.png [new file with mode: 0644]
index.html [new file with mode: 0644]
js/callhistorycarousel.js [new file with mode: 0644]
js/contacts_library.js [new file with mode: 0644]
js/main.js [new file with mode: 0644]
js/phone.js [new file with mode: 0644]
packaging/html5-ui-phone.changes [new file with mode: 0644]
packaging/html5-ui-phone.spec [new file with mode: 0644]
templates/contactCarouselDelegate.html [new file with mode: 0644]
templates/libraryContactDetailDelegate.html [new file with mode: 0644]
templates/template-contacts.html [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..cb90ad0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+PROJECT = html5UIPhone
+
+VERSION := 0.0.1
+PACKAGE = $(PROJECT)-$(VERSION)
+
+INSTALL_FILES = $(PROJECT).wgt
+INSTALL_DIR = ${DESTDIR}/opt/usr/apps/.preinstallWidgets
+
+wgtPkg:
+       cp -r ${DESTDIR}/opt/usr/apps/_common/js/services js/
+       cp -r ${DESTDIR}/opt/usr/apps/_common/css/* css/
+       zip -r $(PROJECT).wgt config.xml css data icon.png index.html js templates
+
+install:
+       @echo "Installing Phone, stand by..."
+       mkdir -p $(INSTALL_DIR)/
+       cp $(PROJECT).wgt $(INSTALL_DIR)/
+
+dist:
+       tar czf ../$(PACKAGE).tar.bz2 .
diff --git a/config.xml b/config.xml
new file mode 100644 (file)
index 0000000..4de9d2a
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<widget xmlns="http://www.w3.org/ns/widgets" xmlns:tizen="http://tizen.org/ns/widgets" id="http://com.intel.tizen/phone" version="0.5.0" viewmodes="fullscreen">
+    <tizen:application id="html5POC09.Phone" package="html5POC09" required_version="2.1"/>
+    <icon src="icon.png"/>
+    <content src="index.html"/>
+    <name>Phone</name>
+    <tizen:privilege name="http://tizen.org/privilege/tizen"/>
+    <tizen:privilege name="http://tizen.org/privilege/application.launch"/>
+    <tizen:privilege name="http://tizen.org/privilege/filesystem.read"/>
+    <tizen:privilege name="http://tizen.org/privilege/filesystem.write"/>
+    <tizen:privilege name="http://tizen.org/privilege/contact.read"/>
+    <tizen:privilege name="http://tizen.org/privilege/contact.write"/>
+    <tizen:privilege name="http://tizen.org/privilege/content.read" />
+    <tizen:privilege name="http://tizen.org/privilege/speech" />
+    <tizen:privilege name="http://tizen.org/privilege/bluetooth.admin" />
+    <tizen:privilege name="http://tizen.org/privilege/bluetooth.spp" />
+    <tizen:privilege name="http://tizen.org/privilege/bluetooth.gap" />
+</widget>
diff --git a/css/contacts_library.css b/css/contacts_library.css
new file mode 100644 (file)
index 0000000..5cee618
--- /dev/null
@@ -0,0 +1,216 @@
+.contactsLibraryContentList .contactElement {
+       padding: 10px;
+       height: 70px;
+       box-shadow: none;
+       border-bottom-style: solid;
+       border-bottom-width: 2px;
+       overflow: hidden;
+       white-space: nowrap;
+       cursor: pointer;
+}
+
+.phoneIcon {
+       background-repeat: no-repeat;
+       background-position: center center;
+       width: 44px;
+       height: 44px;
+       display: inline-block;
+}
+
+.emailIcon {
+       background-repeat: no-repeat;
+       background-position: center center;
+       width: 44px;
+       height: 44px;
+       display: inline-block;
+}
+
+.addressIcon {
+       background-repeat: no-repeat;
+       background-position: center center;
+       width: 44px;
+       height: 44px;
+       display: inline-block;
+}
+
+.contactsLibraryContentList .phoneIcon {
+       padding-top: 23px;
+}
+
+.contactsLibraryContentList .contactPhotoBox {
+       width: 70px;
+       height: 70px;
+       display: inline-block;
+       margin-left: 22px;
+       background-position: center center;
+       background-repeat: no-repeat;
+       background-size: 100% 100%;
+}
+
+.contactsLibraryContentList .contactPhoto {
+       width: 100%;
+       height: 100%;
+}
+
+.contactsLibraryContentList .contactName {
+       display: inline-block;
+       vertical-align: top;
+       padding-left: 18px;
+       line-height: 70px;
+       background-color: transparent !important;
+       text-transform: uppercase;
+}
+
+.contactsLibraryContentGrid {
+       line-height: 0;
+}
+
+.contactsLibraryContentGrid .contactElement {
+       width: 180px;
+       height: 180px;
+       display: inline-block;
+       margin-right: 7px;
+       margin-bottom: 10px;
+       cursor: pointer;
+}
+
+.contactsLibraryContentGrid .phoneIcon {
+       display: none;
+}
+
+.contactsLibraryContentGrid .contactPhotoBox {
+       width: 100%;
+       height: 100%;
+       background-size: 100% 100%;
+}
+
+.contactsLibraryContentGrid .contactPhoto {
+       width: 100%;
+       height: 100%;
+}
+
+.contactsLibraryContentGrid .contactName {
+       position: relative;
+       padding-left: 10px;
+       padding-right: 10px;
+       padding-top: 10px;
+       top: -80px;
+       left: 0;
+       height: 70px;
+       overflow: hidden;
+       text-transform: uppercase;
+}
+
+.contactDetail {
+       margin-top: 48px;
+}
+
+.contactDetailBox1 {
+       margin-left: 45px;
+       width: 120px;
+       display: inline-block;
+       vertical-align: top;
+}
+
+.contactDetailPhotoBox {
+       width: 120px;
+       height: 120px;
+       display: block;
+       background-position: center center;
+       background-repeat: no-repeat;
+       background-size: 100% 100%;
+}
+
+.contactDetailPhoto {
+       width: 100%;
+       height: 100%;
+}
+
+.contactDetailFavorite {
+       width: 48px;
+       height: 48px;
+       margin: 30px auto 0 auto;
+       background-position: center center;
+       background-repeat: no-repeat;
+       background-size: 100% 100%;
+}
+
+.contactDetailBox2 {
+       display: inline-block;
+       vertical-align: top;
+       margin-left: 53px;
+       width: 385px;
+}
+
+.contactDetailBox3 {
+       margin-bottom: 30px;
+}
+
+
+.contactDetailBox4 {
+       margin-left: 15px;
+       vertical-align: top;
+       display: inline-block;
+       width: 320px;
+}
+
+.missedCallIcon {
+       background-repeat: no-repeat;
+       background-position: center center;
+       width: 42px;
+       height: 36px;
+       display: inline-block;
+}
+
+.missedNewCallIcon {
+       background-repeat: no-repeat;
+       background-position: center center;
+       width: 42px;
+       height: 36px;
+       display: inline-block;
+}
+
+.receivedCallIcon {
+       background-repeat: no-repeat;
+       background-position: center center;
+       width: 42px;
+       height: 36px;
+       display: inline-block;
+}
+
+.dialedCallIcon {
+       background-repeat: no-repeat;
+       background-position: center center;
+       width: 42px;
+       height: 36px;
+       display: inline-block;
+}
+
+.callHistoryBox {
+       margin-top: 20px;
+       border-top-style: solid;
+       border-top-width: 2px;
+}
+
+.callHistoryElement {
+       padding: 18px 0 18px 0;
+       box-shadow: none;
+       border-bottom-style: solid;
+       border-bottom-width: 2px;
+       overflow: hidden;
+       white-space: nowrap;
+}
+
+.callHistoryIcon {
+       padding-top: 8px;
+       margin-left: 20px;
+}
+.callHistoryIconGen {
+       top: -10px;
+       background-size: 70%;
+}
+
+.callHistoryDetails {
+       display: inline-block;
+       vertical-align: top;
+}
diff --git a/css/images/null.png b/css/images/null.png
new file mode 100644 (file)
index 0000000..841863c
Binary files /dev/null and b/css/images/null.png differ
diff --git a/css/phone_style.css b/css/phone_style.css
new file mode 100644 (file)
index 0000000..fe5cf59
--- /dev/null
@@ -0,0 +1,277 @@
+.phoneBody {
+       width: 720px;
+       margin: 0;
+       min-height: 1280px;
+       height: auto !important;
+       height: 100%;
+       background-position: center top;
+       background-repeat: no-repeat;
+       background-size: 100% 100%;
+}
+
+.inputPhoneNumberBox {
+       padding-top: 195px;
+       padding-left: 190px;
+}
+
+.inputPhoneNumber {
+       width: 215px;
+       height: 75px;
+       line-height: 75px;
+       padding-right: 90px;
+       padding-left: 30px;
+       border: none;
+       background-color: rgb(0, 0, 0);
+       background-color: rgba(0, 0, 0, 0.2);
+}
+
+.inputPhoneNumber:focus {
+       outline: none;
+}
+
+.inputPhoneNumberBox .deleteButton {
+       display: inline-block;
+       vertical-align: top;
+       margin: 18px 0 0 -73px;
+       padding: 0;
+       width: 55px;
+       height: 36px;
+       background-position: center center;
+       background-repeat: no-repeat;
+       cursor: pointer;
+}
+.noPairedDevice{
+       top:130px;
+       left: 200px;
+       margin-top: 20px;
+       width:100%;
+       height:100%;
+}
+.loadingSpinnerHistory {
+       top:130px;
+       width:100%;
+       height:100%;
+}
+.numbersBox {
+       margin-top: 7px;
+       display: inline-block;
+       width: 430px;
+       font-size: 0;
+       margin-bottom: 44px;
+}
+
+.numberButton {
+       float: left;
+       padding: 0;
+       padding-top: 20px;
+       width: 105px;
+       height: 80px;
+       margin-right: 10px;
+       margin-bottom: 10px;
+       background-color: rgb(0, 0, 0);
+       background-color: rgba(0, 0, 0, 0.2);
+       text-transform: uppercase;
+}
+
+.numberButtonLetters {
+       margin-top: -3px;
+}
+
+.specialChar {
+       padding-top: 30px;
+       height: 70px;
+}
+
+.contactsCarousel {
+       margin: 0;
+       padding: 0;
+       list-style: none;
+       display: block;
+}
+
+.contactsCarousel li {
+       display: block;
+       float: left;
+}
+
+.carouselBox {
+       width: 240px;
+       height: 315px;
+       margin: 0 15px 20px 0;
+       white-space: normal;
+       background-color: rgb(0, 0, 0);
+       background-color: rgba(0, 0, 0, 0.2);
+       box-shadow: 0 2px 30px rgba(0, 0, 0, .7);
+       border-width: 1px;
+       border-style: solid;
+       text-transform: uppercase;
+}
+
+.carouselEdgeBox {
+       width: 240px;
+       height: 315px;
+       margin: 0 15px 20px 0;
+       white-space: normal;
+       visibility: hidden;
+}
+
+.carouselPhotoArea {
+       margin: 5px;
+       text-align: center;
+       border-style: solid;
+       border-width: 0;
+       border-bottom-width: 2px;
+}
+
+.carouselPhotoBox {
+       margin: 32px auto 28px auto;
+       width: 120px;
+       height: 120px;
+       background-position: center center;
+       background-repeat: no-repeat;
+       background-size: 100% 100%;
+}
+
+.carouselPhoto {
+       width: 100%;
+       height: 100%;
+}
+
+.carouselInfoBox {
+       width: 205px;
+       text-align: left;
+       white-space: nowrap;
+       overflow: hidden;
+       margin: 0 auto;
+}
+
+.carouselCallContact {
+       margin-top: 15px;
+       margin-bottom: -5px;
+}
+
+.carouselName {
+       margin-top: 5px;
+       margin-bottom: 10px;
+}
+
+.carouselNumber {
+       background-repeat: no-repeat;
+       background-position: left center;
+       width: 160px;
+       padding-left: 45px;
+       line-height: 38px;
+       cursor: pointer;
+}
+
+.callBox {
+       position: absolute;
+       margin: 0;
+       margin-left: 61px;
+       top: 770px;
+       width: 720px;
+       height: 160px;
+       z-index: 100;
+       -webkit-transition-timing-function: linear;
+       -webkit-transition: left 1s;
+       -moz-transition-timing-function: linear;
+       -moz-transition: left 1s;
+       -ms-transition-timing-function: linear;
+       -ms-transition: left 1s;
+       -o-transition-timing-function: linear;
+       -o-transition: left 1s
+}
+
+.callBoxShow {
+       left: 0;
+}
+
+.callBoxHidden {
+       left: 720px;
+}
+
+.callInfoBox {
+       width: 300px;
+       vertical-align: top;
+       white-space: nowrap;
+       overflow: hidden;
+       display: inline-block;
+}
+
+.callPhotoBox {
+       width: 120px;
+       height: 120px;
+       display: inline-block;
+       background-position: center center;
+       background-repeat: no-repeat;
+       background-size: 100% 100%;
+}
+
+.callPhoto {
+       width: 100%;
+       height: 100%;
+}
+
+.callDuration {
+       margin-left: 60px;
+       display: inline-block;
+       vertical-align: top;
+       padding-top: 46px;
+}
+
+.inCallWith {
+       margin-top: 11px;
+}
+
+.callName {
+       margin-top: 5px;
+       text-transform: uppercase;
+}
+
+.callControlsBox {
+       width: 328px;
+       display: inline-block;
+       vertical-align: top;
+       margin-top: 5px;
+       margin-left: 13px;
+       font-size: 0;
+}
+
+.controlButton {
+       display: inline-block;
+       padding: 70px 0 13px 0;
+       width: 140px;
+       margin-right: 6px;
+       margin-bottom: 8px;
+       background-repeat: no-repeat;
+       background-position: center 15px;
+       box-shadow: 10px 10px 100px rgba(0, 0, 0, .7);
+}
+
+#callVolumeControl {
+       margin-left: 0;
+       margin-top: 25px;
+       width: 600px;
+       height: 55px;
+       top: 20px;
+}
+
+.contactsCarouselBox {
+       width: 765px;
+       position: absolute;
+       top: 770px;
+       left: -16px;
+       padding: 0;
+       margin: 0;
+       display: block;
+       transition: left 1s;
+       -webkit-transition: left 1s;
+}
+
+.contactsCarouselBoxShow {
+       left: -16px;
+}
+
+.contactsCarouselBoxHide {
+       left: -765px;
+}
\ No newline at end of file
diff --git a/data/contacts.json b/data/contacts.json
new file mode 100644 (file)
index 0000000..5859fdf
--- /dev/null
@@ -0,0 +1,202 @@
+{
+    "contacts": [{
+        "id": "F926BBEC-9FE7-4360-831B-814A03DAC655",
+        "lastUpdated": "2012-10-05T12:05:57.386Z",
+        "name": {
+            "prefix": "",
+            "firstName": "Erick",
+            "middleName": "",
+            "lastName": "Briar",
+            "nicknames": ["ebi"],
+            "phoneticName": "",
+            "displayName": null
+        },
+        "account": null,
+        "addresses": [],
+        "photoURI": "",
+        "phoneNumbers": [{
+            "number": "+123 422 789",
+            "types": []
+        }],
+        "emails": [{
+            "email": "erick@hgost.com",
+            "types": []
+        }],
+        "birthday": null,
+        "anniversaries": null,
+        "organization": null,
+        "notes": [],
+        "urls": [],
+        "isFavorite": true,
+        "ringtoneURI": null,
+        "categories": []
+    },
+    {
+        "id": "F926BBEC-9FE7-4360-831B-814A03DAC632",
+        "lastUpdated": "2012-10-05T12:05:57.386Z",
+        "name": {
+            "prefix": "",
+            "firstName": "Gilbert",
+            "middleName": "",
+            "lastName": "Ted",
+            "nicknames": ["gil"],
+            "phoneticName": "",
+            "displayName": null
+        },
+        "account": null,
+        "addresses": [],
+        "photoURI": "",
+        "phoneNumbers": [{
+            "number": "6548 523 574",
+            "types": []
+        }],
+        "emails": [{
+            "email": "ted@qqaa.com",
+            "types": []
+        }],
+        "birthday": null,
+        "anniversaries": null,
+        "organization": null,
+        "notes": [],
+        "urls": [],
+        "isFavorite": false,
+        "ringtoneURI": null,
+        "categories": []
+    },
+    {
+        "id": "F926BBEC-9FE7-4360-831B-814A03DAC631",
+        "lastUpdated": "2012-10-05T12:05:57.386Z",
+        "name": {
+            "prefix": "",
+            "firstName": "Ivor",
+            "middleName": "",
+            "lastName": "Kodey",
+            "nicknames": ["ivo"],
+            "phoneticName": "",
+            "displayName": null
+        },
+        "account": null,
+        "addresses": [],
+        "photoURI": "",
+        "phoneNumbers": [{
+            "number": "1234 555 666",
+            "types": []
+        }],
+        "emails": [{
+            "email": "ivor@srbhufc.net",
+            "types": []
+        }],
+        "birthday": null,
+        "anniversaries": null,
+        "organization": null,
+        "notes": [],
+        "urls": [],
+        "isFavorite": false,
+        "ringtoneURI": null,
+        "categories": []
+    },
+    {
+        "id": "A27F67A5-8879-49D4-9312-31510F018405",
+        "lastUpdated": "2012-10-05T09:03:27.781Z",
+        "name": {
+            "prefix": "",
+            "firstName": "Jonty",
+            "middleName": "",
+            "lastName": "Dana",
+            "nicknames": ["jodan"],
+            "phoneticName": "",
+            "displayName": null
+        },
+        "account": null,
+        "addresses": [],
+        "photoURI": "",
+        "phoneNumbers": [{
+            "number": "9659 421 777",
+            "types": []
+        }],
+        "emails": [{
+            "email": "jodan@lamxyx.org",
+            "types": []
+        }],
+        "birthday": null,
+        "anniversaries": null,
+        "organization": null,
+        "notes": [],
+        "urls": [],
+        "isFavorite": false,
+        "ringtoneURI": null,
+        "categories": []
+    },
+    {
+        "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B",
+        "lastUpdated": "2012-10-05T13:23:26.862Z",
+        "name": {
+            "prefix": "",
+            "firstName": "Brad",
+            "middleName": "",
+            "lastName": "Rob",
+            "nicknames": ["brob"],
+            "phoneticName": "",
+            "displayName": null
+        },
+        "account": null,
+        "addresses": [{
+            "country": "Kasabanan",
+            "region": "",
+            "city": "",
+            "streetAddress": "Crane 12",
+            "additionalInformation": "",
+            "postalCode": "478152",
+            "types": []
+        }],
+        "photoURI": "",
+        "phoneNumbers": [{
+            "number": "3456 124 231",
+            "types": []
+        }],
+        "emails": [{
+            "email": "rob@klatog.tr",
+            "types": []
+        }],
+        "birthday": null,
+        "anniversaries": null,
+        "organization": null,
+        "notes": [],
+        "urls": [],
+        "isFavorite": true,
+        "ringtoneURI": null,
+        "categories": []
+    },
+    {
+        "id": "6C8394CE-9CA5-4FB3-A97C-4D93BEFC5559",
+        "lastUpdated": "2012-10-05T12:05:46.783Z",
+        "name": {
+            "prefix": "",
+            "firstName": "Derick",
+            "middleName": "",
+            "lastName": "Jojo",
+            "nicknames": ["dejo"],
+            "phoneticName": "",
+            "displayName": null
+        },
+        "account": null,
+        "addresses": [],
+        "photoURI": "",
+        "phoneNumbers": [{
+            "number": "0123 456 865",
+            "types": []
+        }],
+        "emails": [{
+            "email": "dejo@waxxos.py",
+            "types": []
+        }],
+        "birthday": null,
+        "anniversaries": null,
+        "organization": null,
+        "notes": [],
+        "urls": [],
+        "isFavorite": false,
+        "ringtoneURI": null,
+        "categories": []
+    }]
+}
\ No newline at end of file
diff --git a/data/history.json b/data/history.json
new file mode 100644 (file)
index 0000000..690d613
--- /dev/null
@@ -0,0 +1,313 @@
+{
+       "history": [{
+               "serviceId": 1350052304188,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B",
+                       "displayName": "Brad Rob",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-12T14:31:44.188Z",
+               "duration": 0,
+               "endReason": "local",
+               "direction": "missed-new",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1350052299533,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "displayName": "Brad Rob",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-12T14:31:39.533Z",
+               "duration": 0,
+               "endReason": "rejected",
+               "direction": "dialed",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1349787460224,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B",
+                       "displayName": "Brad Rob",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-09T12:57:40.225Z",
+               "duration": 0,
+               "endReason": "remote",
+               "direction": "missed-new",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1349787449337,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B",
+                       "displayName": "Brad Rob",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-09T12:57:29.337Z",
+               "duration": 0,
+               "endReason": "local",
+               "direction": "dialed",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1349787440709,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "id": "A27F67A5-8879-49D4-9312-31510F018405",
+                       "displayName": "Jonty Dana",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-09T12:57:20.709Z",
+               "duration": 0,
+               "endReason": "local",
+               "direction": "dialed",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1349787435870,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "id": "F926BBEC-9FE7-4360-831B-814A03DAC631",
+                       "displayName": "Ivor Kodey",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-09T12:57:15.870Z",
+               "duration": 0,
+               "endReason": "local",
+               "direction": "dialed",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1349778842662,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "displayName": "Ivor Kodey",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-09T10:34:02.662Z",
+               "duration": 3849,
+               "endReason": "local",
+               "direction": "dialed",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1349771235969,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "displayName": "Albert Test",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-09T08:27:15.969Z",
+               "duration": 0,
+               "endReason": "local",
+               "direction": "dialed",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1349449039585,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B",
+                       "displayName": "Brad Rob",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-05T14:57:19.585Z",
+               "duration": 3007,
+               "endReason": "remote",
+               "direction": "received",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1349449026162,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B",
+                       "displayName": "Brad Rob",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-05T14:57:06.162Z",
+               "duration": 3879,
+               "endReason": "local",
+               "direction": "dialed",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1349449011962,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "id": "F926BBEC-9FE7-4360-831B-814A03DAC631",
+                       "displayName": "Ivor Kodey",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-05T14:56:51.962Z",
+               "duration": 2992,
+               "endReason": "local",
+               "direction": "dialed",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1349448097026,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "id": "F926BBEC-9FE7-4360-831B-814A03DAC631",
+                       "displayName": "Ivor Kodey",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-05T14:41:37.026Z",
+               "duration": 2792,
+               "endReason": "remote",
+               "direction": "received",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1350052304188,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B",
+                       "displayName": "Brad Rob",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-12T14:31:44.188Z",
+               "duration": 0,
+               "endReason": "local",
+               "direction": "missed-new",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1350052299533,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "displayName": "Brad Rob",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-12T14:31:39.533Z",
+               "duration": 0,
+               "endReason": "rejected",
+               "direction": "dialed",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       },
+       {
+               "serviceId": 1349787460224,
+               "callType": "tizen.tel",
+               "tags": ["call.voice",
+               "call.video"],
+               "callParticipants": [{
+                       "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B",
+                       "displayName": "Brad Rob",
+                       "contactRef": null
+               }],
+               "forwardedFrom": {
+
+               },
+               "startTime": "2012-10-09T12:57:40.225Z",
+               "duration": 0,
+               "endReason": "remote",
+               "direction": "missed-new",
+               "recording": [],
+               "cost": 0,
+               "currency": null
+       }]
+}
\ No newline at end of file
diff --git a/data/photos/nophoto.png b/data/photos/nophoto.png
new file mode 100644 (file)
index 0000000..3f812de
Binary files /dev/null and b/data/photos/nophoto.png differ
diff --git a/icon.png b/icon.png
new file mode 100644 (file)
index 0000000..5d8de5d
Binary files /dev/null and b/icon.png differ
diff --git a/index.html b/index.html
new file mode 100644 (file)
index 0000000..6e8b606
--- /dev/null
@@ -0,0 +1,182 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<meta charset="utf-8" />
+<meta name="viewport" content="width=720, height=1280, user-scalable=no" />
+<meta name="description" content="Phone application" />
+<title>Phone application</title>
+
+<!--  jquery and plugins  -->
+<script type="text/javascript" src="./css/car/components/jQuery/jquery-1.8.2.js"></script>
+<script type="text/javascript" src="./css/car/components/knockout/knockout.js"></script>
+<script type="text/javascript" src="./css/car/components/jsViews/jsrender.js"></script>
+<script type="text/javascript" src="./css/car/components/jsViews/template.js"></script>
+<script type="text/javascript" src="./css/car/components/carousel/jquery.carouFredSel-6.2.1-packed.js"></script>
+<script type="text/javascript" src="./css/car/components/carousel/jquery.touchSwipe.min.js"></script>
+<script type="text/javascript" src="./css/car/components/dateTime/moment.min.js"></script>
+
+<!-- user and car theme, theme engine, ...  -->
+<link rel="stylesheet" href="./css/car/car.css" />
+<link rel="stylesheet" href="./css/car/components/incomingCall/incomingCall.css" />
+
+
+<link rel="stylesheet" href="./css/car/car.css" />
+<script type="text/javascript" src='./css/car/car.js'></script>
+<script type='text/javascript' src='./js/services/bootstrap.js'></script>
+
+<script type='text/javascript' src='./css/car/components/dateTime/dateTime.js'></script>
+<link rel="stylesheet" href="./css/car/components/dateTime/dateTime.css" />
+<script type='text/javascript' src='./css/car/components/boxCaption/boxCaption.js'></script>
+<link rel="stylesheet" href="./css/car/components/boxCaption/boxCaption.css" />
+<script type="text/javascript" src='./css/car/components/alphabetBookmark/alphabetBookmark.js'></script>
+<link rel="stylesheet" href="./css/car/components/alphabetBookmark/alphabetBookmark.css" />
+<script type="text/javascript" src='./css/car/components/library/library.js'></script>
+<link rel="stylesheet" href="./css/car/components/library/library.css" />
+<script type='text/javascript' src='./css/car/components/topBarIcons/topBarIcons.js'></script>
+<link rel="stylesheet" href="./css/car/components/topBarIcons/topBarIcons.css" />
+<script type="text/javascript" src="./css/car/components/bottomPanel/bottomPanel.js"></script>
+<script type='text/javascript' src='./css/car/components/progressBar/progressBar.js'></script>
+<link rel="stylesheet" href="./css/car/components/progressBar/progressBar.css" />
+<link rel="stylesheet" href="./css/car/components/progressBar/volumeSlider.css" />
+
+<script type="text/javascript" src="./js/main.js"></script>
+<script type="text/javascript" src="./js/callhistorycarousel.js"></script>
+<script type="text/javascript" src="./js/phone.js"></script>
+<script type="text/javascript" src="./js/contacts_library.js"></script>
+<script type="text/javascript" src="./css/car/components/jQuery/jquery.nouisliderix.js"></script>
+
+<link rel="stylesheet" type="text/css" href="./css/phone_style.css" />
+<link rel="stylesheet" type="text/css" href="./css/contacts_library.css" />
+
+</head>
+
+<body class="phoneBody">
+       <div id="contactsLibraryButton"
+               class="button libraryButton fontSizeSmaller fontWeightBold fontColorNormal">A-Z</div>
+       <div id="clockElement"></div>
+
+       <div class="inputPhoneNumberBox">
+               <input id="inputPhoneNumber"
+                       class="inputPhoneNumber fontSizeLarge fontSizeBold fontColorTheme boxShadow4 boxShadow4Active"
+                       type="text" />
+               <div id="deleteButton" class="button deleteButton"></div>
+       </div>
+
+       <div id="callButton"
+               class="button callButton callingFalse boxShadow4Active"></div>
+
+       <div class="numbersBox">
+               <div id="numberButton" data-id="1"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active">1</div>
+               <div id="numberButton" data-id="2"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active">
+                       2
+                       <div
+                               class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">ABC</div>
+               </div>
+               <div id="numberButton" data-id="3"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active">
+                       3
+                       <div
+                               class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">DEF</div>
+               </div>
+               <div id="numberButton" data-id="4"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active">
+                       4
+                       <div
+                               class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">GHI</div>
+               </div>
+               <div id="numberButton" data-id="5"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active">
+                       5
+                       <div
+                               class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">JKL</div>
+               </div>
+               <div id="numberButton" data-id="6"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active">
+                       6
+                       <div
+                               class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">MNO</div>
+               </div>
+               <div id="numberButton" data-id="7"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active">
+                       7
+                       <div
+                               class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">PQRS</div>
+               </div>
+               <div id="numberButton" data-id="8"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active">
+                       8
+                       <div
+                               class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">TUV</div>
+               </div>
+               <div id="numberButton" data-id="9"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active">
+                       9
+                       <div
+                               class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">WXYZ</div>
+               </div>
+               <div id="numberButton" data-id="*"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorTheme boxShadow4 boxShadow4Active specialChar">*</div>
+               <div id="numberButton" data-id="0"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active">
+                       0
+                       <div
+                               class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">+</div>
+               </div>
+               <div id="numberButton" data-id="#"
+                       class="button numberButton fontSizeXLarge fontWeightBold fontColorTheme boxShadow4 boxShadow4Active specialChar">#</div>
+       </div>
+
+       <div id="contactsCarouselBox" class= "contactsCarouselBox">
+               <div id="loadingHistorySpinnerWrapper" class="loadingSpinnerHistory" style="display: none; ">
+                       <div id="loadingSpinner" class="loadingSpinner ">
+                               <div id="loadingSpinnerContent" class="loading-container">
+                               <div id="loadingSpinnerImg" class="loading"></div>
+                                       <div id="loadingHistorySpinnerText" class="loading-text fontSizeXXSmall fontWeightBold fontColorNormal">DOWNLOADING HISTORY</div>
+                               </div>
+               </div>
+               </div>
+               <div id = "noPairedDevice" class = "noPairedDevice fontSizeXLarge fontWeightBold fontColorNormal" style="display: none; ">NO BT DEVICE SELECTED</div>
+               <ul id="contactsCarousel" class="contactsCarousel"></ul>
+       </div>
+
+       <div id="callBox" class="callBox callBoxHidden">
+               <div class="callInfoBox">
+                       <div class="callPhotoBox noContactPhoto">
+                               <img id="callPhoto" class="callPhoto" />
+                       </div>
+                       <div id="callDuration"
+                               class="callDuration fontSizeLarge fontWeightBold fontColorNormal">00:00</div>
+                       <div id="inCallWith" class="inCallWith"></div>
+                       <div id="callName"
+                               class="callName fontSizeXLarge fontWeightBold fontColorNormal"></div>
+                       <div id="callNumber"
+                               class="callNumber fontSizeSmall fontWeightBold fontColorTheme"></div>
+               </div>
+               <div class="callControlsBox">
+                       <div
+                               class="button controlButton fontSizeSmaller fontColorNormal fontWeightBold bgColorDark boxShadow4Active speakerButton">SPEAKER</div>
+                       <div
+                               class="button controlButton fontSizeSmaller fontColorNormal fontWeightBold bgColorDark boxShadow4Active muteButton">MUTE</div>
+                       <div
+                               class="button controlButton fontSizeSmaller fontColorNormal fontWeightBold bgColorDark boxShadow4Active holdButton">HOLD</div>
+                       <div
+                               class="button controlButton fontSizeSmaller fontColorNormal fontWeightBold bgColorDark boxShadow4Active addCallButton">ADD
+                               CALL</div>
+               </div>
+               <div id="callVolumeControl">
+                       <div id = "VCicon"></div>
+                       <div class = "sliderStart"></div>
+                       <div id = "VCline" class =  "bgColorTheme"></div>
+                       <div id = "VCinner" class =  "bgColorTheme boxShadow3"></div>
+                       <div class="noVolumeSlider"></div>
+               </div>
+       </div>
+
+       <div id="topBarIcons"></div>
+       <div id="bottomPanel" class="bottomPanel bottomPanelImg"></div>
+       <div id="library" class="library pageBgColorNormalTransparent"></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/js/callhistorycarousel.js b/js/callhistorycarousel.js
new file mode 100644 (file)
index 0000000..9306bb9
--- /dev/null
@@ -0,0 +1,268 @@
+/*global Phone, callContactCarousel*/
+
+/**
+ */
+ /**
+ * This class provides methods to operate with call history carousel. It wrapps handling CarouFredSel object and provides
+ * following operations:
+ *
+ * * Show placed, received and missed phone calls ordered by date of phone call
+ * * For each call displays phone number, contact name, date and time of phone call
+ *
+ * @module PhoneApplication
+ * @class Carousel
+ * @constructor
+ */
+var Carousel = function() {
+       "use strict";
+       this.initializeSwipe();
+};
+/**
+* This property holds call history data array for show in html.
+* @property callHistory {Array}
+*/
+Carousel.prototype.callHistory = [];
+/**
+* This property holds swipe object for internal use in carousel.
+* @property swipe {Object}
+* @private
+*/
+Carousel.prototype.swipe = null;
+/**
+* This property holds callback function which is called after current element in carousel is changed.
+* @property indexChangeCallback {Object}
+* @private
+*/
+Carousel.prototype.indexChangeCallback = null;
+/**
+ * This method adds listener for current carousel element change.
+ *
+ * @method addIndexChangeListener
+ * @param indexChangeCallback {function()} Callback function called after current carousel element changed.
+ */
+Carousel.prototype.addIndexChangeListener = function(indexChangeCallback) {
+       "use strict";
+
+       this.indexChangeCallback = indexChangeCallback;
+};
+/**
+ * This method initializes and configures carousel object
+ *
+ * @method initializeSwipe
+ * @private
+ */
+Carousel.prototype.initializeSwipe = function() {
+       "use strict";
+
+       var self = this;
+       if (!this.swipe) {
+               this.swipe = $('#contactsCarousel').carouFredSel({
+                       auto : false,
+                       circular : false,
+                       infinite : false,
+                       width : 765,
+                       items : {
+                               visible : 3
+                       },
+                       swipe : {
+                               items : 1,
+                               duration : 150,
+                               onMouse : true,
+                               onTouch : true
+                       },
+                       scroll : {
+                               items : 1,
+                               duration : 150,
+                               onAfter : function(data) {
+                                       if (!!self.indexChangeCallback) {
+                                               self.indexChangeCallback(self.getCurrentPosition());
+                                       }
+                               }
+                       }
+               });
+               if (!this.swipe.length) {
+                       this.swipe = null;
+               }
+       }
+};
+/**
+ * This method provides the index of current selected item of the carousel.
+ *
+ * @method getCurrentPosition
+ * @return Current {Integer} index position in carousel.
+ */
+Carousel.prototype.getCurrentPosition = function() {
+       "use strict";
+       var self = this;
+       if (!!self.swipe) {
+               var pos = parseInt(self.swipe.triggerHandler("currentPosition"), 10);
+               return pos;
+       }
+       return null;
+};
+/**
+ * This method moves current position of the carousel to the given index.
+ *
+ * @method slideTo
+ * @param index {Integer}  New index position in carousel
+ */
+Carousel.prototype.slideTo = function(index) {
+       "use strict";
+       if (!!this.swipe && index >= 0 && index < this.callHistory.length) {
+               this.swipe.trigger("slideTo", index);
+       }
+};
+/**
+ * This method fills carousel with call history data and resets its current position to start.
+ *
+ * @method loadCallHistory
+ * @param  callHistory {Array} Call history array.
+ * @param  index {Integer} New index position in carousel.
+ */
+Carousel.prototype.loadCallHistory = function(callHistory, index) {
+       "use strict";
+       this.removeAllItems();
+       this.callHistory = callHistory;
+       this.insertPagesToSwipe();
+       if (index >= 0 && index < this.callHistory.length && !!this.swipe) {
+               this.swipe.trigger("slideTo", [ index, 0, {
+                       duration : 0
+               } ]);
+       }
+};
+/**
+ * This method creates one carousel item for swipe.
+ *
+ * @method createSwipeItem
+ * @param  callHistory {Array} Call history array.
+ * @param  index {Integer} Carousel item index it is use as div id in html.
+ * @return {String} New carousel item as html string.
+ * @private
+ */
+Carousel.prototype.createSwipeItem = function(callHistoryEntry, index) {
+       "use strict";
+       var self = this;
+
+       if (!!callHistoryEntry) {
+               var carouselItem;
+
+               var contact = null;
+               if (!!callHistoryEntry.remoteParties && callHistoryEntry.remoteParties.length) {
+                       contact = Phone.getContactByPersonId(callHistoryEntry.remoteParties[0].personId);
+               }
+
+               var id = "";
+               var name = "";
+               var photoURI = "";
+               var phoneNumber = "";
+               var startTime = callHistoryEntry.startTime || "";
+               var direction = callHistoryEntry.direction || "";
+
+               if (!!callHistoryEntry.remoteParties && callHistoryEntry.remoteParties.length) {
+                       // personId = phoneNumber
+                       phoneNumber = callHistoryEntry.remoteParties[0].personId;
+               }
+
+               if (!!contact) {
+                       if (!!contact.id) {
+                               id = contact.id;
+                       }
+                       if (!!contact.photoURI) {
+                               photoURI = contact.photoURI;
+                       }
+                       if (phoneNumber === "" && !!contact.phoneNumbers && contact.phoneNumbers.length) {
+                               phoneNumber = contact.phoneNumbers[0].number;
+                       }
+                       name = Phone.getDisplayNameStr(contact);
+               }
+
+               if (name === "") {
+                       name = "Unknown";
+               }
+
+               carouselItem = '<li>';
+               carouselItem += '<div id="carouselBox_' + index + '" class="carouselBox borderColorTheme" data-id="' + id + '">';
+               carouselItem += '<div class="carouselPhotoArea borderColorTheme">';
+               carouselItem += '<div class="carouselPhotoBox noContactPhoto">';
+               carouselItem += '<img class="carouselPhoto" src="' + photoURI + '" /></div></div>';
+               carouselItem += '<div class="carouselInfoBox carouselName fontSizeLarger fontWeightBold fontColorNormal">' + name + '</div>';
+               carouselItem += '<div class="carouselInfoBox carouselNumber fontSizeSmall fontWeightBold fontColorTheme">' + phoneNumber + '</div>';
+               carouselItem += '<div class="callHistoryElement borderColorTheme">';
+               carouselItem += '<div class="missedNewCallIcon callHistoryIcon callHistoryIconGen"></div>';
+               carouselItem += '<div class="callHistoryDetails">';
+               carouselItem += '<div class="fontSizeXXSmall fontColorNormal">' + startTime + '</div>';
+               carouselItem += '<div class="fontSizeXXSmall fontWeightBold fontColorTheme">' + direction + '</div></div></div></div>';
+               carouselItem += '</li>';
+
+               carouselItem = $(carouselItem);
+               carouselItem.data("callhistory", callHistoryEntry);
+               carouselItem.data("contact", contact);
+               carouselItem.click(function() {
+                       self.swipe.trigger("slideTo", [ $(this), -1 ]);
+                       var hystoryEntry = $(this).data("callhistory");
+                       var contactEntry = $(this).data("contact");
+                       var contact = {
+                               name : {
+                                       displayName : contactEntry.name.displayName,
+                                       firstName : contactEntry.name.firstName,
+                                       lastName : contactEntry.name.lastName
+                               },
+                               photoURI : contactEntry.photoURI,
+                               phoneNumbers : [ {
+                                       number : hystoryEntry.remoteParties[0].personId
+                               } ]
+
+                       };
+                       callContactCarousel(contact);
+               });
+               return carouselItem;
+       }
+
+       return null;
+};
+/**
+ * This method inserts pages whit carousel elements to swipe.
+ *
+ * @method insertPagesToSwipe
+ * @private
+ */
+Carousel.prototype.insertPagesToSwipe = function() {
+       "use strict";
+       var self = this;
+       var carouselItem;
+       for ( var index = this.callHistory.length - 1; index >= 0; --index) {
+               carouselItem = this.createSwipeItem(this.callHistory[index], index);
+               if (!!carouselItem && !!this.swipe) {
+                       this.swipe.trigger("insertItem", [ carouselItem, 0 ]);
+               }
+       }
+       this.addCarouselEdges();
+};
+/**
+ * This method removes all item from carousel.
+ *
+ * @method removeAllItems
+ */
+Carousel.prototype.removeAllItems = function() {
+       "use strict";
+       var carouselItem;
+
+       if (!!this.swipe) {
+               for ( var index = this.callHistory.length + 1; index >= 0; --index) {
+                       this.swipe.trigger("removeItem", index);
+               }
+       }
+};
+/**
+ * This method adds emty carousel items to the beginning and the end of the carousel
+ * (to make sure first and last visible items appear in the middle of screen instead of at the edges when swiped to edges of carousel).
+ * @method addCarouselEdges
+ */
+Carousel.prototype.addCarouselEdges = function() {
+       "use strict";
+       if (!!this.swipe) {
+               var html = "<li><div class='carouselEdgeBox'></div></li>";
+               this.swipe.trigger("insertItem", [ html, 0 ]);
+               this.swipe.trigger("insertItem", [ html, "end", true ]);
+       }
+};
diff --git a/js/contacts_library.js b/js/contacts_library.js
new file mode 100644 (file)
index 0000000..172f879
--- /dev/null
@@ -0,0 +1,210 @@
+/*global Phone, callContactCarousel, GRID_TAB, LIST_TAB, loadTemplate, ko*/
+
+/**
+ * Class which provides methods to operate with contacts library which displays all contact information (name, phone number, photo) from paired device ordered by name.
+ *
+ * @class ContactsLibrary
+ * @module PhoneApplication
+ */
+var ContactsLibrary = {
+       currentSelectedContact : "",
+       /**
+        * Method initializes contacts library.
+        *
+        * @method init
+        */
+       init : function() {
+               "use strict";
+               $('#library').library("setSectionTitle", "PHONE CONTACTS");
+               $('#library').library("init");
+
+               var tabMenuModel = {
+                       Tabs : [ {
+                               text : "CONTACTS A-Z",
+                               selected : true
+                       } ]
+               };
+
+               $('#library').library("tabMenuTemplateCompile", tabMenuModel);
+
+               $('#library').bind('eventClick_GridViewBtn', function() {
+                       ContactsLibrary.showContacts();
+               });
+
+               $('#library').bind('eventClick_ListViewBtn', function() {
+                       ContactsLibrary.showContacts();
+               });
+
+               $('#library').bind('eventClick_SearchViewBtn', function() {
+               });
+
+               $('#library').bind('eventClick_menuItemBtn', function() {
+                       ContactsLibrary.showContacts();
+               });
+
+               $('#library').bind('eventClick_closeSubpanel', function() {
+               });
+
+               $("#alphabetBookmarkList").on("letterClick", function(event, letter) {
+                       console.log(letter);
+                       Phone.contactsAlphabetFilter(letter === "*" ? "" : letter);
+               });
+
+               ContactsLibrary.showContacts();
+       },
+       /**
+        * Method unhides library page.
+        *
+        * @method show
+        */
+       show : function() {
+               "use strict";
+               $('#library').library("showPage");
+       },
+       /**
+        * Method hides library page.
+        *
+        * @method hide
+        */
+       hide : function() {
+               "use strict";
+               $('#library').library("hidePage");
+       },
+       /**
+        * Method opens contact detail.
+        *
+        * @method openContactDetail
+        * @param contact
+        *            {Object} Object representing contact's information.
+        */
+       openContactDetail : function(contact) {
+               "use strict";
+               if (!!contact) {
+                       ContactsLibrary.currentSelectedContact  = contact;
+                       var history = Phone.getCallHistoryByPersonId(contact.personId);
+                       var formattedContact = ContactsLibrary.initContactDetail(contact);
+                       formattedContact.history = history;
+                       ContactsLibrary.renderContactDetailView(formattedContact);
+               } else {
+                       console.log("Supplied contact is null.");
+               }
+       },
+       /**
+        * Method renders search view.
+        *
+        * @method renderContactDetailView
+        * @param contact
+        *            {Object} Contact object.
+        */
+       renderContactDetailView : function(contact) {
+               "use strict";
+               console.log("open contact called");
+               var subpanelModel = {
+                       textTitle : "CONTACT",
+                       textSubtitle : contact.name || "Unknown",
+                       actionName : "BACK",
+                       action : function() {
+                               console.log("back clicked");
+                               ContactsLibrary.showContacts();
+                               ContactsLibrary.currentSelectedContact = "";
+                       }
+               };
+               $('#library').library("subpanelContentTemplateCompile", subpanelModel);
+               $('#library').library("clearContent");
+               $('#library').library("setContentDelegate", "templates/libraryContactDetailDelegate.html");
+               $('#library').library("contentTemplateCompile", contact, "contactDetail", function() {
+                       $("#contactDetailMobileTitle").boxCaptionPlugin('initSmall', "MOBILE");
+                       $("#contactDetailEmailTitle").boxCaptionPlugin('initSmall', "EMAIL");
+                       $("#contactDetailAddressTitle").boxCaptionPlugin('initSmall', "ADDRESS");
+               });
+       },
+       /**
+        * Method which shows contacts in grid or list view.
+        *
+        * @method showContacts
+        */
+       showContacts : function() {
+               "use strict";
+               console.log("show contacts called");
+               var view = "";
+               switch ($('#library').library('getSelectetLeftTabIndex')) {
+               case GRID_TAB:
+                       view = "contactsLibraryContentGrid";
+                       break;
+               case LIST_TAB:
+                       view = "contactsLibraryContentList";
+                       break;
+               default:
+                       view = "contactsLibraryContentList";
+                       break;
+               }
+               $('#library').library('closeSubpanel');
+               $('#library').library("clearContent");
+               $('#library').library("changeContentClass", view);
+               loadTemplate("templates/", "template-contacts", function() {
+                       var contactsElement = '<div data-bind="template: { name: \'template-contacts\', foreach: Phone.contactsComputed }"></div>';
+                       $(contactsElement).appendTo($('.' + view));
+                       ko.applyBindings(Phone);
+               });
+       },
+       /**
+        * Method which initializes contact detail.
+        *
+        * @method initContactDetail
+        * @param contact
+        *            {Object} Contact object.
+        */
+       initContactDetail : function(contact) {
+               "use strict";
+               var tempContact = {
+                       id : "",
+                       name : "",
+                       phoneNumber : "",
+                       email : "",
+                       photoURI : "",
+                       address : "",
+                       isFavorite : false,
+                       history : []
+               };
+
+               if (!!contact) {
+                       var str = "";
+
+                       if (!!contact.uid) {
+                               tempContact.id = contact.uid;
+                       }
+
+                       if (!!contact.name) {
+                               tempContact.name = Phone.getDisplayNameStr(contact);
+                       }
+
+                       if (!!contact.phoneNumbers && contact.phoneNumbers.length && !!contact.phoneNumbers[0].number) {
+                               tempContact.phoneNumber = contact.phoneNumbers[0].number.trim();
+                       }
+
+                       if (!!contact.emails && contact.emails.length && !!contact.emails[0].email) {
+                               tempContact.email = contact.emails[0].email.trim();
+                       }
+
+                       if (!!contact.photoURI) {
+                               tempContact.photoURI = contact.photoURI.trim();
+                       }
+
+                       if (!!contact.addresses && contact.addresses.length) {
+                               str = !!contact.addresses[0].streetAddress ? contact.addresses[0].streetAddress.trim() + "<br />" : "";
+                               str += !!contact.addresses[0].city ? contact.addresses[0].city.trim() + "<br />" : "";
+                               str += !!contact.addresses[0].country ? contact.addresses[0].country.trim() + "<br />" : "";
+                               str += !!contact.addresses[0].postalCode ? contact.addresses[0].postalCode.trim() : "";
+
+                               if (str.toString().trim() === "") {
+                                       str = "-";
+                               }
+
+                               tempContact.address = str.trim();
+                       }
+
+                       tempContact.isFavorite = contact.isFavorite;
+               }
+               return tempContact;
+       }
+};
diff --git a/js/main.js b/js/main.js
new file mode 100644 (file)
index 0000000..e6d9826
--- /dev/null
@@ -0,0 +1,809 @@
+/*global disconnectCall, Bootstrap, Carousel, ContactsLibrary, getAppByID, disconnectCall, Configuration, Speech, Phone, changeCssBgImageColor, ThemeKeyColorSelected */
+
+/**
+ * This application provides voice call from paired Bluetooth phone. Application uses following APIs:
+ *
+ * * {{#crossLink "Phone"}}{{/crossLink}} library
+ * * [tizen.bt]() as replacement of [tizen.bluetooth](https://developer.tizen.org/dev-guide/2.2.0/org.tizen.web.device.apireference/tizen/bluetooth.html) API due to
+ *   conficts in underlying framework
+ *
+ * Application supports multiple connected devices, however only one of the devices can be selected at the time.
+ * Selection is done from {{#crossLink "Bluetooth"}}{{/crossLink}} UI. In case that phone has active call additional {{#crossLink "Carousel"}}{{/crossLink}}
+ * element is replaced by {{#crossLink "CallDuration"}}{{/crossLink}} element.
+ *
+ * Application allows following operations:
+ *
+ * * {{#crossLink "Phone/acceptCall:method"}}Place call{{/crossLink}}
+ * * Handles incoming calls passed from {{#crossLink "IncomingCall"}}{{/crossLink}} widget
+ * * Display {{#crossLink "Carousel"}}call history{{/crossLink}}
+ * * Display {{#crossLink "ContactsLibrary"}}contact list{{/crossLink}}
+ * * Mute/unmute call - not working due to [TIVI-2448](https://bugs.tizen.org/jira/browse/TIVI-2448)
+ *
+ * Additionaly application can be controlled using speech recognition via {{#crossLink "Speech"}}{{/crossLink}} component.
+ *
+ * Hover and click on elements in images below to navigate to components of Phone application.
+ *
+ * <img id="Image-Maps_1201312180420487" src="../assets/img/phone.png" usemap="#Image-Maps_1201312180420487" border="0" width="649" height="1152" alt="" />
+ *   <map id="_Image-Maps_1201312180420487" name="Image-Maps_1201312180420487">
+ *     <area shape="rect" coords="0,0,573,78" href="../classes/TopBarIcons.html" alt="top bar icons" title="Top bar icons" />
+ *     <area shape="rect" coords="0,77,644,132" href="../classes/Clock.html" alt="clock" title="Clock"    />
+ *     <area shape="rect" coords="0,994,644,1147" href="../classes/BottomPanel.html" alt="bottom panel" title="Bottom panel" />
+ *     <area shape="rect" coords="573,1,644,76" href="../modules/Settings.html" alt="Settings" title="Settings"    />
+ *     <area shape="rect" coords="552,136,646,181" href="../classes/ContactsLibrary.html" alt="Contacts library" title="Contacts library" />
+ *     <area shape="rect" coords="95,345,164,491" href="../classes/Phone.html#method_acceptCall" alt="Call button" title="Call button" />
+ *     <area shape="rect" coords="1,668,644,984" href="../classes/Carousel.html" alt="" title="History carousel" />
+ *     <area shape="rect" coords="171,181,471,635" alt=""   href="../classes/keyboard.html" alt="Keyboard input" title="Keyboard input"    >
+ *   </map>
+ *
+ * @module PhoneApplication
+ * @main PhoneApplication
+ * @class Phone
+ */
+
+/**
+ * Holds object of input for dialing phone number.
+ *
+ * @property telInput {Object}
+ */
+var telInput;
+/**
+ * Instance of class Bootstrap, this class provides unified way to boot up the HTML applications by loading shared components in proper order.
+ * * {{#crossLink "Bootstrap"}}{{/crossLink}}
+ *
+ * @property bootstrap {Object}
+ */
+var bootstrap;
+
+/**
+* Instance of class Carousel, this class provides methods to operate with hystory carousel.
+* * {{#crossLink "Carousel"}}{{/crossLink}}
+*
+* @property callHistoryCarousel {Object}
+*/
+var callHistoryCarousel = null;
+
+/**
+* This property holds information about accept Phone call from Other widgets.
+* If is true, phone call came from another widget.
+* @property acceptPhoneCallFromOtherWidget {Boolean}
+*/
+var acceptPhoneCallFromOtherWidget = false;
+
+/**
+ * Class handling user input from keyboard
+ *
+ * @class keyboard
+ * @static
+ */
+var keyboard = {
+    /**
+     * property holding Interval within which next click on the same key is considered as rotating associated characters with the given key.
+     *
+     * @property clickInterval {int}
+     */
+    clickInterval: 1000,
+
+    /**
+     * property holding info about last pressed key
+     *
+     * @property pressedKey {string}
+     */
+    pressedKey: "-1",
+
+    /**
+     * Array of input object holding info about main key character, all associated characters with the given key and index of currently used key
+     *
+     * @property inputs {Array of Object}
+     */
+    inputs: [{
+        key: "1",
+        values: ["1"],
+        index: 0
+    }, {
+        key: "2",
+        values: ["2", "A", "B", "C"],
+        index: 0
+    }, {
+        key: "3",
+        values: ["3", "D", "E", "F"],
+        index: 0
+    }, {
+        key: "4",
+        values: ["4", "G", "H", "I"],
+        index: 0
+    }, {
+        key: "5",
+        values: ["5", "J", "K", "L"],
+        index: 0
+    }, {
+        key: "6",
+        values: ["6", "M", "N", "O"],
+        index: 0
+    }, {
+        key: "7",
+        values: ["7", "P", "Q", "R", "S"],
+        index: 0
+    }, {
+        key: "8",
+        values: ["8", "T", "U", "V"],
+        index: 0
+    }, {
+        key: "9",
+        values: ["9", "W", "X", "Y", "Z"],
+        index: 0
+    }, {
+        key: "*",
+        values: ["*"],
+        index: 0
+    }, {
+        key: "0",
+        values: ["0", "+"],
+        index: 0
+    }, {
+        key: "#",
+        values: ["#"],
+        index: 0
+    }],
+
+    /**
+     * property holding time of clickedInterval started
+     *
+     * @property startedTime {Date}
+     */
+    startedTime: null,
+
+    /**
+     * property holding input object associated with last pressed key
+     *
+     * @property selectedInput {Object}
+     */
+    selectedInput: null,
+
+    /**
+     * function starting clickInterval
+     *
+     * @method startTimer
+     */
+    startTimer: function() {
+        "use strict";
+        keyboard.startedTime = new Date();
+    },
+
+    /**
+     * function testing if clickInterval expired
+     *
+     * @method intervalExpired
+     * @param currTime {Date}
+     */
+    intervalExpired: function(currTime) {
+        "use strict";
+        if (currTime - keyboard.startedTime > keyboard.clickInterval) {
+            keyboard.resetIndices();
+            return true;
+        }
+        return false;
+    },
+
+    /**
+     * function reseting indices for all input objects to 0
+     *
+     * @method resetIndices
+     */
+    resetIndices: function() {
+        "use strict";
+        for (var i in keyboard.inputs) {
+            if (keyboard.inputs.hasOwnProperty(i)) {
+                keyboard.inputs[i].index = 0;
+            }
+        }
+    },
+
+    /**
+     * function cycling associated input characters within clickInterval
+     *
+     * @method nextKey
+     */
+    nextKey: function() {
+        "use strict";
+        for (var i in keyboard.inputs) {
+            if (keyboard.pressedKey === keyboard.inputs[i].key) {
+                if (keyboard.inputs[i].values.length > 1) {
+                    if (keyboard.inputs[i].index < keyboard.inputs[i].values.length - 1) {
+                        keyboard.inputs[i].index += 1;
+                    } else {
+                        keyboard.inputs[i].index = 0;
+                    }
+                } else {
+                    keyboard.inputs[i].index = 0;
+                }
+                keyboard.selectedInput = keyboard.inputs[i];
+                return keyboard.inputs[i].values[keyboard.inputs[i].index];
+            }
+        }
+        return keyboard.pressedKey;
+    },
+
+    /**
+     * function setting selected input object based on last pressed key
+     *
+     * @method selectInput
+     */
+    selectInput: function() {
+        "use strict";
+        for (var i in keyboard.inputs) {
+            if (keyboard.pressedKey === keyboard.inputs[i].key) {
+                keyboard.selectedInput = keyboard.inputs[i];
+            }
+        }
+    }
+};
+
+/**
+ * Holds status of calling panel initialization.
+ *
+ * @property callingPanelInitialized {Boolean}  if is true, calling panel is initialized
+ * @default false
+ */
+var callingPanelInitialized = false;
+
+/**
+ * Class which provides initialize call info.
+ *
+ * @method initializeCallInfo
+ * @param contact {Object} Contact object.
+ * @for Phone
+ */
+function initializeCallInfo(contact) {
+    "use strict";
+    var callNumber;
+    console.log(contact);
+    if ( !! contact) {
+
+        if ( !! contact.name) {
+            var nameStr;
+            if (contact.name.displayName) {
+                nameStr = contact.name.displayName;
+            } else {
+                nameStr = !! contact.name.firstName ? contact.name.firstName : "";
+                nameStr += !! contact.name.lastName ? " " + contact.name.lastName : "";
+            }
+            $("#callName").html(nameStr.trim());
+        } else {
+            $("#callName").html("Unknown");
+        }
+
+        if ( !! contact.phoneNumbers && contact.phoneNumbers.length) {
+            callNumber = !! contact.phoneNumbers[0].number ? contact.phoneNumbers[0].number : "";
+            $("#callNumber").html(callNumber);
+        } else {
+            $("#callNumber").html("Unknown");
+        }
+
+        if ( !! contact.photoURI) {
+            $("#callPhoto").attr("src", contact.photoURI);
+        }
+    } else {
+        $("#callName").html("Unknown");
+        $("#callNumber").html("Unknown");
+    }
+
+    if (!callingPanelInitialized) {
+        $(".noVolumeSlider").noUiSlider({
+            range: [0, 100],
+            step: 1,
+            start: 50,
+            handles: 1,
+            connect: "lower",
+            orientation: "horizontal",
+            slide: function() {
+                var VolumeSlider = parseInt($(".noVolumeSlider").val(), 10);
+                console.log("noVolumeSlider" + VolumeSlider);
+            }
+        });
+        callingPanelInitialized = true;
+    }
+
+    if ($("#callButton").hasClass("callingFalse")) {
+        $("#callButton").removeClass("callingFalse");
+        $("#callButton").addClass("callingTrue");
+    }
+}
+
+/**
+ * Class which provides methods to operate with call duration. Component show information about current call (call number or call contact, time duration of call).
+ *
+ * @class CallDuration
+ * @static
+ */
+var CallDuration = {
+    /**
+     * Holds value of seconds.
+     *
+     * @property sec {Integer}
+     */
+    sec: 0,
+    /**
+     * Holds value of minutes.
+     *
+     * @property min {Integer}
+     */
+    min: 0,
+    /**
+     * Holds value of hours.
+     *
+     * @property hour {Integer}
+     */
+    hour: 0,
+    /**
+     * Holds object of timer.
+     *
+     * @property timeout {Object}
+     */
+    timeout: null,
+    /**
+     * Method provides initialization of call timers.
+     *
+     * @method initialize
+     */
+    startWatch: function() {
+        "use strict";
+        var self = this;
+        if (!this.timeout) {
+            this.timeout = window.setInterval(function() {
+                self.stopwatch();
+            }, 1000);
+        } else {
+            this.resetIt();
+            this.timeout = window.setInterval(function() {
+                self.stopwatch();
+            }, 1000);
+        }
+    },
+
+    /**
+     * Method provides reset call timers.
+     *
+     * @method resetIt
+     */
+    resetIt: function() {
+        "use strict";
+        CallDuration.sec = 0;
+        CallDuration.min = 0;
+        CallDuration.hour = 0;
+        window.clearTimeout(CallDuration.timeout);
+        var callStatus = tizen.phone.activeCall.state.toLowerCase();
+        if (callStatus === "DIALING".toLowerCase()) {
+            $("#callDuration").html("DIALING");
+        } else if (callStatus === "DISCONNECTED".toLowerCase()) {
+            $("#callDuration").html("ENDED");
+        } else {
+            $("#callDuration").html(
+                ((CallDuration.min <= 9) ? "0" + CallDuration.min : CallDuration.min) + ":" + ((CallDuration.sec <= 9) ? "0" + CallDuration.sec : CallDuration.sec));
+        }
+
+    },
+    /**
+     * Method provides call stop watch.
+     *
+     * @method stopwatch
+     */
+    stopwatch: function() {
+        "use strict";
+
+        var callStatus = tizen.phone.activeCall.state.toLowerCase();
+        if (callStatus === "DIALING".toLowerCase()) {
+            $("#callDuration").html("DIALING");
+        } else if (callStatus === "DISCONNECTED".toLowerCase()) {
+            $("#callDuration").html("ENDED");
+
+        } else {
+            CallDuration.sec++;
+            if (CallDuration.sec === 60) {
+                CallDuration.sec = 0;
+                CallDuration.min++;
+            }
+
+            if (CallDuration.min === 60) {
+                CallDuration.min = 0;
+                CallDuration.hour++;
+            }
+            $("#callDuration").html(
+                ((CallDuration.min <= 9) ? "0" + CallDuration.min : CallDuration.min) + ":" + ((CallDuration.sec <= 9) ? "0" + CallDuration.sec : CallDuration.sec));
+        }
+    }
+};
+
+/**
+* This property holds information about mute of call. If is true, phone call is mute.
+*
+* @property VolumeMuteStatus
+* @default false
+*/
+var VolumeMuteStatus = false;
+
+/**
+ * Class provides a muting of a call
+ *
+ * @method muteCall
+ * @for Phone
+ */
+function muteCall() {
+    "use strict";
+    VolumeMuteStatus = VolumeMuteStatus ? false : true;
+
+    // Not working due to TIVI-2448
+    if (tizen.phone) {
+        //tizen.phone.muteCall(VolumeMuteStatus);
+    }
+    if (VolumeMuteStatus) {
+        changeCssBgImageColor(".muteButton", ThemeKeyColorSelected);
+        $(".muteButton").addClass("fontColorSelected");
+    } else {
+        changeCssBgImageColor(".muteButton", "#FFFFFF");
+        $(".muteButton").removeClass("fontColorSelected");
+    }
+}
+
+/**
+ * Class which provides methods to call contact.
+ *
+ * @method acceptCall
+ * @param contact {Object} Contact object.
+ * @for Phone
+ */
+function acceptCall(contact) {
+    "use strict";
+
+    ContactsLibrary.hide();
+    if ($("#settingsTabs").tabs) {
+        $("#settingsTabs").tabs("hidePage");
+    }
+    $("#callBox").removeClass("callBoxHidden");
+    $("#callBox").addClass("callBoxShow");
+    $('#contactsCarouselBox').removeClass("contactsCarouselBoxShow");
+    $('#contactsCarouselBox').addClass("contactsCarouselBoxHide");
+    if (tizen.phone) {
+        CallDuration.resetIt();
+
+        initializeCallInfo(contact);
+        var callStatus = tizen.phone.activeCall.state.toLowerCase();
+        if (callStatus !== "ACTIVE".toLowerCase() && callStatus !== "DIALING".toLowerCase()) {
+
+            if (callStatus === "INCOMING".toLowerCase()) {
+                tizen.phone.answerCall(function(result) {
+                    console.log(result.message);
+                });
+            } else if (callStatus === "DISCONNECTED".toLowerCase()) {
+
+                var callNumber = contact.phoneNumbers[0] && contact.phoneNumbers[0].number ? contact.phoneNumbers[0].number : "";
+                tizen.phone.invokeCall(callNumber, function(result) {
+                    console.log(result.message);
+                });
+
+            }
+
+        } else if (callStatus === "ACTIVE".toLowerCase()) {
+            CallDuration.startWatch();
+        }
+    }
+}
+
+/**
+ * Class which provides disconnect call.
+ *
+ * @method disconnectCall
+ * @for Phone
+ */
+function disconnectCall() {
+    "use strict";
+    $("#callButton").removeClass("callingTrue");
+    $("#callButton").addClass("callingFalse");
+    if (acceptPhoneCallFromOtherWidget !== true) {
+        $("#callBox").removeClass("callBoxShow");
+        $("#callBox").addClass("callBoxHidden");
+        $('#contactsCarouselBox').removeClass("contactsCarouselBoxHide");
+        $('#contactsCarouselBox').addClass("contactsCarouselBoxShow");
+    }
+    CallDuration.resetIt();
+    if (tizen.phone) {
+        tizen.phone.hangupCall(function(result) {
+            console.log(result.message);
+        });
+    }
+    $("#inputPhoneNumber").val('');
+}
+
+$(document).ready(
+    function() {
+        "use strict";
+        setTimeout(function() {
+            /* initialize phone widget by remote device status */
+            if (tizen.phone) {
+                tizen.phone.getSelectedRemoteDevice(function(selectedRemoteDevice){
+                if (selectedRemoteDevice !== "") {
+                    $("#noPairedDevice").hide();
+                    $("#loadingHistorySpinnerWrapper").show();
+                } else {
+                    $("#noPairedDevice").show();
+                }
+                });
+                /* initialize phone widget by active call status */
+                var callStatus = tizen.phone.activeCall.state.toLowerCase();
+                if (callStatus === "INCOMING".toLowerCase() || callStatus === "DIALING".toLowerCase() || callStatus === "ACTIVE".toLowerCase()) {
+                    var contact;
+                    if (tizen.phone.callState) {
+                        contact = tizen.phone.activeCall.contact;
+                    }
+                    acceptPhoneCallFromOtherWidget = true;
+                    acceptCall(contact);
+                } else if (callStatus === "DISCONNECTED".toLowerCase()) {
+                    disconnectCall();
+                }
+            }
+            /* start keyboard timer */
+            keyboard.startTimer();
+            /* initialize bootstrap */
+            bootstrap = new Bootstrap(function(status) {
+                telInput = $("#inputPhoneNumber");
+                $("#clockElement").ClockPlugin('init', 5);
+                $("#clockElement").ClockPlugin('startTimer');
+                $("#topBarIcons").topBarIconsPlugin('init', 'phone');
+                $('#bottomPanel').bottomPanel('init');
+                if (!callHistoryCarousel) {
+
+                    callHistoryCarousel = new Carousel();
+                }
+
+                if (typeof Phone !== "undefined") {
+                    /* add listener to selected remote device */
+                    tizen.phone.addRemoteDeviceSelectedListener(function(returnID) {
+                        if ((!!returnID && !!returnID.error) || (!!returnID && !!returnID.value && returnID.value === "")) {
+                            $("#loadingHistorySpinnerWrapper").hide();
+                            $(".caroufredsel_wrapper").hide();
+                            $("#noPairedDevice").show();
+                        } else {
+                            $("#noPairedDevice").hide();
+                            $("#loadingHistorySpinnerWrapper").show();
+                            $(".caroufredsel_wrapper").show();
+                        }
+                    });
+                    /* initialize contacts and call history, if not accept phone call from another widget */
+                    if (acceptPhoneCallFromOtherWidget !== true) {
+                        window.setTimeout(function() {
+                            Phone.loadContacts(function(err) {
+                                if (!err) {
+                                    ContactsLibrary.init();
+                                    Phone.loadCallHistory(function(err) {
+                                        if (!err) {
+                                            $("#loadingHistorySpinnerWrapper").hide();
+                                            callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
+                                        }
+                                    });
+                                }
+                            });
+
+                        }, 2000);
+                    }
+                    /* add listener to change contacts list  */
+                    tizen.phone.addContactsChangedListener(function() {
+                        if (acceptPhoneCallFromOtherWidget !== true) {
+                            window.setTimeout(function() {
+                                Phone.loadContacts(function(err) {
+                                    if (!err) {
+                                        ContactsLibrary.init();
+                                    }
+                                });
+                            }, 1000);
+                        }
+                    });
+                    /* add listener to change call history  */
+                    tizen.phone.addCallHistoryChangedListener(function() {
+                        $("#loadingHistorySpinnerWrapper").show();
+                        if (acceptPhoneCallFromOtherWidget !== true) {
+                            window.setTimeout(function() {
+                                Phone.loadCallHistory(function(err) {
+                                    if (!err) {
+                                        $("#loadingHistorySpinnerWrapper").hide();
+                                        callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
+
+                                    }
+                                });
+                            }, 1000);
+                        }
+
+                    });
+                }
+
+                $("#contactsLibraryButton").bind('click', function() {
+
+                    ContactsLibrary.show();
+
+                });
+
+                $(".numbersBox").delegate("#numberButton", "click", function() {
+                    var pressTime = new Date(),
+                        number, oneCharPX = 32;
+                    if (keyboard.intervalExpired(pressTime)) {
+                        number = telInput.attr("value") + $(this).data("id");
+                        telInput.attr("value", number);
+                        $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
+                        keyboard.pressedKey = $(this).data("id").toString();
+
+                    } else {
+                        if (keyboard.pressedKey === "-1" || keyboard.pressedKey !== $(this).data("id").toString()) {
+                            number = telInput.attr("value") + $(this).data("id");
+                            telInput.attr("value", number);
+                            $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
+                            keyboard.pressedKey = $(this).data("id").toString();
+                        } else {
+                            var phoneNumText = telInput.attr("value");
+                            if (keyboard.pressedKey === $(this).data("id").toString() && keyboard.selectedInput !== null && keyboard.selectedInput.values.length === 1) {
+                                number = telInput.attr("value") + $(this).data("id");
+                                telInput.attr("value", number);
+                                $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
+                            } else {
+                                var numToUpdate = phoneNumText.slice(0, phoneNumText.length - 1);
+                                numToUpdate += keyboard.nextKey();
+                                telInput.attr("value", numToUpdate);
+
+                            }
+                        }
+                    }
+                    keyboard.selectInput();
+                    keyboard.startTimer();
+                    return false;
+                });
+
+                $(".inputPhoneNumberBox").delegate("#deleteButton", "click", function() {
+                    var number = telInput.attr("value");
+                    number = number.slice(0, number.length - 1);
+                    telInput.attr("value", number);
+                    return false;
+                });
+
+                $('#callButton').bind('click', function() {
+                    var phoneNumber = $("#inputPhoneNumber").val();
+                    if ($("#callBox").hasClass("callBoxShow")) {
+                        disconnectCall();
+                    } else if (phoneNumber !== "") {
+                        var contact = Phone.getContactByPhoneNumber(phoneNumber);
+                        if (contact === null) {
+
+                            contact = {
+                                phoneNumbers: [{
+                                    number: phoneNumber
+                                }]
+                            };
+
+                        }
+                        acceptCall(contact);
+                    }
+                });
+                $('.muteButton').bind('click', function() {
+                    muteCall();
+                });
+                if (tizen.phone) {
+                    /* add listener to change call history entry, because if call is ended tizen.phone give back only last history object */
+                    tizen.phone.addCallHistoryEntryAddedListener(function(contact) {
+                        if (acceptPhoneCallFromOtherWidget !== true) {
+
+
+                            var tmpCallHistory = Phone.callHistory();
+                            var tmpContact = [];
+                            tmpContact.push(contact);
+                            tmpContact = Phone.formatCallHistory(tmpContact);
+                            tmpCallHistory.unshift(tmpContact[0]);
+                            Phone.callHistory(tmpCallHistory);
+
+                            callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
+
+                        }
+                    });
+                    /* add listener to change call state */
+                    tizen.phone.addCallChangedListener(function(result) {
+                        var contact;
+                        if ( !! result.contact.name) {
+                            contact = result.contact;
+                        } else {
+                            contact = {
+                                phoneNumbers: [{
+                                    /* jshint camelcase: false */
+                                    number: tizen.phone.activeCall.line_id
+                                    /* jshint camelcase: true */
+                                }]
+
+                            };
+                        }
+
+                        console.log("result.state " + result.state);
+
+                        switch (result.state.toLowerCase()) {
+                            case "DISCONNECTED".toLowerCase():
+
+                                disconnectCall(contact);
+
+                                if (acceptPhoneCallFromOtherWidget === true) {
+
+                                    window.setTimeout(function() {
+                                        if (typeof tizen !== "undefined") {
+                                            tizen.application.getCurrentApplication().exit();
+                                        }
+                                    }, 1000);
+                                }
+
+                                Configuration.set("acceptedCall", "false");
+
+                                break;
+                            case "ACTIVE".toLowerCase():
+                                if (Configuration._values.acceptedCall !== "true") {
+                                    /* global self */
+                                    self.incomingCall.acceptIncommingCall();
+                                    CallDuration.startWatch();
+                                    console.log("phone active");
+                                    Configuration.set("acceptedCall", "true");
+                                }
+                                break;
+                            case "DIALING".toLowerCase():
+                                acceptCall(contact);
+                                break;
+                        }
+                    });
+                }
+
+            if (typeof(Speech) !== 'undefined') {
+                /* add listener to voice recognition */
+                Speech.addVoiceRecognitionListener({
+                    oncall: function() {
+                        if (ContactsLibrary.currentSelectedContact !== "" && $('#library').library("isVisible")) {
+                            acceptCall(ContactsLibrary.currentSelectedContact);
+                        }
+                    }
+                });
+            } else {
+                console.warn("Speech API is not available.");
+            }
+
+            });
+        }, 0);
+
+    });
+
+/**
+ * Class which provides call contact carousel.
+ *
+ * @method callContactCarousel
+ * @param contact {Object} Contact object.
+ * @for Phone
+ *
+ */
+
+function callContactCarousel(contact) {
+    "use strict";
+
+    acceptCall(contact);
+}
+
+/**
+ * Class which provides call by contact ID.
+ *
+ * @method callContactById
+ * @param contactId {String} Contact id.
+ * @for Phone
+ */
+function callContactById(contactId) {
+    "use strict";
+    $("#contactDetailMobile").addClass("fontColorSelected ");
+    if (contactId !== "" && typeof contactId !== undefined) {
+
+        var contactObject = Phone.getContactById(contactId);
+        if ( !! contactObject) {
+            window.setTimeout(function() {
+                acceptCall(contactObject);
+                $("#contactDetailMobile").removeClass("fontColorSelected ");
+            }, 500);
+        } else {
+            console.log("contact not found");
+            $("#contactDetailMobile").removeClass("fontColorSelected ");
+        }
+    }
+}
diff --git a/js/phone.js b/js/phone.js
new file mode 100644 (file)
index 0000000..35ebedd
--- /dev/null
@@ -0,0 +1,361 @@
+/* global ko, formatPhoneNumber, moment */
+
+/**
+ * This class provides methods to operate with contacts and call history from [tizen.phone](./native/group__phoned.html).
+ * Sample contact object looks like:
+ *
+ *     {
+ *          uid: "JeremyMartinson15417543010",
+ *          personId: "15417543010",
+ *                     name: {
+ *                             firstName: "Jeremy",
+ *                             lastName: "Martinson"
+ *                             displayName :"Jeremy Martinson"
+ *                     },
+ *                     phoneNumbers: {
+ *                             number:"1-541-754-3010"
+ *                     },
+ *                     emails: {
+ *                             email: "Jeremy.Martinson@gmail.com"
+ *                     },
+ *                     photoURI: {
+ *                             photoURI: "url://"
+ *                     },
+ *                     addresses: {
+ *                             streetAddress: "455 Larkspur Dr.",
+ *                             city: "San Jose",
+ *                             country: "California",
+ *                             postalCode: " "
+ *                     }
+ *     }
+ *
+ * @class Phone
+ * @module PhoneApplication
+ * @constructor
+ */
+var Phone = (function() {
+       "use strict";
+
+       function Phone() {
+               var self = this;
+               if (typeof(tizen.phone) !== 'undefined') {
+                       this.phone = tizen.phone;
+               } else {
+                       this.phone = null;
+               }
+               this.contacts = ko.observableArray([]);
+               this.callHistory = ko.observableArray([]);
+
+               this.contactsAlphabetFilter = ko.observable("");
+
+               this.contactsComputed = ko.computed(function() {
+                       if (self.contactsAlphabetFilter() !== "") {
+                               return ko.utils.arrayFilter(self.contacts(), function(contact) {
+                                       if ( !! contact.name && !! contact.name.leftLastName) {
+                                               return contact.name.lastName.toString().toLowerCase().trim().indexOf(
+                                                       self.contactsAlphabetFilter().toString().toLowerCase().trim()) === 0;
+                                       }
+                                       return false;
+                               });
+                       }
+                       return self.contacts();
+               });
+       }
+
+/**
+ * This method download contacts from tizen.phone. Using  API method tizen.phone.getContacts.
+ * @method loadContacts
+ * @param callback {function(error)} Callback function called after method is finished. Parameter `error` will contain any error that was intercepted.
+ */
+
+       Phone.prototype.loadContacts = function(callback) {
+               var self = this;
+               var i, conntactsArrayLength;
+               if ( !! self.phone) {
+
+                       self.phone.getContacts(0, function(contactsArray) {
+
+                               contactsArray = self.formatContacts(contactsArray);
+                               self.contacts(contactsArray);
+                               if ( !! callback) {
+                                       callback(null);
+                               }
+                       }, function(err) {
+                               console.log("Error(" + err.code + "): " + err.message);
+                               if ( !! callback) {
+                                       callback(err);
+                               }
+                       });
+               } else {
+                       if ( !! callback) {
+                               callback("Phone API is not available.");
+                       }
+               }
+       };
+
+       /**
+        * This method provides compare contact by last name, it is use in observable array from [knockout library](http://knockoutjs.com/documentation/observableArrays.html).
+        * @method compareByLastName
+        * @param left {Object} left compare element
+        * @param right {Object} right compare element
+        */
+
+       Phone.prototype.compareByLastName = function(left, right) {
+
+               var leftLastName = "Unknown";
+               if ( !!left.name && !!left.name.lastName && left.name.lastName !== "") {
+                       leftLastName = left.name.lastName;
+               }
+               leftLastName = leftLastName.toString().trim().toLowerCase();
+               var rightLastName = "Unknown";
+               if ( !!right.name && !!right.name.lastName && right.name.lastName !== "") {
+                       rightLastName = right.name.lastName;
+               }
+               rightLastName = rightLastName.toString().trim().toLowerCase();
+               return leftLastName === rightLastName ? 0 : (leftLastName < rightLastName) ? -1 : 1;
+       };
+       /**
+        * This method compares call history items by date, it is used in observable array from [knockout library](http://knockoutjs.com/documentation/observableArrays.html).
+        * @method compareHistoryByDate
+        * @param left {Object} left compare element
+        * @param right {Object} right compare element
+        */
+       Phone.prototype.compareHistoryByDate = function(left, right) {
+
+               var monthNames = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
+
+               var tmpLeftDateTime = left.startTime.toUpperCase().split(" ");
+               var tmpLeftDate = tmpLeftDateTime[0].split("/");
+               tmpLeftDate[0] = tmpLeftDate[0].indexOf(tmpLeftDate[0]) + 1;
+               var tmpLeftTime = tmpLeftDateTime[1].split(":");
+               var leftDate = new Date(parseInt(tmpLeftDate[0], 10), parseInt(tmpLeftDate[1], 10), parseInt(tmpLeftDate[2], 10), parseInt(tmpLeftTime[0], 10), parseInt(tmpLeftTime[1], 10));
+
+               var tmpRightDateTime = right.startTime.toUpperCase().split(" ");
+               var tmpRightDate = tmpRightDateTime[0].split("/");
+               tmpRightDate[0] = tmpRightDate[0].indexOf(tmpRightDate[0]) + 1;
+               var tmpRightTime = tmpRightDateTime[1].split(":");
+               var rightDate = new Date(parseInt(tmpRightDate[0], 10), parseInt(tmpRightDate[1], 10), parseInt(tmpRightDate[2], 10), parseInt(tmpRightTime[0], 10), parseInt(tmpRightTime[1], 10));
+
+               return leftDate === rightDate ? 0 : (leftDate > rightDate) ? -1 : 1;
+
+       };
+       /**
+        * This method clear contacts array.
+        * @method clearContacts
+        */
+       Phone.prototype.clearContacts = function() {
+
+               var self = this;
+               self.contacts.removeAll();
+               self.contacts([]);
+       };
+       /**
+        * This method searches contact in contacts list by id .
+        * @method getContactById
+        * @param id {String} search contact id
+        * @return {Object} if contact is found, return contact. Else return first element from contacts list.
+        */
+       Phone.prototype.getContactById = function(id) {
+
+               var self = this;
+               var contact = ko.utils.arrayFirst(self.contacts(), function(contact) {
+                       return contact.uid === id;
+               });
+               return contact;
+       };
+       /**
+        * This method searches contact in contacts list by personId.
+        * @method getContactByPersonId
+        * @param id {String} search contact personId
+        * @return {Object} if contact is found, return contact. Else return first element from contacts list.
+        */
+       Phone.prototype.getContactByPersonId = function(personId) {
+
+               var self = this;
+               var contact = ko.utils.arrayFirst(self.contacts(), function(contact) {
+                       if (contact.personId === personId) {
+                               return true;
+                       }
+
+                       if ( !! contact.phoneNumbers && contact.phoneNumbers.length) {
+                               for (var i = 0; i < contact.phoneNumbers.length; ++i) {
+                                       if ( !! contact.phoneNumbers[i].number && contact.phoneNumbers[i].number === personId) {
+                                               return true;
+                                       }
+                               }
+                       }
+
+                       return false;
+               });
+               return contact;
+       };
+       /**
+        * This method searches contact in contacts list by phoneNumber.
+        * @method getContactByPhoneNumber
+        * @param id {String} search contact phoneNumber
+        * @return {Object} if contact is found, return contact. Else return first element from contacts list.
+        */
+       Phone.prototype.getContactByPhoneNumber = function(phoneNumber) {
+
+               var self = this;
+               if ( !! phoneNumber && phoneNumber !== "") {
+                       phoneNumber = formatPhoneNumber(phoneNumber);
+                       var foundContact = ko.utils.arrayFirst(self.contacts(), function(contact) {
+                               if ( !! contact.phoneNumbers && contact.phoneNumbers.length) {
+
+
+                                       for (var i = 0; i < contact.phoneNumbers.length; ++i) {
+                                               if ( !! contact.phoneNumbers[i].number && contact.phoneNumbers[i].number === phoneNumber) {
+                                                       return true;
+                                               }
+                                       }
+                               }
+                               return false;
+                       });
+                       return foundContact;
+               }
+               return null;
+       };
+       /**
+        * This method composes display name from contact names.
+        * @method getDisplayNameStr
+        * @param contact {Object} contact object
+        * @return {String} contact name for display
+        */
+       Phone.prototype.getDisplayNameStr = function(contact) {
+
+               var self = this;
+               if ( !! contact && !! contact.name) {
+                       var name = [];
+                       if ( !! contact.name.lastName && contact.name.lastName !== "") {
+                               name.push(contact.name.lastName);
+                       }
+                       if ( !! contact.name.firstName && contact.name.firstName !== "") {
+                               name.push(contact.name.firstName);
+                       }
+                       if (name.length === 0 && !! contact.name.displayName && contact.name.displayName !== "") {
+                               name.push(contact.name.displayName);
+                       }
+                       return name.join(" ");
+               }
+               return "Unknown";
+       };
+       /**
+ * This method downloads call history from tizen.phone. Using  API method tizen.phone.getCallHistory.
+
+ * @method loadCallHistory
+ * @param callback {function(error)} Callback function called after method is finished. Parameter `error` will contain any error that was intercepted.
+ */
+       Phone.prototype.loadCallHistory = function(callback) {
+
+               var self = this;
+               var i;
+               var callHistoryArrayLength;
+               if ( !! self.phone) {
+                       self.phone.getCallHistory(25, function(callHistoryArray) {
+                               callHistoryArray = self.formatCallHistory(callHistoryArray);
+
+                               self.callHistory(callHistoryArray);
+                               if ( !! callback) {
+                                       callback(null);
+                               }
+                       }, function(err) {
+                               console.log("Error(" + err.code + "): " + err.message);
+                               if ( !! callback) {
+                                       callback(err);
+                               }
+                       });
+               } else {
+                       if ( !! callback) {
+                               callback("Phone API is not available.");
+                       }
+               }
+       };
+       /**
+ * This method prepares contact data to be displayed in html.
+
+ * @method formatContacts
+ * @param contactsList {Array} list of contacts
+ * @return {Array} list of contacts
+ */
+       Phone.prototype.formatContacts = function(contactsList) {
+               var i, j;
+               var contactsListLength;
+               var phoneNumbersLength;
+               contactsListLength = contactsList.length;
+               for (i = 0; i < contactsListLength; i++) {
+                       if (!contactsList[i].photoURI) {
+                               contactsList[i].photoURI = null;
+                       }
+                       if (!contactsList[i].addresses) {
+                               contactsList[i].addresses = null;
+                       }
+
+                       if (!contactsList[i].emails) {
+                               contactsList[i].emails = null;
+                       }
+                       if ( !! contactsList[i].phoneNumbers) {
+                               phoneNumbersLength = contactsList[i].phoneNumbers.length;
+                               for (j = 0; j < phoneNumbersLength; j++) {
+                                       contactsList[i].phoneNumbers[j].number = formatPhoneNumber(contactsList[i].phoneNumbers[j].number);
+                               }
+                       }
+               }
+               return contactsList;
+       };
+       /**
+ * This method prepares call history data to be displayed in html.
+
+ * @method formatCallHistory
+ * @param callHistoryList {Array} list of call history
+ * @return {Array} call history array
+ */
+       Phone.prototype.formatCallHistory = function(callHistoryList) {
+
+               var callHistoryListLength;
+               var i;
+               callHistoryListLength = callHistoryList.length;
+               for (i = 0; i < callHistoryListLength; i++) {
+                       if ( !! callHistoryList[i].startTime) {
+                               var date = callHistoryList[i].startTime;
+                               callHistoryList[i].startTime = moment(date).format("MMM/DD/YYYY HH:mm");
+                       } else {
+                               callHistoryList[i].startTime = null;
+                       }
+               }
+               return callHistoryList;
+       };
+       /**
+        * This method clears call history.
+        * @method clearCallHistory
+        */
+       Phone.prototype.clearCallHistory = function() {
+
+               var self = this;
+               self.callHistory.removeAll();
+               self.callHistory([]);
+       };
+       /**
+        * This method searches call history by contact personID.
+        * @method getCallHistoryByPersonId
+        * @param personId {string} search contact personId
+        * @return {Array} call history array
+        */
+       Phone.prototype.getCallHistoryByPersonId = function(personId) {
+
+               var self = this;
+               var callHistory = ko.utils.arrayFilter(self.callHistory(), function(callHistoryEntry) {
+                       if ( !! callHistoryEntry.remoteParties) {
+                               for (var i = 0; i < callHistoryEntry.remoteParties.length; ++i) {
+                                       if (callHistoryEntry.remoteParties[i].personId === personId) {
+                                               return true;
+                                       }
+                               }
+                       }
+                       return false;
+               });
+               return callHistory;
+       };
+       window.__phone = undefined === window.__phone ? new Phone() : window.__phone;
+       return window.__phone;
+})();
\ No newline at end of file
diff --git a/packaging/html5-ui-phone.changes b/packaging/html5-ui-phone.changes
new file mode 100644 (file)
index 0000000..ef2d9f8
--- /dev/null
@@ -0,0 +1,4 @@
+* Thu Mar 06 2014 brianjjones <brian.j.jones@intel.com> 4245f58
+- Initial commit of the Phone app
+
+
diff --git a/packaging/html5-ui-phone.spec b/packaging/html5-ui-phone.spec
new file mode 100644 (file)
index 0000000..4298f0f
--- /dev/null
@@ -0,0 +1,36 @@
+Name:       html5_UI_Phone
+Summary:    A proof of concept pure html5 UI
+Version:    0.0.1
+Release:    1
+Group:      Applications/System
+License:    Apache 2.0
+URL:        http://www.tizen.org
+Source0:    %{name}-%{version}.tar.bz2
+BuildRequires:  zip
+BuildRequires:  html5_UI_Common
+Requires:   wrt-installer
+Requires:   wrt-plugins-ivi
+
+%description
+A proof of concept pure html5 UI
+
+%prep
+%setup -q -n %{name}-%{version}
+
+%build
+
+make wgtPkg
+
+%install
+rm -rf %{buildroot}
+%make_install
+
+%post
+    wrt-installer -i /opt/usr/apps/.preinstallWidgets/html5UIPhone.wgt;
+
+%postun
+    wrt-installer -un html5POC09.Phone
+
+%files
+%defattr(-,root,root,-)
+/opt/usr/apps/.preinstallWidgets/html5UIPhone.wgt
diff --git a/templates/contactCarouselDelegate.html b/templates/contactCarouselDelegate.html
new file mode 100644 (file)
index 0000000..54a4efd
--- /dev/null
@@ -0,0 +1,24 @@
+<li><div id="carouselBox_{{:#index}}" class="carouselBox borderColorTheme" data-id="{{:id}}" onclick="callContactById('{{:id}}');">
+               <div class="carouselPhotoArea borderColorTheme">
+                       <div class="carouselPhotoBox noContactPhoto">
+                               <img class="carouselPhoto" src="{{:photoURI}}" />
+                       </div>
+               </div>
+               <!-- <div class="carouselInfoBox carouselCallContact"></div> -->
+               <div
+                       class="carouselInfoBox carouselName fontSizeLarger fontWeightBold fontColorNormal">
+                       {{if name}} {{if name.firstName}} {{:name.firstName}} {{/if}} {{if
+                       name.lastName}} {{:name.lastName}} {{/if}} {{/if}}</div>
+               <div class="carouselInfoBox carouselNumber fontSizeSmall fontWeightBold fontColorTheme">
+                       {{if phoneNumbers}} {{if phoneNumbers[0]}}
+                       {{:phoneNumbers[0].number}} {{/if}} {{/if}}</div>
+               <div class="callHistoryElement borderColorTheme">
+                       <div class="{{if direction == 'missed-new'}}missedNew{{else}}{{:direction.toLowerCase()}}{{/if}}CallIcon callHistoryIcon callHistoryIconGen"></div>
+                       <div class="callHistoryDetails">
+                               <div class="fontSizeXXSmall fontColorNormal">{{:startTime.toString().toUpperCase()}}</div>
+                               <div class="fontSizeXXSmall fontWeightBold fontColorTheme">{{:direction.toString().toUpperCase()}}</div>
+                       </div>
+               </div>
+
+       </div>
+</li>
diff --git a/templates/libraryContactDetailDelegate.html b/templates/libraryContactDetailDelegate.html
new file mode 100644 (file)
index 0000000..4d46c86
--- /dev/null
@@ -0,0 +1,47 @@
+
+<div class="contactDetailBox1">
+       <div class="contactDetailPhotoBox noContactPhoto">
+               <img class="contactDetailPhoto" src="{{:photoURI}}" />
+       </div>
+       <div
+               class="contactDetailFavorite isFavorite{{if isFavorite}}True{{else}}False{{/if}}">
+       </div>
+</div>
+
+<div class="contactDetailBox2">
+       <div class="contactDetailBox3" onClick="callContactById('{{:id}}')">
+               <div class="phoneIcon"></div>
+               <div class="contactDetailBox4">
+                       <div id="contactDetailMobileTitle">MOBILE</div>
+                       <div id= "contactDetailMobile" class="fontSizeLarger fontWeightBold fontColorNormal">{{:phoneNumber}}</div>
+               </div>
+
+       </div>
+       <div class="contactDetailBox3">
+               <div class="emailIcon"></div>
+               <div class="contactDetailBox4">
+                       <div id="contactDetailEmailTitle">EMAIL</div>
+                       <div class="fontSizeLarger fontWeightBold fontColorNormal">{{:email}}</div>
+               </div>
+       </div>
+       <div class="contactDetailBox3">
+               <div class="addressIcon"></div>
+               <div class="contactDetailBox4">
+                       <div id="contactDetailAddressTitle">ADDRESS</div>
+                       <div class="fontSizeLarger fontWeightBold fontColorNormal">{{:address.toUpperCase()}}</div>
+               </div>
+       </div>
+</div>
+
+<div class="callHistoryBox borderColorTheme">
+       {{foreach history}}
+       <div class="callHistoryElement borderColorTheme">
+               <div
+                       class="{{if direction == 'missed-new'}}missedNew{{else}}{{:direction.toString().toLowerCase()}}{{/if}}CallIcon callHistoryIcon"></div>
+               <div class="callHistoryDetails">
+                       <div class="fontSizeLarge fontWeightBold fontColorNormal">{{:startTime.toString().toUpperCase()}}</div>
+                       <div class="fontSizeXSmall fontWeightBold fontColorTheme">{{:direction.toString().toUpperCase()}}</div>
+               </div>
+       </div>
+       {{/foreach}}
+</div>
\ No newline at end of file
diff --git a/templates/template-contacts.html b/templates/template-contacts.html
new file mode 100644 (file)
index 0000000..b14892a
--- /dev/null
@@ -0,0 +1,10 @@
+<div class="contactElement boxShadow4 borderColorTheme"
+       data-bind="click: ContactsLibrary.openContactDetail">
+       <div class="phoneIcon"></div>
+       <div class="contactPhotoBox noContactPhoto">
+               <img class="contactPhoto" data-bind="attr:{src: photoURI}" />
+       </div>
+       <div
+               class="contactName fontSizeLarge fontWeightBold fontColorNormal textBgColorNormalTransparent"
+               data-bind="text: Phone.getDisplayNameStr($data)"></div>
+</div>