├── .jshintignore ├── .gitattributes ├── test ├── scripts ├── components ├── fixtures │ └── img │ │ ├── 60x40.gif │ │ ├── 100x240.gif │ │ ├── 120x120.gif │ │ ├── 240x100.gif │ │ ├── 480x480.gif │ │ └── 1200x1600.jpg ├── lib │ ├── expect.js │ ├── JSCovReporter │ │ └── reporter.js │ └── asyncstorage-mock.js ├── index.html └── spec │ └── main.js ├── .bowerrc ├── .rvmrc ├── Gemfile ├── app ├── images │ ├── call.png │ ├── send.png │ ├── sms.png │ ├── call2x.png │ ├── delete.png │ ├── loading.png │ ├── nature.png │ ├── objects.png │ ├── people.png │ ├── places.png │ ├── send2x.png │ ├── sms2x.png │ ├── symbols.png │ ├── delete2x.png │ ├── nature2x.png │ ├── objects2x.png │ ├── people2x.png │ ├── places2x.png │ ├── symbols2x.png │ ├── audio_file.png │ ├── emoji_button.png │ ├── image_file.png │ ├── video_file.png │ ├── control_button.png │ ├── icon_settings.png │ ├── refresh-icon2x.png │ ├── unknown_avatar.png │ ├── emoji_button_inv.png │ ├── icon_settings2x.png │ ├── frame_contact_book.png │ ├── control_button_active.png │ ├── frame_contact_active.png │ ├── icon_14px_image_read.png │ ├── icon_14px_image_read2x.png │ ├── icon_14px_image_unread.png │ ├── icon_14px_message_read.png │ ├── icon_14px_place_read.png │ ├── icon_14px_place_read2x.png │ ├── icon_14px_place_unread.png │ ├── btn_pin_button_normal2x.png │ ├── btn_pin_button_pressed2x.png │ ├── icon_14px_image_unread2x.png │ ├── icon_14px_message_read2x.png │ ├── icon_14px_message_unread.png │ ├── icon_14px_place_unread2x.png │ ├── shared │ │ ├── buttons │ │ │ ├── ui │ │ │ │ ├── danger.png │ │ │ │ ├── shadow.png │ │ │ │ ├── default.png │ │ │ │ ├── disabled.png │ │ │ │ ├── recommend.png │ │ │ │ ├── shadow@2x.png │ │ │ │ ├── shadow@1.5x.png │ │ │ │ ├── danger-press.png │ │ │ │ └── danger-disabled.png │ │ │ └── icons │ │ │ │ ├── dialog.png │ │ │ │ ├── view.png │ │ │ │ ├── view@2x.png │ │ │ │ ├── view_rtl.png │ │ │ │ ├── dialog@1.5x.png │ │ │ │ ├── dialog@2x.png │ │ │ │ ├── dialog_rtl.png │ │ │ │ ├── view@1.5x.png │ │ │ │ ├── view_rtl@2x.png │ │ │ │ ├── dialog_rtl@2x.png │ │ │ │ ├── view_rtl@1.5x.png │ │ │ │ └── dialog_rtl@1.5x.png │ │ ├── switches │ │ │ ├── switch │ │ │ │ ├── icon.png │ │ │ │ ├── icon2x.png │ │ │ │ ├── handler.png │ │ │ │ ├── handler2x.png │ │ │ │ └── background.png │ │ │ ├── check │ │ │ │ ├── danger.png │ │ │ │ ├── default.png │ │ │ │ ├── danger2x.png │ │ │ │ ├── default2x.png │ │ │ │ └── negative.png │ │ │ └── radio │ │ │ │ ├── danger.png │ │ │ │ ├── default.png │ │ │ │ ├── danger2x.png │ │ │ │ ├── default2x.png │ │ │ │ └── negative.png │ │ ├── input_areas │ │ │ ├── ui │ │ │ │ ├── active.png │ │ │ │ ├── shadow.png │ │ │ │ ├── background.png │ │ │ │ ├── separator.png │ │ │ │ ├── shadow2x.png │ │ │ │ ├── shadow-invert.png │ │ │ │ ├── shadow-invert2x.png │ │ │ │ ├── shadow-search.png │ │ │ │ └── shadow-search2x.png │ │ │ └── icons │ │ │ │ ├── arrow.png │ │ │ │ ├── arrow2x.png │ │ │ │ ├── clear.png │ │ │ │ └── clear2x.png │ │ ├── confirm │ │ │ └── images │ │ │ │ └── ui │ │ │ │ ├── danger.png │ │ │ │ ├── default.png │ │ │ │ ├── pattern.png │ │ │ │ ├── disabled.png │ │ │ │ ├── gradient.png │ │ │ │ ├── recommend.png │ │ │ │ ├── danger-press.png │ │ │ │ └── danger-disabled.png │ │ ├── headers │ │ │ └── images │ │ │ │ ├── icons │ │ │ │ ├── add.png │ │ │ │ ├── back.png │ │ │ │ ├── down.png │ │ │ │ ├── edit.png │ │ │ │ ├── menu.png │ │ │ │ ├── send.png │ │ │ │ ├── up.png │ │ │ │ ├── user.png │ │ │ │ ├── add@2x.png │ │ │ │ ├── clear.png │ │ │ │ ├── close.png │ │ │ │ ├── group.png │ │ │ │ ├── reply.png │ │ │ │ ├── up@2x.png │ │ │ │ ├── add@1.5x.png │ │ │ │ ├── add@2.25x.png │ │ │ │ ├── back-rtl.png │ │ │ │ ├── back@1.5x.png │ │ │ │ ├── back@2x.png │ │ │ │ ├── back_rtl.png │ │ │ │ ├── clear@2x.png │ │ │ │ ├── close@2x.png │ │ │ │ ├── compose.png │ │ │ │ ├── down@1.5x.png │ │ │ │ ├── down@2x.png │ │ │ │ ├── edit@1.5x.png │ │ │ │ ├── edit@2x.png │ │ │ │ ├── group2x.png │ │ │ │ ├── location.png │ │ │ │ ├── menu@1.5x.png │ │ │ │ ├── menu@2x.png │ │ │ │ ├── options.png │ │ │ │ ├── reply-all.png │ │ │ │ ├── reply@2x.png │ │ │ │ ├── reply_all.png │ │ │ │ ├── send@1.5x.png │ │ │ │ ├── send@2x.png │ │ │ │ ├── up@1.5x.png │ │ │ │ ├── up@2.25x.png │ │ │ │ ├── user@1.5x.png │ │ │ │ ├── user@2x.png │ │ │ │ ├── attachments.png │ │ │ │ ├── back-rtl@2x.png │ │ │ │ ├── back@2.25x.png │ │ │ │ ├── clear@1.5x.png │ │ │ │ ├── close@1.5x.png │ │ │ │ ├── close@2.25x.png │ │ │ │ ├── compose@2x.png │ │ │ │ ├── down@2.25x.png │ │ │ │ ├── edit@2.25x.png │ │ │ │ ├── location2x.png │ │ │ │ ├── menu@2.25x.png │ │ │ │ ├── options@2x.png │ │ │ │ ├── organic │ │ │ │ │ ├── add.png │ │ │ │ │ ├── add@2x.png │ │ │ │ │ ├── back.png │ │ │ │ │ ├── close.png │ │ │ │ │ ├── edit.png │ │ │ │ │ ├── add@1.5.png │ │ │ │ │ ├── add@1.5x.png │ │ │ │ │ ├── back@2x.png │ │ │ │ │ ├── back_rtl.png │ │ │ │ │ ├── close@2x.png │ │ │ │ │ ├── edit@2x.png │ │ │ │ │ ├── add@2.25x.png │ │ │ │ │ ├── back@1.5x.png │ │ │ │ │ ├── back@2.25x.png │ │ │ │ │ ├── close@1.5x.png │ │ │ │ │ ├── close@2.25x.png │ │ │ │ │ ├── edit@1.5x.png │ │ │ │ │ ├── edit@2.25x.png │ │ │ │ │ ├── back_rtl@1.5x.png │ │ │ │ │ ├── back-rtl@2.25x.png │ │ │ │ │ ├── back.svg │ │ │ │ │ └── close.svg │ │ │ │ ├── reply@1.5x.png │ │ │ │ ├── reply@2.25x.png │ │ │ │ ├── send@2.25x.png │ │ │ │ ├── user@2.25x.png │ │ │ │ ├── attachments2x.png │ │ │ │ ├── back-rtl@1.5x.png │ │ │ │ ├── back-rtl@2.25x.png │ │ │ │ ├── back_rtl@1.5x.png │ │ │ │ ├── compose@1.5x.png │ │ │ │ ├── compose@2.25x.png │ │ │ │ ├── options@1.5x.png │ │ │ │ ├── options@2.25x.png │ │ │ │ ├── reply-all@1.5x.png │ │ │ │ ├── reply-all@2x.png │ │ │ │ ├── reply_all@1.5x.png │ │ │ │ ├── reply_all@2x.png │ │ │ │ ├── reply.png@2.25x.png │ │ │ │ ├── reply_all@2.25x.png │ │ │ │ ├── menu.svg │ │ │ │ ├── back.svg │ │ │ │ └── close.svg │ │ │ │ └── ui │ │ │ │ ├── header.png │ │ │ │ ├── shadow.png │ │ │ │ ├── separator.png │ │ │ │ ├── shadow@2x.png │ │ │ │ ├── subheader.png │ │ │ │ ├── dark │ │ │ │ ├── header.png │ │ │ │ ├── negative.png │ │ │ │ ├── separator.png │ │ │ │ ├── subheader.png │ │ │ │ ├── separator@1.5x.png │ │ │ │ └── separator-large.png │ │ │ │ ├── shadow@1.5x.png │ │ │ │ ├── noise-pattern.png │ │ │ │ ├── organic │ │ │ │ ├── header.png │ │ │ │ ├── negative.png │ │ │ │ ├── pattern.png │ │ │ │ ├── separator.png │ │ │ │ ├── subheader.png │ │ │ │ └── separator-large.png │ │ │ │ ├── overlay │ │ │ │ ├── header.png │ │ │ │ └── separator.png │ │ │ │ └── separator-large.png │ │ ├── progress_activity │ │ │ ├── default.png │ │ │ ├── activity.png │ │ │ └── default2x.png │ │ └── value_selector │ │ │ ├── ui │ │ │ ├── default.png │ │ │ ├── pattern.png │ │ │ ├── shadow.png │ │ │ ├── gradient.png │ │ │ ├── shadow@2x.png │ │ │ ├── affirmative.png │ │ │ ├── shadow@1.5x.png │ │ │ ├── gradient@1.5x.png │ │ │ ├── shadow-invert.png │ │ │ ├── shadow-invert@2x.png │ │ │ └── shadow-invert@1.5x.png │ │ │ └── icons │ │ │ ├── checked.png │ │ │ ├── checked@2x.png │ │ │ └── checked@1.5x.png │ ├── btn_message_button_normal2x.png │ ├── btn_photo_button_normal2x.png │ ├── btn_photo_button_pressed2x.png │ ├── frame_contact_book_active.png │ ├── icon_14px_message_unread2x.png │ ├── icon_14px_notification_read.png │ ├── btn_message_button_pressed2x.png │ ├── icon_14px_notification_read2x.png │ ├── icon_14px_notification_unread.png │ └── icon_14px_notification_unread2x.png ├── emoji │ ├── sheet_20.png │ ├── sheet_45.png │ └── bin │ │ ├── mkcss.js │ │ └── mkselector.js ├── styles │ ├── _index.sass │ ├── _contacts.sass │ ├── _location.sass │ ├── _migration.sass │ ├── _animations.scss │ ├── _validate.sass │ ├── _compose_image.sass │ ├── _settings.sass │ ├── shared │ │ ├── _responsive.scss │ │ ├── _toolbars.scss │ │ ├── _overrides.scss │ │ ├── _progress_activity.scss │ │ └── _switches.scss │ ├── main.sass │ ├── _profile.sass │ ├── _emoji.sass │ ├── _login.sass │ ├── _inbox.sass │ ├── _common_register.sass │ └── _common.sass ├── icons │ ├── 30 │ │ └── openwapp.png │ ├── 60 │ │ └── openwapp.png │ └── 128 │ │ └── openwapp.png ├── scripts │ ├── templates │ │ ├── contacts.hbs │ │ ├── alert-message.hbs │ │ ├── notification.hbs │ │ ├── contact-photo.hbs │ │ ├── updateNeeded.hbs │ │ ├── compose-message.hbs │ │ ├── index.hbs │ │ ├── migration.hbs │ │ ├── contact.hbs │ │ ├── location-viewer.hbs │ │ ├── location-message.hbs │ │ ├── text-message.hbs │ │ ├── mini-conversation.hbs │ │ ├── compose-location.hbs │ │ ├── compose-image.hbs │ │ ├── multimedia-message.hbs │ │ ├── contact-profile.hbs │ │ ├── conversation.hbs │ │ ├── validate.hbs │ │ ├── settings.hbs │ │ ├── group-profile.hbs │ │ ├── profile.hbs │ │ └── inbox.hbs │ ├── utils │ │ ├── platform.js │ │ ├── connectivity.js │ │ ├── PhoneNumberMetaDataUpdates.js │ │ ├── mimetype.js │ │ ├── language.js │ │ ├── country.js │ │ ├── contact-picker.js │ │ ├── thumbnail.js │ │ └── sdmanager.js │ ├── global.js │ ├── views │ │ ├── index.js │ │ ├── contact-photo.js │ │ ├── location-message.js │ │ ├── text-message.js │ │ ├── contact.js │ │ ├── mini-conversation.js │ │ ├── notification-message.js │ │ ├── location-viewer.js │ │ ├── settings.js │ │ ├── contact-profile.js │ │ ├── compose-message.js │ │ └── validate.js │ ├── models │ │ ├── geoposition.js │ │ └── background-service.js │ ├── vendor │ │ └── canvasToBlob │ │ │ ├── canvas-toBlob.min.js │ │ │ └── canvas-toBlob.js │ ├── router-migration.js │ ├── storage │ │ └── auth.js │ ├── collections │ │ ├── countries.js │ │ └── messages.js │ └── main.js ├── locales │ └── locales.ini └── index.html ├── grunt.local.sample.json ├── .gitignore ├── bower.json ├── .editorconfig ├── .jshintrc ├── package.json ├── contacts.json.sample ├── enable-remote-debugging.sh ├── tools └── create_countries_json.py └── Readme.md /.jshintignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /test/scripts: -------------------------------------------------------------------------------- 1 | ../app/scripts -------------------------------------------------------------------------------- /test/components: -------------------------------------------------------------------------------- 1 | ../app/components -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/components" 3 | } 4 | -------------------------------------------------------------------------------- /.rvmrc: -------------------------------------------------------------------------------- 1 | rvm_gemset_create_on_use_flag=1 2 | rvm gemset use html5-webapp -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'compass', '~> 0.12.2' 4 | -------------------------------------------------------------------------------- /app/images/call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/call.png -------------------------------------------------------------------------------- /app/images/send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/send.png -------------------------------------------------------------------------------- /app/images/sms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/sms.png -------------------------------------------------------------------------------- /app/emoji/sheet_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/emoji/sheet_20.png -------------------------------------------------------------------------------- /app/emoji/sheet_45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/emoji/sheet_45.png -------------------------------------------------------------------------------- /app/images/call2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/call2x.png -------------------------------------------------------------------------------- /app/images/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/delete.png -------------------------------------------------------------------------------- /app/images/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/loading.png -------------------------------------------------------------------------------- /app/images/nature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/nature.png -------------------------------------------------------------------------------- /app/images/objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/objects.png -------------------------------------------------------------------------------- /app/images/people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/people.png -------------------------------------------------------------------------------- /app/images/places.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/places.png -------------------------------------------------------------------------------- /app/images/send2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/send2x.png -------------------------------------------------------------------------------- /app/images/sms2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/sms2x.png -------------------------------------------------------------------------------- /app/images/symbols.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/symbols.png -------------------------------------------------------------------------------- /app/images/delete2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/delete2x.png -------------------------------------------------------------------------------- /app/images/nature2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/nature2x.png -------------------------------------------------------------------------------- /app/images/objects2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/objects2x.png -------------------------------------------------------------------------------- /app/images/people2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/people2x.png -------------------------------------------------------------------------------- /app/images/places2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/places2x.png -------------------------------------------------------------------------------- /app/images/symbols2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/symbols2x.png -------------------------------------------------------------------------------- /app/styles/_index.sass: -------------------------------------------------------------------------------- 1 | #loading-wrapper 2 | width: 100% 3 | margin-top: 50% 4 | text-align: center 5 | -------------------------------------------------------------------------------- /app/icons/128/openwapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/icons/128/openwapp.png -------------------------------------------------------------------------------- /app/icons/30/openwapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/icons/30/openwapp.png -------------------------------------------------------------------------------- /app/icons/60/openwapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/icons/60/openwapp.png -------------------------------------------------------------------------------- /app/images/audio_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/audio_file.png -------------------------------------------------------------------------------- /app/images/emoji_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/emoji_button.png -------------------------------------------------------------------------------- /app/images/image_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/image_file.png -------------------------------------------------------------------------------- /app/images/video_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/video_file.png -------------------------------------------------------------------------------- /test/fixtures/img/60x40.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/test/fixtures/img/60x40.gif -------------------------------------------------------------------------------- /app/images/control_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/control_button.png -------------------------------------------------------------------------------- /app/images/icon_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_settings.png -------------------------------------------------------------------------------- /app/images/refresh-icon2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/refresh-icon2x.png -------------------------------------------------------------------------------- /app/images/unknown_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/unknown_avatar.png -------------------------------------------------------------------------------- /test/fixtures/img/100x240.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/test/fixtures/img/100x240.gif -------------------------------------------------------------------------------- /test/fixtures/img/120x120.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/test/fixtures/img/120x120.gif -------------------------------------------------------------------------------- /test/fixtures/img/240x100.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/test/fixtures/img/240x100.gif -------------------------------------------------------------------------------- /test/fixtures/img/480x480.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/test/fixtures/img/480x480.gif -------------------------------------------------------------------------------- /app/images/emoji_button_inv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/emoji_button_inv.png -------------------------------------------------------------------------------- /app/images/icon_settings2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_settings2x.png -------------------------------------------------------------------------------- /test/fixtures/img/1200x1600.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/test/fixtures/img/1200x1600.jpg -------------------------------------------------------------------------------- /app/images/frame_contact_book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/frame_contact_book.png -------------------------------------------------------------------------------- /app/images/control_button_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/control_button_active.png -------------------------------------------------------------------------------- /app/images/frame_contact_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/frame_contact_active.png -------------------------------------------------------------------------------- /app/images/icon_14px_image_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_image_read.png -------------------------------------------------------------------------------- /app/images/icon_14px_image_read2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_image_read2x.png -------------------------------------------------------------------------------- /app/images/icon_14px_image_unread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_image_unread.png -------------------------------------------------------------------------------- /app/images/icon_14px_message_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_message_read.png -------------------------------------------------------------------------------- /app/images/icon_14px_place_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_place_read.png -------------------------------------------------------------------------------- /app/images/icon_14px_place_read2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_place_read2x.png -------------------------------------------------------------------------------- /app/images/icon_14px_place_unread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_place_unread.png -------------------------------------------------------------------------------- /app/images/btn_pin_button_normal2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/btn_pin_button_normal2x.png -------------------------------------------------------------------------------- /app/images/btn_pin_button_pressed2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/btn_pin_button_pressed2x.png -------------------------------------------------------------------------------- /app/images/icon_14px_image_unread2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_image_unread2x.png -------------------------------------------------------------------------------- /app/images/icon_14px_message_read2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_message_read2x.png -------------------------------------------------------------------------------- /app/images/icon_14px_message_unread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_message_unread.png -------------------------------------------------------------------------------- /app/images/icon_14px_place_unread2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_place_unread2x.png -------------------------------------------------------------------------------- /app/images/shared/buttons/ui/danger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/ui/danger.png -------------------------------------------------------------------------------- /app/images/shared/buttons/ui/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/ui/shadow.png -------------------------------------------------------------------------------- /app/images/btn_message_button_normal2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/btn_message_button_normal2x.png -------------------------------------------------------------------------------- /app/images/btn_photo_button_normal2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/btn_photo_button_normal2x.png -------------------------------------------------------------------------------- /app/images/btn_photo_button_pressed2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/btn_photo_button_pressed2x.png -------------------------------------------------------------------------------- /app/images/frame_contact_book_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/frame_contact_book_active.png -------------------------------------------------------------------------------- /app/images/icon_14px_message_unread2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_message_unread2x.png -------------------------------------------------------------------------------- /app/images/icon_14px_notification_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_notification_read.png -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/dialog.png -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/view.png -------------------------------------------------------------------------------- /app/images/shared/buttons/ui/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/ui/default.png -------------------------------------------------------------------------------- /app/images/shared/buttons/ui/disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/ui/disabled.png -------------------------------------------------------------------------------- /app/images/shared/buttons/ui/recommend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/ui/recommend.png -------------------------------------------------------------------------------- /app/images/shared/buttons/ui/shadow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/ui/shadow@2x.png -------------------------------------------------------------------------------- /app/images/shared/switches/switch/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/switch/icon.png -------------------------------------------------------------------------------- /app/images/btn_message_button_pressed2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/btn_message_button_pressed2x.png -------------------------------------------------------------------------------- /app/images/icon_14px_notification_read2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_notification_read2x.png -------------------------------------------------------------------------------- /app/images/icon_14px_notification_unread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_notification_unread.png -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/view@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/view@2x.png -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/view_rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/view_rtl.png -------------------------------------------------------------------------------- /app/images/shared/buttons/ui/shadow@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/ui/shadow@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/ui/active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/ui/active.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/ui/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/ui/shadow.png -------------------------------------------------------------------------------- /app/images/shared/switches/check/danger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/check/danger.png -------------------------------------------------------------------------------- /app/images/shared/switches/check/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/check/default.png -------------------------------------------------------------------------------- /app/images/shared/switches/radio/danger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/radio/danger.png -------------------------------------------------------------------------------- /app/images/shared/switches/radio/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/radio/default.png -------------------------------------------------------------------------------- /app/images/shared/switches/switch/icon2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/switch/icon2x.png -------------------------------------------------------------------------------- /app/images/icon_14px_notification_unread2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/icon_14px_notification_unread2x.png -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/dialog@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/dialog@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/dialog@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/dialog@2x.png -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/dialog_rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/dialog_rtl.png -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/view@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/view@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/view_rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/view_rtl@2x.png -------------------------------------------------------------------------------- /app/images/shared/buttons/ui/danger-press.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/ui/danger-press.png -------------------------------------------------------------------------------- /app/images/shared/confirm/images/ui/danger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/confirm/images/ui/danger.png -------------------------------------------------------------------------------- /app/images/shared/confirm/images/ui/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/confirm/images/ui/default.png -------------------------------------------------------------------------------- /app/images/shared/confirm/images/ui/pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/confirm/images/ui/pattern.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/add.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/back.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/down.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/edit.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/menu.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/send.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/up.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/user.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/header.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/shadow.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/icons/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/icons/arrow.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/icons/arrow2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/icons/arrow2x.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/icons/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/icons/clear.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/icons/clear2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/icons/clear2x.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/ui/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/ui/background.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/ui/separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/ui/separator.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/ui/shadow2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/ui/shadow2x.png -------------------------------------------------------------------------------- /app/images/shared/progress_activity/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/progress_activity/default.png -------------------------------------------------------------------------------- /app/images/shared/switches/check/danger2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/check/danger2x.png -------------------------------------------------------------------------------- /app/images/shared/switches/check/default2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/check/default2x.png -------------------------------------------------------------------------------- /app/images/shared/switches/check/negative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/check/negative.png -------------------------------------------------------------------------------- /app/images/shared/switches/radio/danger2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/radio/danger2x.png -------------------------------------------------------------------------------- /app/images/shared/switches/radio/default2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/radio/default2x.png -------------------------------------------------------------------------------- /app/images/shared/switches/radio/negative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/radio/negative.png -------------------------------------------------------------------------------- /app/images/shared/switches/switch/handler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/switch/handler.png -------------------------------------------------------------------------------- /app/images/shared/switches/switch/handler2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/switch/handler2x.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/ui/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/ui/default.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/ui/pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/ui/pattern.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/ui/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/ui/shadow.png -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/dialog_rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/dialog_rtl@2x.png -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/view_rtl@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/view_rtl@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/buttons/ui/danger-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/ui/danger-disabled.png -------------------------------------------------------------------------------- /app/images/shared/confirm/images/ui/disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/confirm/images/ui/disabled.png -------------------------------------------------------------------------------- /app/images/shared/confirm/images/ui/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/confirm/images/ui/gradient.png -------------------------------------------------------------------------------- /app/images/shared/confirm/images/ui/recommend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/confirm/images/ui/recommend.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/add@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/add@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/clear.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/close.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/group.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/up@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/up@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/separator.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/shadow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/shadow@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/subheader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/subheader.png -------------------------------------------------------------------------------- /app/images/shared/progress_activity/activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/progress_activity/activity.png -------------------------------------------------------------------------------- /app/images/shared/progress_activity/default2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/progress_activity/default2x.png -------------------------------------------------------------------------------- /app/images/shared/switches/switch/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/switches/switch/background.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/ui/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/ui/gradient.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/ui/shadow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/ui/shadow@2x.png -------------------------------------------------------------------------------- /grunt.local.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "b2g_bin_path": "/Applications/B2G.app/Contents/MacOS", 3 | "gaia_homepath": "/Users/OAmat/Documents/code/gaia" 4 | } 5 | -------------------------------------------------------------------------------- /app/images/shared/buttons/icons/dialog_rtl@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/buttons/icons/dialog_rtl@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/confirm/images/ui/danger-press.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/confirm/images/ui/danger-press.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/add@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/add@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/add@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/add@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/back-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/back-rtl.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/back@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/back@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/back@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/back_rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/back_rtl.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/clear@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/clear@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/close@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/compose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/compose.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/down@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/down@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/down@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/down@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/edit@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/edit@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/edit@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/edit@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/group2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/group2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/location.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/menu@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/menu@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/menu@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/menu@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/options.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply-all.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply_all.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/send@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/send@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/send@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/send@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/up@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/up@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/up@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/up@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/user@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/user@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/user@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/user@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/dark/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/dark/header.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/shadow@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/shadow@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/ui/shadow-invert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/ui/shadow-invert.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/ui/shadow-invert2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/ui/shadow-invert2x.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/ui/shadow-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/ui/shadow-search.png -------------------------------------------------------------------------------- /app/images/shared/input_areas/ui/shadow-search2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/input_areas/ui/shadow-search2x.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/icons/checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/icons/checked.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/ui/affirmative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/ui/affirmative.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/ui/shadow@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/ui/shadow@1.5x.png -------------------------------------------------------------------------------- /app/scripts/templates/contacts.hbs: -------------------------------------------------------------------------------- 1 |

{{translate 'all'}}

2 | 5 | -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/attachments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/attachments.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/back-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/back-rtl@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/back@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/back@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/clear@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/clear@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/close@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/close@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/close@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/close@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/compose@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/compose@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/down@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/down@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/edit@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/edit@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/location2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/location2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/menu@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/menu@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/options@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/options@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/add.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/send@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/send@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/user@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/user@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/dark/negative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/dark/negative.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/dark/separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/dark/separator.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/dark/subheader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/dark/subheader.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/noise-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/noise-pattern.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/organic/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/organic/header.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/overlay/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/overlay/header.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/icons/checked@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/icons/checked@2x.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/ui/gradient@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/ui/gradient@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/ui/shadow-invert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/ui/shadow-invert.png -------------------------------------------------------------------------------- /app/images/shared/confirm/images/ui/danger-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/confirm/images/ui/danger-disabled.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/attachments2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/attachments2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/back-rtl@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/back-rtl@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/back-rtl@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/back-rtl@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/back_rtl@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/back_rtl@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/compose@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/compose@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/compose@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/compose@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/options@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/options@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/options@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/options@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/add@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/add@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/back.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/close.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/edit.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply-all@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply-all@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply-all@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply-all@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply_all@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply_all@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply_all@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply_all@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/organic/negative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/organic/negative.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/organic/pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/organic/pattern.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/organic/separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/organic/separator.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/organic/subheader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/organic/subheader.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/overlay/separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/overlay/separator.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/separator-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/separator-large.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/icons/checked@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/icons/checked@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/ui/shadow-invert@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/ui/shadow-invert@2x.png -------------------------------------------------------------------------------- /app/scripts/templates/alert-message.hbs: -------------------------------------------------------------------------------- 1 |
2 |

HTTP Error {{code}}: {{message}}

3 |
4 |
{{response}}
5 |
6 | -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/add@1.5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/add@1.5.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/add@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/add@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/back@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/back_rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/back_rtl.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/close@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/edit@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/edit@2x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply.png@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply.png@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/reply_all@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/reply_all@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/dark/separator@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/dark/separator@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/value_selector/ui/shadow-invert@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/value_selector/ui/shadow-invert@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/add@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/add@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/back@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/back@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/back@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/back@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/close@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/close@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/close@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/close@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/edit@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/edit@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/edit@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/edit@2.25x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/dark/separator-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/dark/separator-large.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/back_rtl@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/back_rtl@1.5x.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/ui/organic/separator-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/ui/organic/separator-large.png -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/back-rtl@2.25x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozillahispano/openwapp/HEAD/app/images/shared/headers/images/icons/organic/back-rtl@2.25x.png -------------------------------------------------------------------------------- /app/scripts/templates/notification.hbs: -------------------------------------------------------------------------------- 1 |
  • 2 | {{message}} 3 | 4 |
  • 5 | -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/menu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/scripts/templates/contact-photo.hbs: -------------------------------------------------------------------------------- 1 | Avatar 8 | -------------------------------------------------------------------------------- /app/scripts/templates/updateNeeded.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    {{translate 'oldAppAlert'}}

    4 |

    {{translate 'version'}}: {{latestTag}}

    5 |
    6 |
    7 | -------------------------------------------------------------------------------- /app/scripts/utils/platform.js: -------------------------------------------------------------------------------- 1 | define([], function () { 2 |  'use strict'; 3 | 4 | return { 5 | isFFOS11: function () { 6 | return (typeof navigator.mozTCPSocket.listen) !== 'function'; 7 | } 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /app/styles/_contacts.sass: -------------------------------------------------------------------------------- 1 | @mixin contacts-row 2 | border-bottom: 0.1rem solid #CCC 3 | margin-bottom: 2rem 4 | font-size: 1.7rem 5 | &:first-child 6 | margin-top: 1.5rem 7 | &:last-child 8 | margin-bottom: 0 9 | border: none 10 | -------------------------------------------------------------------------------- /app/styles/_location.sass: -------------------------------------------------------------------------------- 1 | form.locationScreen 2 | p.center, menu 3 | text-align: center 4 | p.hide 5 | display: none 6 | button 7 | width: 100% !important 8 | section.error 9 | p 10 | display: block 11 | p.center, h1 12 | display: none 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .tmp 4 | .sass-cache 5 | app/components 6 | Gemfile.lock 7 | .openwapp.sublime-project 8 | .openwapp.sublime-workspace 9 | .DS_Store 10 | grunt.local.json 11 | contacts.json 12 | test/cov 13 | app/scripts/templates.js 14 | app/styles/main.css 15 | -------------------------------------------------------------------------------- /app/styles/_migration.sass: -------------------------------------------------------------------------------- 1 | #migration-page section.main 2 | p 3 | margin-top: 0.5rem 4 | .progressvalue 5 | padding-right: 0.5rem 6 | &.spinner 7 | text-align: center 8 | progress 9 | margin-top: 1rem 10 | &.hidden 11 | display: none 12 | -------------------------------------------------------------------------------- /test/lib/expect.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * chai 3 | * Copyright(c) 2011-2012 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | module.exports = function (chai, util) { 8 | chai.expect = function (val, message) { 9 | return new chai.Assertion(val, message); 10 | }; 11 | }; 12 | 13 | -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/back.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/back.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/scripts/templates/compose-message.hbs: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /app/scripts/templates/index.hbs: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 |

    {{translate 'openWapp'}}

    5 |
    6 | 7 |
    8 |
    9 | 10 | {{translate 'loading'}} 11 |
    12 |
    13 |
    14 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Openwapp", 3 | "version": "1.9.4", 4 | "dependencies": { 5 | "requirejs": "~2.1.22", 6 | "underscore": "~1.8.3", 7 | "zepto": "~1.1.6", 8 | "backbone": "~1.1.2", 9 | "handlebars": "2.0.0", 10 | "PhoneNumber.js": "~0.5.0", 11 | "fxosRate": "~0.5.0", 12 | "coseme": "~1.1.3" 13 | }, 14 | "devDependencies": {} 15 | } 16 | -------------------------------------------------------------------------------- /app/scripts/templates/migration.hbs: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/images/shared/headers/images/icons/organic/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /app/scripts/templates/contact.hbs: -------------------------------------------------------------------------------- 1 | 2 | 4 |
    5 |
    Name
    6 |
    {{displayName}}
    7 |
    Subject
    8 |
    {{subject}}
    9 |
    State
    10 |
    {{state}}
    11 |
    12 |
    13 | {{#if showControls}} 14 | 16 | {{/if}} 17 | -------------------------------------------------------------------------------- /app/styles/_animations.scss: -------------------------------------------------------------------------------- 1 | @keyframes pulse { 2 | 0% { box-shadow: 0px 0px 2.1rem 1.4rem rgba(191, 249, 249, 0) } 3 | 16% { box-shadow: 0px 0px 2.1rem 1.4rem rgba(191, 249, 249, 1) } 4 | 33% { box-shadow: 0px 0px 2.1rem 1.4rem rgba(191, 249, 249, 0) } 5 | 50% { box-shadow: 0px 0px 2.1rem 1.4rem rgba(191, 249, 249, 0.33) } 6 | 66% { box-shadow: 0px 0px 2.1rem 1.4rem rgba(191, 249, 249, 0) } 7 | 84% { box-shadow: 0px 0px 2.1rem 1.4rem rgba(191, 249, 249, 0.66) } 8 | 100% { box-shadow: 0px 0px 2.1rem 1.4rem rgba(191, 249, 249, 0) } 9 | } 10 | -------------------------------------------------------------------------------- /app/styles/_validate.sass: -------------------------------------------------------------------------------- 1 | .page 2 | &.validate 3 | .screenname-page 4 | left: 100% 5 | .validate-page 6 | left: 0% 7 | &.screenname 8 | .screenname-page 9 | left: 0% 10 | .validate-page 11 | left: -100% 12 | 13 | #validate-page input[type=number] 14 | width: 9rem 15 | text-align: center 16 | 17 | #validate-page input[type=submit] 18 | margin-top: 0.6rem 19 | 20 | #validate-page button 21 | +secondary-action-button 22 | padding-top: 0 23 | margin-top: 1.8rem 24 | 25 | #validate-page 26 | +register-page 27 | -------------------------------------------------------------------------------- /app/styles/_compose_image.sass: -------------------------------------------------------------------------------- 1 | #compose-image 2 | text-align: center 3 | .loading 4 | margin-left: 10rem 5 | .page-wrapper 6 | max-height: -moz-calc(100% - #{$conversation-footer-height + $header-height}) 7 | max-height: -webkit-calc(100% - #{$conversation-footer-height + $header-height}) 8 | .viewer 9 | text-align: center 10 | padding: 0.9rem 1.9rem 0 1.9rem 11 | form#conversation-compose-image 12 | display: block 13 | & input[type=text] 14 | width: -moz-calc(100% - 1.3rem) 15 | width: -webkit-calc(100% - 1.3rem) 16 | -------------------------------------------------------------------------------- /app/scripts/templates/location-viewer.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | Back 4 |

    {{headerText}}

    5 |
    6 | 7 |
    8 |

    9 | 10 | {{translate 'loading'}} 11 |

    12 | 15 |
    16 |
    17 | -------------------------------------------------------------------------------- /app/scripts/templates/location-message.hbs: -------------------------------------------------------------------------------- 1 |
  • 2 | 3 | 4 | 5 | 6 | {{locationText}} 7 | 8 | 9 | 10 | {{#ifIsUnsent status }} 11 | 12 | {{/ifIsUnsent}} 13 | {{#ifIsSent status}} 14 | 15 | {{/ifIsSent}} 16 | {{#ifIsReceived status }} 17 | ✓✓ 18 | {{/ifIsReceived}} 19 |
  • 20 | -------------------------------------------------------------------------------- /app/scripts/templates/text-message.hbs: -------------------------------------------------------------------------------- 1 |
  • 2 | {{author}} 3 | {{expand contents}} 4 | 5 | {{#ifIsUnsent status }} 6 | {{! not traslated because it will be replaced with a picture }} 7 | 8 | {{/ifIsUnsent}} 9 | {{#ifIsSent status}} 10 | 11 | {{/ifIsSent}} 12 | {{#ifIsReceived status }} 13 | ✔✔ 14 | {{/ifIsReceived}} 15 |
  • 16 | -------------------------------------------------------------------------------- /app/styles/_settings.sass: -------------------------------------------------------------------------------- 1 | .page 2 | &.settings 3 | #settings 4 | left: 0% 5 | .tos-page 6 | left: 100% 7 | .privacy-page 8 | left: 100% 9 | &.tos 10 | #settings 11 | left: -100% 12 | &.privacy 13 | #settings 14 | left: -100% 15 | 16 | #settings 17 | background: #1e1e20 18 | +register-page 19 | 20 | #settings div.page-wrapper 21 | background: none 22 | 23 | #settings > header:first-child 24 | +dark-bg-header 25 | 26 | #settings > footer 27 | +dark-footer 28 | 29 | #settings h2 30 | +dark-highlight-row 31 | text-indent: 3rem 32 | 33 | #settings li 34 | +dark-row 35 | line-height: 4.4rem 36 | a 37 | +list-button 38 | padding-left: 0.7rem 39 | -------------------------------------------------------------------------------- /app/scripts/templates/mini-conversation.hbs: -------------------------------------------------------------------------------- 1 |
  • 4 | 5 |
    6 |
    Name
    7 |
    {{title}}
    8 |
    Last event
    9 |
    11 | {{lastMessage}}
    12 |
    Time
    13 |
    14 |
    15 | 17 |
    18 |
  • 19 | -------------------------------------------------------------------------------- /test/lib/JSCovReporter/reporter.js: -------------------------------------------------------------------------------- 1 | headings = []; 2 | 3 | onload = function(){ 4 | headings = document.querySelectorAll('h2'); 5 | }; 6 | 7 | onscroll = function(e){ 8 | var heading = find(window.scrollY); 9 | if (!heading) return; 10 | var links = document.querySelectorAll('#menu a') 11 | , link; 12 | 13 | for (var i = 0, len = links.length; i < len; ++i) { 14 | link = links[i]; 15 | link.className = link.getAttribute('href') == '#' + heading.id 16 | ? 'active' 17 | : ''; 18 | } 19 | }; 20 | 21 | function find(y) { 22 | var i = headings.length 23 | , heading; 24 | 25 | while (i--) { 26 | heading = headings[i]; 27 | if (y > heading.offsetTop) { 28 | return heading; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/scripts/utils/connectivity.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone' 3 | ], function (Backbone) { 4 | 'use strict'; 5 | 6 | var Connectivity = Backbone.Model.extend({ 7 | defaults: function () { 8 | return { 9 | online: navigator.onLine 10 | }; 11 | }, 12 | 13 | initialize: function () { 14 | var _this = this; 15 | window.addEventListener('online', function () { 16 | console.log('navigator has gone online'); 17 | _this.set({ online: true }); 18 | }); 19 | 20 | window.addEventListener('offline', function () { 21 | console.log('navigator has gone offline'); 22 | _this.set({ online: false }); 23 | }); 24 | } 25 | }); 26 | 27 | return new Connectivity(); 28 | }); 29 | -------------------------------------------------------------------------------- /app/locales/locales.ini: -------------------------------------------------------------------------------- 1 | @import url(openwapp.en-US.properties) 2 | @import url(fxosrate.en-US.properties) 3 | 4 | [de] 5 | @import url(openwapp.de.properties) 6 | 7 | [eo] 8 | @import url(openwapp.eo.properties) 9 | 10 | [es] 11 | @import url(openwapp.es.properties) 12 | @import url(fxosrate.es.properties) 13 | 14 | [fy-NL] 15 | @import url(openwapp.fy-NL.properties) 16 | 17 | [it] 18 | @import url(openwapp.it.properties) 19 | 20 | [nl] 21 | @import url(openwapp.nl.properties) 22 | 23 | [pt] 24 | @import url(openwapp.pt.properties) 25 | 26 | [pt-BR] 27 | @import url(openwapp.pt-BR.properties) 28 | @import url(fxosrate.pt-BR.properties) 29 | 30 | [ta-IN] 31 | @import url(openwapp.ta-IN.properties) 32 | 33 | [zh-CN] 34 | @import url(openwapp.zh-CN.properties) 35 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": false, 21 | "maxlen": 80, 22 | "maxstatements": 20, 23 | "maxcomplexity": 9, 24 | "maxdepth": 2, 25 | "predef": [ 26 | "define", 27 | "require", 28 | "describe", 29 | "it", 30 | "xit", 31 | "before", 32 | "beforeEach", 33 | "after", 34 | "afterEach", 35 | "sinon", 36 | "expect" 37 | ], 38 | "white": true 39 | } 40 | -------------------------------------------------------------------------------- /app/styles/shared/_responsive.scss: -------------------------------------------------------------------------------- 1 | /* 2 | This file is used to change the CSS sizes of the Gaia UI. 3 | 4 | It's usefulness will be reduced to virtually none once the platform part of 5 | https://bugzilla.mozilla.org/show_bug.cgi?id=838505 is landed and Gecko correctly reflect 6 | the device dpi and device pixel ratio to web content. 7 | */ 8 | 9 | /* NOTE: media queries to be able to use images in hi-res */ 10 | @mixin respond-to($media) { 11 | @if $media == peak { 12 | // portrait 13 | @media all and (min-device-width: 540px) and (max-device-width: 720px) and (max-device-aspect-ratio: 1/1) { 14 | @content; 15 | } 16 | // landscape 17 | @media all and (min-device-width: 960px) and (max-device-width: 1280px) and (min-device-aspect-ratio: 1/1) { 18 | @content; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OpenWapp 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
    20 |
    21 |
    22 |
    23 | 24 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/styles/shared/_toolbars.scss: -------------------------------------------------------------------------------- 1 | /* ---------------------------------- 2 | * Toolbars 3 | * ---------------------------------- */ 4 | 5 | [role="toolbar"] { 6 | height: 4rem; 7 | width: 100%; 8 | position: fixed; 9 | bottom: 0; 10 | left: 0; 11 | z-index: 100; 12 | background: rgba(0,0,0, 0.85); 13 | } 14 | 15 | [role="toolbar"] ul { 16 | float: left; 17 | list-style: none; 18 | padding: 0; 19 | margin: 0; 20 | } 21 | 22 | [role="toolbar"] ul:last-child { 23 | float: right; 24 | } 25 | 26 | [role="toolbar"] li { 27 | float: left; 28 | } 29 | 30 | [role="toolbar"] button { 31 | width: 5.5rem; 32 | height: 4rem; 33 | border: none; 34 | font-size: 0; 35 | background: transparent no-repeat 50% 50% / 3rem auto; 36 | padding: 0; 37 | border-radius: 0; 38 | } 39 | 40 | [role="toolbar"] button:active { 41 | background-color: #008aaa; 42 | } 43 | -------------------------------------------------------------------------------- /app/scripts/global.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'vendor/coseme-client/client', 3 | 'fxosrate' 4 | ], function(cosemeClient) { 5 | 'use strict'; 6 | 7 | // Initialize FirefoxOS Rate library 8 | window.fxosRate = Object.create(window.fxosRate); 9 | var fxosRateOpts = { 10 | daysUntilPrompt: 10, 11 | usesUntilPrompt: 5, 12 | eventsUntilPrompt: 5, 13 | remindPeriod: 7 14 | }; 15 | window.fxosRate.init('openwapp', '{{latestTag}}', fxosRateOpts); 16 | 17 | var client = cosemeClient.init(); 18 | 19 | return { 20 | maxStoredMessages: 300, // Number of messages stored before they are removed 21 | client: client, 22 | mapsAppCode: 'qkRqH6Iz3yKMrcflO44pZA', 23 | mapsAppId: 'CaXtXWGiQ1gicvatQdwy', 24 | gpsTimeout: 60000, //60 sec timeout to get current position 25 | httpTimeout: 30000, 26 | maxImageWidth: 900, 27 | maxImageHeight: 1200 28 | }; 29 | }); 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OpenWapp", 3 | "version": "1.9.4", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/mozillahispano/openwapp.git" 7 | }, 8 | "dependencies": {}, 9 | "devDependencies": { 10 | "grunt": "~0.4.5", 11 | "grunt-contrib-clean": "~0.6.0", 12 | "grunt-contrib-compass": "~1.0.3", 13 | "grunt-contrib-connect": "~0.10.1", 14 | "grunt-contrib-copy": "~0.8.0", 15 | "grunt-contrib-handlebars": "~0.10.2", 16 | "grunt-contrib-imagemin": "^0.9.4", 17 | "grunt-contrib-jshint": "~0.11.2", 18 | "grunt-coverjs": "~1.0.0", 19 | "grunt-mocha": "~0.4.13", 20 | "grunt-open": "~0.2.3", 21 | "grunt-regarde": "~0.1.1", 22 | "grunt-requirejs": "~0.4.2", 23 | "grunt-shell": "~1.1.2", 24 | "grunt-string-replace": "~1.2.0", 25 | "matchdep": "~0.3.0" 26 | }, 27 | "engines": { 28 | "node": ">=0.10.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/scripts/templates/compose-location.hbs: -------------------------------------------------------------------------------- 1 | 13 |
    14 |
    15 | Back 16 | 17 |

    {{translate 'sendLocation'}}

    18 |
    19 | 20 |
    21 |
    22 | 23 |
    24 |
    25 |
    26 | -------------------------------------------------------------------------------- /app/scripts/templates/compose-image.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | Back 4 | 5 | 6 | 7 |

    {{conversationTitle}}

    8 |
    9 | 10 |
    11 |

    12 | 13 | {{translate 'loading'}} 14 |

    15 | 16 |
    17 | 24 |
    25 | -------------------------------------------------------------------------------- /app/scripts/templates/multimedia-message.hbs: -------------------------------------------------------------------------------- 1 |
  • 2 | {{author}} 3 | {{translate type}}
    {{content.caption}}
    4 | 13 | 14 | {{#ifIsUnsent status }} 15 | {{! not traslated because it will be replaced with a picture }} 16 | 17 | {{/ifIsUnsent}} 18 | {{#ifIsSent status}} 19 | 20 | {{/ifIsSent}} 21 | {{#ifIsReceived status }} 22 | ✔✔ 23 | {{/ifIsReceived}} 24 |
  • 25 | -------------------------------------------------------------------------------- /app/scripts/templates/contact-profile.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 |

    {{displayName}}

    5 |
    6 | 7 |
    8 |
    9 |

    10 | 12 |

    13 |

    {{state}}

    14 |
    15 |
    16 |
    17 | 19 |
    20 |
    21 | 28 |
    29 |
    30 |
    31 |
    32 | -------------------------------------------------------------------------------- /app/styles/main.sass: -------------------------------------------------------------------------------- 1 | // compass modules 2 | @import compass/css3/appearance 3 | @import compass/css3/background-size 4 | @import compass/css3/border-radius 5 | @import compass/css3/box-shadow 6 | @import compass/css3/box-sizing 7 | @import compass/css3/images 8 | @import compass/css3/transform 9 | @import compass/reset 10 | @import compass/utilities/general/clearfix 11 | 12 | // gaia building blocks 13 | @import shared/_responsive 14 | @import shared/_headers 15 | @import shared/_progress_activity 16 | @import shared/_input_areas 17 | @import shared/_switches 18 | @import shared/_confirm 19 | @import shared/_toolbars 20 | @import shared/_buttons 21 | @import shared/_overrides 22 | 23 | // our own styles 24 | @import _common 25 | @import _common_register 26 | @import _login 27 | @import _validate 28 | @import _index 29 | @import _inbox 30 | @import _contacts 31 | @import _settings 32 | @import _profile 33 | @import _conversation 34 | @import _migration 35 | @import _location 36 | @import _compose_image 37 | @import _emoji 38 | @import _animations 39 | -------------------------------------------------------------------------------- /app/styles/shared/_overrides.scss: -------------------------------------------------------------------------------- 1 | section[role="region"] > header:first-child h1 { 2 | padding: 0 1rem 0 0; 3 | } 4 | 5 | section[role="region"] > header:first-child h1 a { 6 | font-size: 2rem; 7 | text-overflow: inherit; 8 | color: inherit; 9 | text-align: center; 10 | width: 100%; 11 | } 12 | 13 | section[role="region"] header h2 { 14 | background-color: rgba(255, 255, 255, 0.9); 15 | } 16 | 17 | section[role="region"] > header:first-child .icon.icon-location { 18 | background-image: url(/images/shared/headers/images/icons/location.png); 19 | } 20 | 21 | section[role="region"] > header:first-child .icon.icon-attach { 22 | background-image: url(/images/shared/headers/images/icons/attachments.png); 23 | } 24 | 25 | section[role="region"] > header:first-child .icon.icon-group { 26 | background-image: url(/images/shared/headers/images/icons/group.png); 27 | } 28 | 29 | [role="toolbar"] button:active { 30 | background: #008aaa no-repeat 50% 50% / 3rem auto; 31 | } 32 | 33 | section[role="region"] > header:first-child a { 34 | min-width: 3rem 35 | } 36 | -------------------------------------------------------------------------------- /app/scripts/views/index.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone', 3 | 'zeptojs', 4 | 'global', 5 | 'templates' 6 | ], function (Backbone, $, global, templates) { 7 | 'use strict'; 8 | 9 | var Index = Backbone.View.extend({ 10 | 11 | el: '#main-page', 12 | 13 | template: templates.index, 14 | 15 | initialize: function () { 16 | var validCredentials = global.auth.get('validCredentials'); 17 | 18 | // async database didn't finish 19 | if (validCredentials === null) { 20 | this.listenTo( 21 | global.auth, 'change:validCredentials', this._handleLogin); 22 | } 23 | else { 24 | this._handleLogin(global.auth); 25 | } 26 | }, 27 | 28 | _handleLogin: function () { 29 | var validCredentials = global.auth.get('validCredentials'); 30 | var nextScreen = validCredentials ? 'inbox' : 'login'; 31 | global.router.navigate(nextScreen, { trigger: true }); 32 | }, 33 | 34 | render: function () { 35 | this.$el.html(this.template()); 36 | } 37 | 38 | }); 39 | 40 | return Index; 41 | }); 42 | -------------------------------------------------------------------------------- /app/scripts/models/geoposition.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone' 3 | ], function (Backbone) { 4 | 'use strict'; 5 | 6 | var GeoPosition = Backbone.Model.extend({ 7 | defaults: function () { 8 | return { 9 | latitude: null, 10 | longitude: null 11 | }; 12 | }, 13 | 14 | update: function (maxTime) { 15 | var _this = this; 16 | navigator.geolocation.getCurrentPosition( 17 | function (position) { 18 | console.log('**** geolocation OK'); 19 | console.log('**** latitude: ' + position.coords.latitude); 20 | console.log('**** longitude: ' + position.coords.longitude); 21 | _this.set({ 22 | latitude: position.coords.latitude, 23 | longitude: position.coords.longitude 24 | }); 25 | _this.trigger('success', position); 26 | }, 27 | function (error) { 28 | _this.trigger('error', error); 29 | }, 30 | { 31 | timeout: maxTime 32 | } 33 | ); 34 | } 35 | }); 36 | 37 | return GeoPosition; 38 | }); 39 | -------------------------------------------------------------------------------- /contacts.json.sample: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "givenName": [ 4 | "Dark" 5 | ], 6 | "familyName": [ 7 | "Vador" 8 | ], 9 | "tel": [ 10 | { 11 | "value": "34612345678" 12 | } 13 | ] 14 | }, 15 | { 16 | "givenName": [ 17 | "Lionel" 18 | ], 19 | "familyName": [ 20 | "Messi" 21 | ], 22 | "tel": [ 23 | { 24 | "value": "34613245677" 25 | } 26 | ] 27 | }, 28 | { 29 | "givenName": [ 30 | "Angus" 31 | ], 32 | "familyName": [ 33 | "McGyver" 34 | ], 35 | "tel": [ 36 | { 37 | "value": "34612345676" 38 | } 39 | ] 40 | }, 41 | { 42 | "givenName": [ 43 | "Xavi" 44 | ], 45 | "familyName": [ 46 | "Hernandez" 47 | ], 48 | "tel": [ 49 | { 50 | "value": "3461234566" 51 | } 52 | ] 53 | }, 54 | { 55 | "givenName": [ 56 | "Mickael" 57 | ], 58 | "familyName": [ 59 | "Fernandez" 60 | ], 61 | "tel": [ 62 | { 63 | "value": "34677777777" 64 | } 65 | ] 66 | } 67 | ] 68 | -------------------------------------------------------------------------------- /app/scripts/utils/PhoneNumberMetaDataUpdates.js: -------------------------------------------------------------------------------- 1 | /* global PHONE_NUMBER_META_DATA */ 2 | /* jshint maxlen: 535 */ 3 | /** 4 | * This file is intented to patch our current version of PhoneNumber.js. 5 | * 1) Brazilian numbers 6 | */ 7 | define(['libphonenumber/PhoneNumberMetaData'], 8 | function () { 9 | 'use strict'; 10 | 11 | // Patching brazilian numbers. The rule only allowed a "119" as a mobile 12 | // prefix in some cases, that we change for: ^(?:1[1-9]|2[12478])9. 13 | // @see: http://en.wikipedia.org/wiki/Telephone_numbers_in_Brazil#Ninth_digit_for_mobile_numbers 14 | PHONE_NUMBER_META_DATA[55] = '["BR","00(?:1[45]|2[135]|[34]1|43)","0","0(?:(1[245]|2[135]|[34]1)(\\d{10,11}))?","$2",,"\\d{8,11}","[1-46-9]\\d{7,10}|5\\d{8,9}",[["(\\d{4})(\\d{4})","$1-$2","[2-9](?:[1-9]|0[1-9])","$FG","NA"],["(\\d{5})(\\d{4})","$1-$2","9(?:[1-9]|0[1-9])","$FG","NA"],["(\\d{2})(\\d{5})(\\d{4})","$1 $2-$3","^(?:1[1-9]|2[12478])9","($FG)",],["(\\d{2})(\\d{4})(\\d{4})","$1 $2-$3","[1-9][1-9]","($FG)",],["([34]00\\d)(\\d{4})","$1-$2","[34]00",,],["([3589]00)(\\d{2,3})(\\d{4})","$1 $2 $3","[3589]00","$NP$FG",]]]'; 15 | } 16 | ); -------------------------------------------------------------------------------- /app/scripts/utils/mimetype.js: -------------------------------------------------------------------------------- 1 | define([], function () { 2 |  'use strict'; 3 | 4 | // XXX: Use this source: 5 | // https://github.com/mozilla-b2g/gaia/blob/master/shared/js/mime_mapper.js 6 | var _extensionByMimeType = { 7 | // Image 8 | 'image/jpeg': 'jpg', 9 | 'image/png': 'png', 10 | 'image/gif': 'gif', 11 | 'image/bmp': 'bmp', 12 | // Audio 13 | 'audio/aac': 'm4a', 14 | 'audio/mpeg': 'mp3', 15 | 'audio/mp4': 'm4a', 16 | 'audio/ogg': 'ogg', 17 | 'audio/webm': 'webm', 18 | 'audio/3gpp': '3gp', 19 | 'audio/amr': 'amr', 20 | // Video 21 | 'video/mp4': 'mp4', 22 | 'video/mpeg': 'mpg', 23 | 'video/ogg': 'ogg', 24 | 'video/webm': 'webm', 25 | 'video/3gpp': '3gp', 26 | // Application 27 | // If we want to support some types, like pdf, just add 28 | // 'application/pdf': 'pdf' 29 | 'application/vcard': 'vcf', 30 | // Text 31 | 'text/vcard': 'vcf', 32 | 'text/x-vcard': 'vcf' 33 | }; 34 | 35 | return { 36 | getExtension: function (mimetype) { 37 | return _extensionByMimeType[mimetype]; 38 | } 39 | }; 40 | }); 41 | -------------------------------------------------------------------------------- /app/scripts/vendor/canvasToBlob/canvas-toBlob.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/canvas-toBlob.js/blob/master/canvas-toBlob.js */ 2 | (function(a){var b=a.Uint8Array,c=a.HTMLCanvasElement,d=/\s*;\s*base64\s*(?:;|$)/i,f,e=function(n){var o=n.length,k=new b(o/4*3|0),m=0,q=0,r=[0,0],g=0,p=0,l,h,j;while(o--){h=n.charCodeAt(m++);l=f[h-43];if(l!==255&&l!==j){r[1]=r[0];r[0]=h;p=(p<<6)|l;g++;if(g===4){k[q++]=p>>>16;if(r[1]!==61){k[q++]=p>>>8}if(r[0]!==61){k[q++]=p}g=0}}}return k};if(b){f=new b([62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,0,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51])}if(c&&!c.prototype.toBlob){c.prototype.toBlob=function(n,l){if(!l){l="image/png"}if(this.mozGetAsFile){n(this.mozGetAsFile("canvas",l));return}var k=Array.prototype.slice.call(arguments,1),i=this.toDataURL.apply(this,k),h=i.indexOf(","),m=i.substring(h+1),g=d.test(i.substring(0,h)),j;if(Blob.fake){j=new Blob;if(g){j.encoding="base64"}else{j.encoding="URI"}j.data=m;j.size=m.length}else{if(b){if(g){j=new Blob([e(m)],{type:l})}else{j=new Blob([decodeURIComponent(m)],{type:l})}}}n(j)}}}(self)); 3 | -------------------------------------------------------------------------------- /app/styles/_profile.sass: -------------------------------------------------------------------------------- 1 | #profile 2 | +register-page 3 | img 4 | max-width: 20rem 5 | max-height: 15rem 6 | width: auto 7 | height: auto 8 | fieldset legend 9 | width: 100% 10 | #revision 11 | text-align: center 12 | text-transform: none 13 | 14 | #profile button.leave, 15 | #profile button.add-participant 16 | margin-top: 1.5rem 17 | 18 | #profile .participants li 19 | +contacts-row 20 | +contact(6rem) 21 | dl 22 | width: calc(100% - 5rem) 23 | text-align: left 24 | 25 | #profile .details 26 | padding: 1rem 27 | 28 | button 29 | background-image: none 30 | background-color: #f2f2f2 31 | text-align: left 32 | font-weight: bold 33 | 34 | button:active 35 | background-color: #b2f2ff 36 | color: white 37 | 38 | button.icon 39 | background-repeat: no-repeat 40 | background-position: right 41 | 42 | button.icon-call 43 | background-image: url(/images/call.png) 44 | 45 | button.icon-message 46 | background-image: url(/images/sms.png) 47 | 48 | button.dial 49 | width: calc(100% - 5rem) 50 | 51 | button.send-sms 52 | float: right 53 | width: 4rem 54 | background-position: center 55 | -------------------------------------------------------------------------------- /app/scripts/templates/conversation.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | Back 4 | 5 | 6 | compose image 7 | 8 | send location 9 | 10 |

    11 | {{title}} 12 |

    13 |
    14 |
    15 |

    16 |

    {{translate 'conversationLastSeen'}}

    17 |

    {{translate 'conversationIsOnline'}}

    18 |

    {{translate 'loadingParticipants'}}

    19 |
    20 |
    21 | {{translate 'sendingImage'}} 22 | 23 |
    24 | 25 | 26 |
    27 |
      28 |
      29 |
      30 | -------------------------------------------------------------------------------- /app/styles/_emoji.sass: -------------------------------------------------------------------------------- 1 | #emoji-list 2 | min-height: 0 3 | height: 21rem 4 | background-color: #333 5 | overflow: hidden 6 | z-index: 100 7 | padding: 0.2rem 0 0 0 8 | position: absolute 9 | bottom: 0 10 | [role="toolbar"] 11 | position: absolute 12 | text-align: center 13 | ul:last-child 14 | width: 100% 15 | float: none 16 | ul:last-child li 17 | display: inline-block 18 | float: none 19 | 20 | #emoji-list .content 21 | height: calc(100% - 4rem - 0.2rem) 22 | overflow: auto 23 | text-align: center 24 | 25 | #emoji-list i.emoji 26 | margin-top: 1.2rem 27 | 28 | #emoji-list.hidden 29 | display: none 30 | 31 | #emoji-list .content li 32 | display: inline-block 33 | 34 | #emoji-list .emoji 35 | width: 4.5rem 36 | height: 4.5rem 37 | 38 | #emoji-list .icon.icon-people 39 | background-image: url(/images/people.png) 40 | 41 | #emoji-list .icon.icon-nature 42 | background-image: url(/images/nature.png) 43 | 44 | #emoji-list .icon.icon-objects 45 | background-image: url(/images/objects.png) 46 | 47 | #emoji-list .icon.icon-places 48 | background-image: url(/images/places.png) 49 | 50 | #emoji-list .icon.icon-symbols 51 | background-image: url(/images/symbols.png) 52 | -------------------------------------------------------------------------------- /app/scripts/templates/validate.hbs: -------------------------------------------------------------------------------- 1 |
      3 |
      4 |

      {{translate 'registerOpenWapp'}}

      5 |
      6 | 7 |
      8 |
      9 |

      10 | {{translate 'validateCode'}} 11 |
      12 | {{phoneNumber}} 13 |

      14 |
      15 |

      {{translate 'validateCodeProgress'}}

      16 | 17 |
      18 |
      19 | 20 |
      21 |
      22 |

      23 | 25 | - 26 | 28 |

      29 |

      30 | 31 |

      32 |

      33 | 34 |

      35 | 38 |
      39 |
      40 |
      41 |
      42 | -------------------------------------------------------------------------------- /app/styles/_login.sass: -------------------------------------------------------------------------------- 1 | .page 2 | &.init 3 | .register-page 4 | left: 0% 5 | .register-network-page 6 | left: 100% 7 | .tos-page 8 | left: 100% 9 | .privacy-page 10 | left: 100% 11 | .register-confirmation-page 12 | left: 100% 13 | &.network-prompt 14 | .register-page 15 | left: -100% 16 | .register-network-page 17 | left: 0% 18 | .tos-page 19 | left: 100% 20 | .privacy-page 21 | left: 100% 22 | .register-confirmation-page 23 | left: 100% 24 | &.confirmation 25 | .register-page 26 | left: -100% 27 | .register-network-page 28 | left: -100% 29 | .tos-page 30 | left: 100% 31 | .privacy-page 32 | left: 100% 33 | .register-confirmation-page 34 | left: 0% 35 | 36 | #login-page, #tos-page, #confirm-phone-page, #network-prompt-page 37 | +register-page 38 | 39 | #login-page input[type=submit], #confirm-phone-page input[type=submit] 40 | margin-top: 0.8rem 41 | 42 | #login-page section.main p 43 | margin-top: 0.5rem 44 | label 45 | margin-left: 0 46 | 47 | #confirm-phone-page button 48 | +secondary-action-button 49 | padding-top: 0 50 | margin-top: 1.8rem 51 | 52 | #confirm-phone-page legend 53 | background-color: #E6E6E6 54 | 55 | #login-page, #network-prompt-page 56 | #carrier-choose, #network-choose, #sim-choose 57 | width: 100% 58 | border-right: #ccc solid 0.1rem 59 | 60 | -------------------------------------------------------------------------------- /app/emoji/bin/mkcss.js: -------------------------------------------------------------------------------- 1 | 2 | var emojiData = require('./emoji_pretty.json'); 3 | 4 | var fs = require('fs'); 5 | var util = require('util'); 6 | 7 | var cssSpecs = [{ prefix: '', size: 20 }, { prefix: '#emoji-list ', size: 45 }]; 8 | 9 | cssSpecs.forEach(function (spec) { 10 | var cssFile = __dirname + '/../emoji-' + spec.size + 'px.css'; 11 | fs.open(cssFile , 'w', function (err, file) { 12 | writeIntro(file, spec); 13 | writeEmoji(file, spec); 14 | }); 15 | }); 16 | 17 | function writeIntro(file, spec) { 18 | var prefix = spec.prefix; 19 | var size = spec.size; 20 | var intro = util.format( 21 | prefix + 22 | '.emoji { background: url(./sheet_%d.png) top left no-repeat; ' + 23 | 'width: %dpx; height: %dpx; display: inline-block; vertical-align: top; }\n', 24 | size, size, size 25 | ); 26 | write(file, intro); 27 | } 28 | 29 | function writeEmoji(file, spec) { 30 | var prefix = spec.prefix; 31 | var size = spec.size; 32 | emojiData.forEach(function (emoji) { 33 | var code = 34 | emoji.unified.replace(/^0+([0-9a-f]{1,3})-/g, '$1').toLowerCase(); 35 | var cssSelector = '.emoji' + code; 36 | var rules = util.format( 37 | 'background-position: %dpx %dpx;', 38 | -emoji.sheet_x * size, 39 | -emoji.sheet_y * size 40 | ); 41 | write(file, prefix + cssSelector + ' { ' + rules + ' }\n'); 42 | }); 43 | } 44 | 45 | function write(file, data) { 46 | var buffer = new Buffer(data, 'utf-8'); 47 | fs.writeSync(file, buffer, 0, buffer.length, null); 48 | } 49 | -------------------------------------------------------------------------------- /app/scripts/router-migration.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone', 3 | 'underscore', 4 | 'global', 5 | 'views/migration' 6 | ], function (Backbone, _, global, MigrationView) { 7 | 'use strict'; 8 | 9 | var Router = Backbone.Router.extend({ 10 | initialize: function () { 11 | }, 12 | 13 | routes: { 14 | ':old/:new': 'migration' 15 | }, 16 | 17 | migration: function (oldVersion, newVersion) { 18 | var model = new Backbone.Model(); 19 | model.set('oldVersion', parseInt(oldVersion, 10)); 20 | model.set('newVersion', parseInt(newVersion, 10)); 21 | this.show(new MigrationView({ model: model })); 22 | }, 23 | 24 | show: function (view) { 25 | if (this.currentView) { 26 | // We use these methods instead of .remove() because remove() 27 | // deletes the View main element 28 | this.currentView.stopListening(); 29 | this.currentView.undelegateEvents(); 30 | this.currentView.$el.empty(); 31 | 32 | // If defined call the close method of the view 33 | if (typeof this.currentView.close !== 'undefined') { 34 | this.currentView.close(); 35 | } 36 | 37 | // Views can define a clear() method which should clean them for 38 | // avoiding memory leaks 39 | if (typeof this.currentView.clear !== 'undefined') { 40 | this.currentView.clear(); 41 | } 42 | } 43 | 44 | this.currentView = view; 45 | this.currentView.render(); 46 | } 47 | }); 48 | 49 | return Router; 50 | }); 51 | -------------------------------------------------------------------------------- /app/scripts/utils/language.js: -------------------------------------------------------------------------------- 1 | define([], function () { 2 |  'use strict'; 3 | 4 | return { 5 | /** 6 | * Get the user's locale. 7 | * @return String representing the language version as defined 8 | * in BCP 47. Examples of valid language codes 9 | * include "en", "en-US", "fr", "es-ES", etc. 10 | */ 11 | getLocale: function() { 12 | // in Gecko versions > 32 we may use `navigator.languages` to get 13 | // the preferred languages (see 14 | // https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages) 15 | return window.navigator.language; 16 | }, 17 | 18 | /** 19 | * Get the user's language (the first part of the locale). 20 | * @return String like "en", "fr", "de", etc. (always two characters) 21 | */ 22 | getLanguage: function () { 23 | return this.getLocale().substr(0, 2); 24 | }, 25 | 26 | /** 27 | * Get the user's locale if there is a translation for it. 28 | * Otherwise it returns the default language. 29 | */ 30 | getLegalLocale: function () { 31 | var availableToSLocales = { 32 | es: 'es', 33 | en: 'en-US', 34 | 'en-US': 'en-US', 35 | 'pt-br': 'pt-br', 36 | pt: 'pt-br', 37 | it: 'it', 38 | de: 'de', 39 | nl: 'nl' 40 | }; 41 | 42 | return availableToSLocales[this.getLocale()] || 43 | availableToSLocales[this.getLanguage()] || 44 | 'en'; // not sure if 'en' is correct here, should it not be 'en-US'? 45 | } 46 | }; 47 | }); 48 | -------------------------------------------------------------------------------- /app/scripts/views/contact-photo.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone', 3 | 'zeptojs', 4 | 'global', 5 | 'models/contact', 6 | 'templates' 7 | ], function (Backbone, $, global, contactModel, templates) { 8 | 'use strict'; 9 | var Contacts = Backbone.View.extend({ 10 | template: templates['contact-photo'], 11 | 12 | model: contactModel, 13 | 14 | photoURL: null, 15 | 16 | render: function () { 17 | this.listenTo(this.model, 'change:_photo', this._updatePicture); 18 | this._updatePicture(); 19 | return this; 20 | }, 21 | 22 | _updatePicture: function () { 23 | this._makeURL(); 24 | var newElement = this.template({ 25 | photoURL: this.photoURL 26 | }); 27 | if (this.$el) { 28 | this.$el.replaceWith(newElement); 29 | } 30 | this.setElement(newElement); 31 | 32 | // Revoke the URL as the image data is already loaded by the image. 33 | this.el.onload = function () { 34 | this._clear(); 35 | }.bind(this); 36 | }, 37 | 38 | _makeURL: function () { 39 | var photo = this.model.get('photo'); 40 | if (!photo) { return; } 41 | 42 | if (photo instanceof Blob) { 43 | this.photoURL = window.URL.createObjectURL(photo); 44 | } 45 | else { 46 | console.warning('The photo is not a blob.'); 47 | } 48 | }, 49 | 50 | _clear: function () { 51 | if (this.photoURL) { 52 | window.URL.revokeObjectURL(this.photoURL); 53 | this.photoURL = null; 54 | } 55 | } 56 | }); 57 | 58 | return Contacts; 59 | }); 60 | -------------------------------------------------------------------------------- /app/scripts/templates/settings.hbs: -------------------------------------------------------------------------------- 1 |
      2 |
      3 | 4 |

      {{translate 'settings'}}

      5 |
      6 |
      7 |

      {{translate 'settingsGeneral'}}

      8 | 14 |

      {{translate 'settingsAbout'}}

      15 | 21 |
      22 |
      23 | 24 |
      25 |
      26 | 29 |

      {{translate 'terms'}}

      30 |
      31 |
      32 |
      33 |
      34 |
      35 | 36 | 37 |
      39 |
      40 | 43 |

      {{translate 'privacyPolicy'}}

      44 |
      45 |
      46 |
      47 |
      48 |
      49 | -------------------------------------------------------------------------------- /app/scripts/utils/country.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone' 3 | ], function (Backbone) { 4 | 'use strict'; 5 | 6 | var Country = Backbone.Model.extend({ 7 | defaults: function () { 8 | return { 9 | carriers: {}, 10 | code: '', 11 | name: '', 12 | prefix: '' 13 | }; 14 | }, 15 | 16 | addMccMnc: function(mcc, mnc, networkname) { 17 | if (this.hasMccMnc(mcc, mnc)) { 18 | console.warn('Tried to add network with mcc ' + mcc + ' and mnc ' + 19 | mnc + ' multiple times to country ' + this.get('code')); 20 | } 21 | var networkList = this.get('networkList'); 22 | if (!networkList.hasOwnProperty(networkname)) { 23 | networkList[networkname] = []; 24 | } 25 | networkList[networkname].push({mcc: mcc, mnc: mnc}); 26 | }, 27 | 28 | toString: function () { 29 | return this.get('name'); 30 | }, 31 | 32 | hasMccMnc: function(mcc, mnc) { 33 | return this.getCarrier(mcc, mnc) !== null && true || false; 34 | }, 35 | 36 | getCarrier: function(mcc, mnc) { 37 | var carriers = this.get('carriers'), 38 | carrierList = Object.keys(carriers), 39 | neededCarrierList = carrierList.filter(function(carrierName) { 40 | return carriers[carrierName].filter(function(mccMnc) { 41 | return mccMnc.mcc === mcc && mccMnc.mnc === mnc; 42 | }).length > 0; 43 | }); 44 | if (neededCarrierList.length === 0) { 45 | return null; 46 | } else { 47 | return neededCarrierList[0]; 48 | } 49 | } 50 | }); 51 | 52 | return Country; 53 | }); 54 | -------------------------------------------------------------------------------- /app/scripts/utils/contact-picker.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'global', 3 | 'models/contact', 4 | 'utils/phonenumber' 5 | ], function (global, Contact, PhoneNumber) { 6 | 'use strict'; 7 | 8 | function pickContact(callback) { 9 | var pick = new window.MozActivity({ 10 | name: 'pick', 11 | data: { 12 | type: 'webcontacts/contact' 13 | } 14 | }); 15 | 16 | pick.onsuccess = function () { 17 | if (!this.result) { return callback(null, null); } 18 | 19 | var activityContact = this.result; 20 | var phone = activityContact.number; 21 | global.client.confirmContacts([phone], 22 | function (err, details) { 23 | 24 | var contact = new Contact({ 25 | 'displayName': activityContact.name[0] || '' 26 | }); 27 | 28 | if (err) { 29 | console.warn('The contact ' + phone + ' can not be confirmed.'); 30 | var parsed = PhoneNumber.parse(phone); 31 | var msisdn = parsed ? parsed.full : phone; 32 | contact.set({ 33 | 'id': msisdn, 34 | 'confirmed': false, 35 | 'phone': msisdn 36 | }); 37 | return callback('contact-not-confirmed', contact); 38 | } 39 | 40 | contact.set({ 41 | 'id': details[0].n, 42 | 'confirmed': !!details[0].w, 43 | 'state': details[0].s, 44 | 'phone': details[0].n 45 | }); 46 | 47 | callback(null, contact); 48 | } 49 | ); 50 | }; 51 | 52 | pick.onerror = function (evt) { 53 | callback(evt.target.error); 54 | }; 55 | } 56 | 57 | return pickContact; 58 | }); 59 | -------------------------------------------------------------------------------- /app/scripts/templates/group-profile.hbs: -------------------------------------------------------------------------------- 1 |
      2 |
      3 | 4 | 5 | 8 | 9 |

      10 | {{#if isEditMode}} 11 | {{translate 'groupProfile'}} 12 | {{else}} 13 | {{translate 'newGroup'}} 14 | {{/if}} 15 |

      16 |
      17 | 18 |
      19 |
      20 |
      21 |

      22 | {{translate 'groupPictureDescription'}} 24 |

      25 | 26 |

      {{translate 'groupPictureDescription'}}

      27 |

      28 | 31 | 32 |

      33 |

      {{translate 'subjectDescription'}}

      34 | {{#if showControls}} 35 | 37 | {{/if}} 38 |
        39 |
      40 | {{#if isEditMode}} 41 | 44 | {{/if}} 45 |
      46 |
      47 |
      48 |
      49 | -------------------------------------------------------------------------------- /app/scripts/views/location-message.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'underscore', 3 | 'backbone', 4 | 'zeptojs', 5 | 'global', 6 | 'models/message', 7 | 'templates' 8 | ], function (_, Backbone, $, global, Message, templates) { 9 | 'use strict'; 10 | 11 | return Backbone.View.extend({ 12 | 13 | template: templates['location-message'], 14 | 15 | model: Message, 16 | 17 | events: { 18 | }, 19 | 20 | initialize: function () { 21 | this.listenTo(this.model, 'change:status', this._changeStatus); 22 | }, 23 | 24 | clear: function () { 25 | this.stopListening(); 26 | }, 27 | 28 | render: function () { 29 | var data = _.extend(this.model.toJSON(), { 30 | locationText: this.model.get('contents').address ? 31 | this.model.get('contents').address : '' 32 | }); 33 | data.meta.timestamp = data.meta.sentDate.getTime(); 34 | var newElement = this.template(data); 35 | this.setElement(newElement); 36 | 37 | var latitude = this.model.get('contents').coords.latitude; 38 | var longitude = this.model.get('contents').coords.longitude; 39 | 40 | var img = this.$el.find('a img'); 41 | img.css('visibility', 'hidden'); 42 | 43 | var url = [ 44 | 'http://m.nok.it/?c=' + latitude + ',' + longitude, 45 | '&h=100&w=150&z=15&r=10&nord', 46 | '&app_code=' + global.mapsAppCode, 47 | '&app_id=' + global.mapsAppId 48 | ].join(''); 49 | 50 | 51 | img.attr('src', url); 52 | img.on('load', function () { 53 | img.css('visibility', 'visible'); 54 | }); 55 | }, 56 | 57 | _changeStatus: function () { 58 | var oldElement = this.$el; 59 | this.render(); 60 | oldElement.replaceWith(this.$el); 61 | } 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /app/scripts/views/text-message.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone', 3 | 'zeptojs', 4 | 'global', 5 | 'models/message', 6 | 'templates', 7 | 'templates/helpers' 8 | ], function (Backbone, $, global, Message, templates, helpers) { 9 | 'use strict'; 10 | 11 | return Backbone.View.extend({ 12 | 13 | template: templates['text-message'], 14 | 15 | model: Message, 16 | 17 | events: { 18 | 'click .resend': '_requestResend', 19 | 'click a': '_openInBrowser' 20 | }, 21 | 22 | initialize: function () { 23 | this.listenTo(this.model, 'change:status', this._changeStatus); 24 | }, 25 | 26 | clear: function () { 27 | this.stopListening(); 28 | }, 29 | 30 | render: function () { 31 | var jsonModel = this.model.toJSON(); 32 | jsonModel.meta.timestamp = jsonModel.meta.sentDate.getTime(); 33 | if (jsonModel.from.authorMsisdn) { 34 | jsonModel.author = 35 | global.contacts.getParticipantName(jsonModel.from.authorMsisdn); 36 | } 37 | else { 38 | jsonModel.author = jsonModel.from.displayName; 39 | } 40 | var newElement = this.template(jsonModel); 41 | this.setElement(newElement); 42 | 43 | helpers.revealEmoji(this.$el.find('span.content')); 44 | }, 45 | 46 | _requestResend: function () { 47 | console.log('Request to send message', this.model.get('contents')); 48 | this.trigger('message:resend', this.model); 49 | }, 50 | 51 | _changeStatus: function () { 52 | var oldElement = this.$el; 53 | this.render(); 54 | oldElement.replaceWith(this.$el); 55 | }, 56 | 57 | _openInBrowser: function (evt) { 58 | evt.preventDefault(); 59 | new window.MozActivity({ 60 | name: 'view', 61 | data: { type: 'url', url: evt.target.href } 62 | }); 63 | } 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /enable-remote-debugging.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Defines two properties needs for firefoxOS remote debugging 4 | # 5 | # Usage: 6 | # ./enable-remote-debugging.sh gaia_home 7 | # 8 | 9 | # KNOWN BUG: 10 | # The remote port is actually ignored and it launches listening 11 | # in 2828 (marionette port). 12 | # So, just connect to localhost:2828 from Firefox remote debugger 13 | 14 | # This has been tested with the v1.0.1 branch of gaia. Make 15 | # sure you are in that branch by going to wherever you have 16 | # gaia and running git checkout v1.0.1. 17 | # ------------------------------------------------------------ 18 | 19 | GAIA_PATH=$1 20 | CUSTOM_SETTINGS="${GAIA_PATH}/build/custom-settings.json" 21 | 22 | if [[ ! -d "${GAIA_PATH}" ]] || [[ ! -d "${GAIA_PATH}/build" ]] ; then 23 | echo "Unknown folder ${GAIA_PATH}" 24 | echo "" 25 | echo "Usage:" 26 | echo "$0 gaia_home" 27 | echo "" 28 | echo "Example: $0 ~/Documents/code/gaia" 29 | exit 0 30 | fi 31 | 32 | # see https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS/Customization_with_the_.userconfig_file 33 | # also, see http://blog.astithas.com/2012/10/debugging-firefox-os.html 34 | cat > "${CUSTOM_SETTINGS}.new" <& /dev/null; then 45 | rm "${CUSTOM_SETTINGS}.new" 46 | else 47 | mv "${CUSTOM_SETTINGS}.new" "${CUSTOM_SETTINGS}" 48 | fi 49 | 50 | # Equivalent when running by hand: 51 | # 52 | #echo "user_pref(\"devtools.debugger.remote-enabled\", true);" >>${GAIA_PATH}/profile/prefs.js 53 | #echo "user_pref(\"marionette.defaultPrefs.enabled\", false);" >>${GAIA_PATH}/profile/prefs.js 54 | #echo "user_pref(\"devtools.debugger.remote-port\", 7000);" >>${GAIA_PATH}/profile/prefs.js 55 | 56 | -------------------------------------------------------------------------------- /app/emoji/bin/mkselector.js: -------------------------------------------------------------------------------- 1 | 2 | 'use stricts' 3 | 4 | var categories = require('./emoji-categories.json'); 5 | 6 | var fs = require('fs'); 7 | var util = require('util'); 8 | 9 | var templateFile = __dirname + '/../emoji-selector.hbs'; 10 | fs.open(templateFile, 'w', function (err, file) { 11 | writeIntro(file); 12 | writeCategories(file); 13 | writeOutro(file); 14 | }); 15 | 16 | function writeIntro(file) { 17 | var intro = ''); 61 | } 62 | 63 | function write(file, data) { 64 | var buffer = new Buffer(data, 'utf-8'); 65 | fs.writeSync(file, buffer, 0, buffer.length, null); 66 | } 67 | -------------------------------------------------------------------------------- /app/styles/shared/_progress_activity.scss: -------------------------------------------------------------------------------- 1 | /* ---------------------------------- 2 | * Progress + activities 3 | * ---------------------------------- */ 4 | 5 | /* Spinner */ 6 | progress:not([value]) { 7 | background: url(/images/shared/progress_activity/default.png) no-repeat center center / 100% auto; 8 | @include respond-to(peak) { 9 | background-image: url(/images/shared/progress_activity/default2x.png); 10 | } 11 | animation: 0.9s rotate infinite steps(30); 12 | width: 2.9rem; 13 | height: 2.9rem; 14 | border: none; 15 | display: inline-block; 16 | vertical-align: middle; 17 | } 18 | 19 | progress:not([value])::-moz-progress-bar { 20 | background: none; 21 | } 22 | 23 | /* Progress bar */ 24 | progress[value] { 25 | width: 100%; 26 | background: #333; 27 | border: none; 28 | border-radius: 0.3rem; 29 | height: 0.3rem; 30 | display: block; 31 | } 32 | 33 | progress[value]::-moz-progress-bar { 34 | background: #01c5ed; 35 | border-radius: 0; 36 | } 37 | 38 | /* Progress bar */ 39 | progress[value].pack-activity { 40 | background-image: url(/images/shared/progress_activity/activity.png); 41 | background-repeat: repeat; 42 | background-size: auto 100%; 43 | animation: 0.5s move infinite steps(15); 44 | } 45 | 46 | /* Labels */ 47 | p[role="status"] { 48 | padding: 0.5rem; 49 | font: 300 2.25rem/1.4em "MozTT", Sans-serif; 50 | color: #fff; 51 | } 52 | 53 | p[role="status"] progress:not([value]) { 54 | margin-right: 0.5rem; 55 | } 56 | 57 | p[role="status"] progress:not([value]) + span { 58 | margin-bottom: 0; 59 | } 60 | 61 | p[role="status"] span { 62 | display: inline-block; 63 | vertical-align: middle; 64 | margin-bottom: 0.5rem; 65 | } 66 | 67 | /* Light Activity bar */ 68 | progress[value].pack-activity.light { 69 | background-color: #f4f4f4; 70 | } 71 | 72 | 73 | /* Animations */ 74 | @keyframes rotate { 75 | from { transform: rotate(1deg); } 76 | to { transform: rotate(360deg); } 77 | } 78 | 79 | @keyframes move { 80 | from { background-position: 0 0; } 81 | to { background-position: 1rem 0; } 82 | } 83 | -------------------------------------------------------------------------------- /test/lib/asyncstorage-mock.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'underscore', 3 | 'vendor/async-storage/async-storage' 4 | ], function (_, AsyncStorage) { 5 | 'use strict'; 6 | 7 | // Mock for AsyncStorage library 8 | // 9 | // replaces it with an in-memory storage. 10 | // 11 | // Uses sinon to create/restore mocks. 12 | // 13 | // setFakeStorage(): activate it. 14 | // releaseFakeStorage(): deactivate it after having activated. 15 | // 16 | 17 | var asyncStorageMock = {}; 18 | 19 | asyncStorageMock.stubs = []; 20 | 21 | asyncStorageMock.setFakeStorage = function () { 22 | 23 | asyncStorageMock.simulatedStorage = {}; // where data is actually stored 24 | 25 | asyncStorageMock.stubs.push(sinon.stub(AsyncStorage, 'getItem', 26 | function (key, callback) { 27 | callback(asyncStorageMock.simulatedStorage[key]); 28 | })); 29 | 30 | asyncStorageMock.stubs.push(sinon.stub(AsyncStorage, 'setItem', 31 | function (key, attributes, callback) { 32 | asyncStorageMock.simulatedStorage[key] = _.clone(attributes); 33 | if (callback) { callback(); } 34 | })); 35 | 36 | asyncStorageMock.stubs.push(sinon.stub(AsyncStorage, 'removeItem', 37 | function (key, callback) { 38 | delete asyncStorageMock.simulatedStorage[key]; 39 | if (callback) { callback(); } 40 | })); 41 | 42 | asyncStorageMock.stubs.push(sinon.stub(AsyncStorage, 'length', 43 | function (callback) { 44 | var size = 0, key; 45 | for (key in asyncStorageMock.simulatedStorage) { 46 | if (asyncStorageMock.simulatedStorage.hasOwnProperty(key)) { size++; } 47 | } 48 | if (callback) { callback(size); } 49 | })); 50 | 51 | asyncStorageMock.stubs.push(sinon.stub(AsyncStorage, 'clear', 52 | function (callback) { 53 | asyncStorageMock.simulatedStorage = {}; 54 | if (callback) { callback(); } 55 | })); 56 | }; 57 | 58 | asyncStorageMock.releaseFakeStorage = function () { 59 | asyncStorageMock.stubs.forEach(function (stub) { 60 | stub.restore(); 61 | }); 62 | asyncStorageMock.stubs = []; 63 | }; 64 | 65 | return asyncStorageMock; 66 | }); -------------------------------------------------------------------------------- /app/scripts/views/contact.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'underscore', 3 | 'backbone', 4 | 'zeptojs', 5 | 'global', 6 | 'models/contact', 7 | 'views/contact-photo', 8 | 'templates', 9 | 'templates/helpers' 10 | ], function (_, Backbone, $, global, contactModel, ContactPhoto, templates, 11 | helpers) { 12 | 'use strict'; 13 | 14 | var Contacts = Backbone.View.extend({ 15 | tagName: 'li', 16 | 17 | template: templates.contact, 18 | 19 | model: contactModel, 20 | 21 | contactPhoto: null, 22 | 23 | events: { 24 | 'click span': '_goToConversation' 25 | }, 26 | 27 | initialize: function (options) { 28 | this.listenTo(this.model, 'change:state', this.render); 29 | this.listenTo(this.model, 'change:displayName', this.render); 30 | this.listenTo(this.model, 'change:subject', this.render); 31 | this.listenTo(this.model, 'change:_photo', this.render); 32 | this.showControls = !!options.showControls; 33 | }, 34 | 35 | render: function () { 36 | // link this contact to a conversation with it, if phone number, inbox 37 | // if no phone numbers are defined 38 | var phone = this.model.get('phone'); 39 | var convURI = phone ? 'conversation/' + phone : 'inbox'; 40 | var data = _.extend(this.model.toJSON(), { 41 | conversationUri: convURI, 42 | showControls: this.showControls 43 | }); 44 | this.$el.html(this.template(data)); 45 | 46 | if (this.model.get('isGroup')) { 47 | this.$el.addClass('group'); 48 | } 49 | 50 | if (!this.contactPhoto) { 51 | this.contactPhoto = new ContactPhoto({ model: this.model }); 52 | } 53 | 54 | this.$el.find('aside').append(this.contactPhoto.render().el); 55 | helpers.revealEmoji(this.$el.find('dd.state')); 56 | 57 | return this; 58 | }, 59 | 60 | _goToConversation: function (evt) { 61 | if (evt) { evt.preventDefault(); } 62 | global.router.navigate('conversation/' + this.model.get('id'), 63 | {trigger: true}); 64 | }, 65 | 66 | clear: function () {} 67 | }); 68 | 69 | return Contacts; 70 | }); 71 | -------------------------------------------------------------------------------- /app/scripts/storage/auth.js: -------------------------------------------------------------------------------- 1 | define(['vendor/async-storage/async-storage'], function (AsyncStorage) { 2 | 'use strict'; 3 | 4 | return { 5 | store: function (userId, password, msisdn, mcc, mnc, profile, callback) { 6 | AsyncStorage.setItem('credentials', { 7 | userId: userId, 8 | password: password, 9 | msisdn: msisdn, 10 | profile: { 11 | screenName: profile.screenName, 12 | status: profile.status, 13 | photo: profile.photo, 14 | thumb: profile.thumb 15 | }, 16 | mcc: mcc, 17 | mnc: mnc 18 | }, callback); 19 | }, 20 | 21 | storeProfileData: function (screenName, status, photo, thumb, callback) { 22 | AsyncStorage.getItem('credentials', function (credentials) { 23 | if (credentials) { 24 | AsyncStorage.setItem('credentials', { 25 | userId: credentials.userId, 26 | password: credentials.password, 27 | msisdn: credentials.msisdn, 28 | profile: { 29 | screenName: screenName, 30 | status: status, 31 | photo: photo, 32 | thumb: thumb 33 | }, 34 | mcc: credentials.mcc, 35 | mnc: credentials.mnc 36 | }, callback); 37 | } 38 | 39 | }); 40 | 41 | }, 42 | 43 | load: function (callback) { 44 | AsyncStorage.getItem('credentials', function (credentials) { 45 | var userId = null, 46 | password = null, 47 | msisdn = null, 48 | profile = null, 49 | mcc = '000', 50 | mnc = '000'; 51 | 52 | if (credentials) { 53 | userId = credentials.userId; 54 | password = credentials.password; 55 | msisdn = credentials.msisdn; 56 | profile = credentials.profile; 57 | mcc = credentials.mcc; 58 | mnc = credentials.mnc; 59 | } 60 | if (callback) { 61 | callback(userId, password, msisdn, mcc, mnc, profile); 62 | } 63 | }); 64 | }, 65 | 66 | clear: function (callback) { 67 | AsyncStorage.removeItem('credentials', callback); 68 | } 69 | }; 70 | }); 71 | -------------------------------------------------------------------------------- /app/scripts/utils/thumbnail.js: -------------------------------------------------------------------------------- 1 | define([], function () { 2 | 'use strict'; 3 | 4 | var DEFAULT_MAX_SIZE = 120; 5 | 6 | return { 7 | setMaxSize: function (maxSize) { 8 | this.maxSize = maxSize; 9 | }, 10 | generate: function (imageBlob, callback, options) { 11 | options = options || {}; 12 | var maxSize = this.maxSize || DEFAULT_MAX_SIZE; 13 | var quality = options.quality || 0.8; 14 | var forceSquare = options.forceSquare || false; 15 | 16 | var fileReader = new FileReader(); 17 | fileReader.readAsDataURL(imageBlob); 18 | 19 | fileReader.onload = function (evt) { 20 | var img = document.createElement('img'); 21 | 22 | img.onload = function () { 23 | var width = img.width, 24 | height = img.height; 25 | 26 | var scale = Math.min(maxSize / Math.max(height, width), 1); 27 | var ratio = width / height; 28 | 29 | var c = document.createElement('canvas'); 30 | var newWidth = width * scale; 31 | var newHeight = height * scale; 32 | if (forceSquare) { 33 | c.width = Math.max(newWidth, newHeight); 34 | c.height = c.width; 35 | } 36 | else { 37 | c.width = newWidth; 38 | c.height = newHeight; 39 | } 40 | 41 | var ctx = c.getContext('2d'); 42 | var offsetX = (c.width - newWidth) / 2; 43 | var offsetY = (c.height - newHeight) / 2; 44 | ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight); 45 | 46 | if (options.asBlob) { 47 | c.toBlob(function (blob) { 48 | callback(null, blob, ratio); 49 | }, 'image/jpeg', quality); 50 | } else { 51 | callback( 52 | null, c.toDataURL('image/jpeg').split('base64,')[1], ratio); 53 | } 54 | }; 55 | 56 | img.onerror = function (err) { 57 | callback(err); 58 | }; 59 | 60 | // The `src` of the image should be set after the event handler 61 | img.src = evt.target.result; 62 | }; 63 | 64 | fileReader.onerror = function (evt) { 65 | callback(evt); 66 | }; 67 | } 68 | }; 69 | }); 70 | -------------------------------------------------------------------------------- /app/scripts/collections/countries.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone', 3 | 'underscore', 4 | 'utils/country', 5 | 'global' 6 | ], function (Backbone, _, Country) { 7 | 'use strict'; 8 | 9 | var Countries = Backbone.Collection.extend({ 10 | model: Country, 11 | 12 | initialize: function () { 13 | this.mccMncMap = new Map(); 14 | this.fetchCountries(); 15 | }, 16 | 17 | comparator: function (item) { 18 | return item.get('name'); 19 | }, 20 | 21 | addCountry: function (country) { 22 | var carrier, 23 | _this = this, 24 | newCountry = new Country({ 25 | carriers: country.carriers, 26 | code: country.code, 27 | name: country.full, 28 | prefix: country.prefix 29 | }), 30 | addNetworkToMap = function(network) { 31 | _this.mccMncMap.set(network.mcc + '-' + network.mnc, newCountry); 32 | }; 33 | this.add(newCountry); 34 | for (carrier in country.carriers){ 35 | if (country.carriers.hasOwnProperty(carrier)) { 36 | country.carriers[carrier].map(addNetworkToMap); 37 | } 38 | } 39 | }, 40 | 41 | fetchCountries: function () { 42 | // Get from the countries.json. responseType to json is not allowed, see 43 | // http://updates.html5rocks.com/2012/01/Getting-Rid-of-Synchronous-XHRs 44 | var xhr = new XMLHttpRequest(); 45 | xhr.open('GET', '/scripts/countries.json', false); // sync request 46 | xhr.send(null); 47 | 48 | if (xhr.status === 200) { 49 | var parsed = {}; 50 | try { 51 | parsed = JSON.parse(xhr.responseText); 52 | } catch (e) { 53 | console.error('Something happened while trying to parse the JSON', e); 54 | } 55 | parsed.map(this.addCountry, this); 56 | } else { 57 | console.error(xhr.statusText); 58 | } 59 | }, 60 | 61 | getSelectedCountry: function (value) { 62 | return this.find(function (model) { 63 | return model.get('code') === value; 64 | }); 65 | }, 66 | 67 | getCountryByMccMnc: function (mcc, mnc) { 68 | return this.mccMncMap.get(mcc + '-' + mnc); 69 | } 70 | }); 71 | 72 | return Countries; 73 | }); 74 | -------------------------------------------------------------------------------- /app/scripts/views/mini-conversation.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone', 3 | 'zeptojs', 4 | 'global', 5 | 'models/conversation', 6 | 'views/contact-photo', 7 | 'templates', 8 | 'templates/helpers' 9 | ], function (Backbone, $, global, Conversation, ContactPhoto, templates, 10 | helpers) { 11 | 'use strict'; 12 | 13 | return Backbone.View.extend({ 14 | 15 | template: templates['mini-conversation'], 16 | 17 | model: Conversation, 18 | 19 | el: 'li', 20 | 21 | events: { 22 | 'click li > a' : '_openConversation' 23 | }, 24 | 25 | initialize: function () { 26 | this.contactPhotoURL = null; 27 | this.listenTo(this.model, 'change', this._refreshRender); 28 | var messages = this.model.get('messages'); 29 | if (messages) { 30 | this.listenTo(messages, 'add', this._refreshRender); 31 | } 32 | }, 33 | 34 | clear: function () { 35 | if (this.contactPhotoURL) { 36 | window.URL.revokeObjectURL(this.contactPhotoURL); 37 | } 38 | }, 39 | 40 | render: function () { 41 | var json = this.model.toJSON(); 42 | 43 | // Make last message always the latest if there are messages, 44 | // else use whatever is there (e.g. what was recovered from storage) 45 | var messages = this.model.get('messages'); 46 | if (messages && messages.size()) { 47 | var last = messages.at(messages.size() - 1); 48 | json.lastMessage = last.getSummary(); 49 | json.date = this.model.get('date'); 50 | } 51 | 52 | var newElement = this.template(json); 53 | this.setElement(newElement); 54 | 55 | var contact = this.model.get('contact'); 56 | if (contact) { 57 | this.contactPhoto = new ContactPhoto({ model: contact }); 58 | this.$el.find('.avatar').append(this.contactPhoto.render().el); 59 | } 60 | helpers.revealEmoji(this.$el.find('dd.name')); 61 | helpers.revealEmoji(this.$el.find('dd.event')); 62 | }, 63 | 64 | // re-render in current element 65 | _refreshRender: function () { 66 | if (this.$el) { 67 | var oldElement = this.$el; 68 | this.render(); 69 | oldElement.replaceWith(this.$el); 70 | } 71 | }, 72 | 73 | _openConversation: function (event) { 74 | event.preventDefault(); 75 | global.router.navigate('conversation/' + this.model.get('id'), 76 | {trigger: true}); 77 | } 78 | }); 79 | 80 | }); 81 | -------------------------------------------------------------------------------- /app/scripts/views/notification-message.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone', 3 | 'zeptojs', 4 | 'global', 5 | 'models/message', 6 | 'templates', 7 | 'templates/helpers' 8 | ], function (Backbone, $, global, Message, templates, helpers) { 9 | 'use strict'; 10 | 11 | return Backbone.View.extend({ 12 | 13 | template: templates.notification, 14 | 15 | el: '.notification', 16 | 17 | model: Message, 18 | 19 | render: function () { 20 | var newElement = this.template({ 21 | timestamp: this.model.get('meta').date.getTime(), 22 | date: this.model.get('meta').date, 23 | message: this._getMessage() 24 | }); 25 | this.setElement(newElement); 26 | helpers.revealEmoji(this.$el.find('.content')); 27 | }, 28 | 29 | _getMessage: function () { 30 | var stringId, message, who; 31 | var notification = this.model; 32 | var contents = notification.get('contents'); 33 | var type = notification.get('meta').type; 34 | 35 | switch (type) { 36 | case 'group-subject': 37 | who = notification.get('from').authorMsisdn; 38 | stringId = 'notificationSubjectChanged'; 39 | if (global.auth.isMe(who)) { 40 | stringId += 'ByYou'; 41 | } 42 | message = navigator.mozL10n.get([stringId], { 43 | 'who': global.contacts.getParticipantName(who), 44 | 'subject': contents.subject 45 | }); 46 | break; 47 | 48 | case 'group-participant': 49 | stringId = 'notificationGroupParticipant'; 50 | stringId += contents.event === 'add' ? 'Added' : 'Removed'; 51 | message = navigator.mozL10n.get([stringId], { 52 | 'who': global.contacts.getParticipantName(contents.participant) 53 | }); 54 | break; 55 | 56 | case 'group-picture': 57 | stringId = 'notificationGroupPicture'; 58 | stringId += contents.event === 'update' ? 'Updated' : 'Removed'; 59 | who = notification.get('from').authorMsisdn; 60 | if (global.auth.isMe(who)) { 61 | stringId += 'ByYou'; 62 | message = navigator.mozL10n.get([stringId]); 63 | } else { 64 | message = navigator.mozL10n.get([stringId], { 65 | 'who' : global.contacts.getParticipantName(who) 66 | }); 67 | } 68 | break; 69 | 70 | default: 71 | message = 'unknown notification!'; 72 | break; 73 | } 74 | return message; 75 | } 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /app/scripts/templates/profile.hbs: -------------------------------------------------------------------------------- 1 |
      2 |
      3 | 4 | 6 |

      {{translate 'profile'}}

      7 |
      8 | 9 |
      10 | 21 |
      22 |
      23 |

      24 | Avatar 26 |

      27 | 28 |

      {{translate 'pictureDescription'}}

      29 |

      30 | 34 | 35 |

      36 |

      {{translate 'screenNameDescription'}}

      37 |

      38 | 41 | 42 |

      43 |

      {{translate 'statusDescription'}}

      44 |

      45 |

      46 | {{translate 'wakeUpTime'}} 47 |
      48 |

      49 |

      {{translate 'wakeUpDescription'}}

      50 |

      51 | 52 | 53 |

      54 |
      55 |
      56 |

      {{translate 'accountExpiration'}}: {{formattedMessageDate expiration}}

      57 | {{translate 'upgradeNow'}} 58 |
      59 |

      {{translate 'version'}}: {{latestTag}}

      60 |

      rev: {{currentCommit}}

      61 |
      62 |
      63 |
      64 | -------------------------------------------------------------------------------- /app/scripts/views/location-viewer.js: -------------------------------------------------------------------------------- 1 | /* global MozActivity */ 2 | define([ 3 | 'underscore', 4 | 'backbone', 5 | 'zeptojs', 6 | 'global', 7 | 'models/message', 8 | 'templates' 9 | ], function (_, Backbone, $, global, Message, templates) { 10 | 'use strict'; 11 | 12 | return Backbone.View.extend({ 13 | el: '#main-page', 14 | 15 | template: templates['location-viewer'], 16 | 17 | model: Message, 18 | 19 | events: { 20 | 'click img' : 'openGoogleMaps' 21 | }, 22 | 23 | initialize: function (options) { 24 | this.conversation = options.conversation; 25 | this.scrollTop = options.scrollTop; 26 | }, 27 | 28 | clear: function () { 29 | this.stopListening(); 30 | }, 31 | 32 | render: function () { 33 | var data = _.extend(this.model.toJSON(), { 34 | headerText: this.model.get('contents').address ? 35 | this.model.get('contents').address : '', 36 | scrollTop: this.scrollTop 37 | }); 38 | this.$el.html(this.template(data)); 39 | 40 | this.renderLocation(); 41 | }, 42 | 43 | renderLocation: function () { 44 | var latitude = this.model.get('contents').coords.latitude; 45 | var longitude = this.model.get('contents').coords.longitude; 46 | 47 | var maxWidth = this.$el.find('.page-wrapper').width(); 48 | var maxHeight = this.$el.find('.page-wrapper').height(); 49 | 50 | var img = $(''); 51 | this.$el.find('.viewer').append(img); 52 | 53 | var url = [ 54 | 'http://m.nok.it/?c=' + latitude + ',' + longitude, 55 | '&h=' + maxHeight + '&w=' + maxWidth + '&z=15&r=10&nord', 56 | '&app_code=' + global.mapsAppCode, 57 | '&app_id=' + global.mapsAppId 58 | ].join(''); 59 | 60 | img.attr('src', url); 61 | this.$el.find('.viewer').removeClass('image'); 62 | this.$el.find('.viewer').height(maxHeight).width(maxWidth).show(); 63 | this.$el.find('.loading').hide(); 64 | }, 65 | 66 | openGoogleMaps: function () { 67 | var latitude = this.model.get('contents').coords.latitude; 68 | var longitude = this.model.get('contents').coords.longitude; 69 | var address = this.model.get('contents').address; 70 | 71 | var mapsUrl = [ 72 | 'http://m.here.com/#action=search¶ms={"latitude":' + latitude + 73 | ',"longitude":' + longitude + ',"address": "' + address + '"}&bmk=1' 74 | ].join(''); 75 | 76 | console.log('opening map', mapsUrl); 77 | 78 | new MozActivity({ 79 | name: 'view', 80 | data: { 81 | type: 'url', 82 | url: mapsUrl 83 | } 84 | }); 85 | } 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /app/scripts/main.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | baseUrl: '/scripts/', 3 | paths: { 4 | zeptojs: '../components/zepto/zepto.min', 5 | jquery: '../components/zepto/zepto', 6 | underscore: '../components/underscore/underscore-min', 7 | backbone: '../components/backbone/backbone', 8 | handlebars: '../components/handlebars/handlebars.min', 9 | rtc: 'vendor/ottcomms-rtc-web/rtc', 10 | libphonenumber: '../components/PhoneNumber.js', 11 | coseme: '../components/coseme/dist/coseme', 12 | emoji: '../emoji/emoji', 13 | fxosrate: '../components/fxosRate/fxosrate', 14 | 15 | }, 16 | shim: { 17 | 'zeptojs': { 18 | exports: '$' 19 | }, 20 | 'underscore': { 21 | exports: '_' 22 | }, 23 | 'backbone': { 24 | deps: ['underscore', 'jquery'], 25 | exports: 'Backbone' 26 | }, 27 | 'handlebars': { 28 | exports: 'Handlebars' 29 | }, 30 | 'rtc/rtc': { 31 | exports: 'RTC', 32 | deps: ['rtc/sip', 'rtc/connection'] 33 | }, 34 | 'libphonenumber/PhoneNumber': { 35 | exports: 'PhoneNumber', 36 | deps: ['utils/PhoneNumberMetaDataUpdates'] 37 | }, 38 | 'vendor/async-storage/async-storage': { 39 | exports: 'asyncStorage' 40 | }, 41 | 'fxosrate': { 42 | exports: 'fxosRate' 43 | }, 44 | 'coseme': { 45 | exports: 'CoSeMe', 46 | init: function () { 47 | 'use strict'; 48 | var CoSeMe = this.CoSeMe; 49 | CoSeMe.crypto = this.CryptoJS; 50 | CoSeMe.common.Logger.disableAll(); 51 | return this.CoSeMe; 52 | } 53 | } 54 | } 55 | }); 56 | 57 | require([ 58 | 'backbone', 59 | 'global', 60 | 'models/auth', 61 | 'collections/contacts', 62 | 'router', 63 | 'models/rtc', 64 | 'models/geoposition', 65 | 'collections/history', 66 | 'templates/helpers', 67 | 'utils/notifications', 68 | 'storage/dbmanager', 69 | 'utils/language', 70 | 'utils/sdmanager', 71 | 'models/background-service' 72 | ], function (Backbone, global, Auth, Contacts, Router, Rtc, GeoPosition, 73 | HistoryCollection, HandlebarsHelpers, Notifications, DbManager, 74 | Language, SDManager, 75 | BackgroundService) { 76 | 'use strict'; 77 | Backbone.$ = require('zeptojs'); 78 | global.router = new Router(); 79 | global.auth = new Auth(); 80 | global.geoPosition = new GeoPosition(); 81 | global.rtc = new Rtc(); 82 | global.contacts = new Contacts(); 83 | global.historyCollection = new HistoryCollection(); 84 | global.notifications = new Notifications(); 85 | global.language = Language.getLanguage(); 86 | global.sdManager = new SDManager(); 87 | global.backgroundService = new BackgroundService(); 88 | global.backgroundService.start(); 89 | 90 | HandlebarsHelpers.register(); 91 | 92 | Backbone.history.start(); 93 | }); 94 | -------------------------------------------------------------------------------- /tools/create_countries_json.py: -------------------------------------------------------------------------------- 1 | #! /bin/python3 2 | 3 | # This script reads a csv-list of countries and writes it's contents to a json-file 4 | # The csv-list must be called 'countries.csv'; the json-file will be called 'countries.json' 5 | # Both files should be within the current directory. 6 | # The csv-file should have the form 7 | # mcc, junk, mnc, junk, country_code (e.g. 'de'), name, prefix (e.g. '43'), carrier 8 | # and should contain a header line 9 | # The json-file will contain list of countries as follows 10 | #[ 11 | # { 12 | # "carriers": { 13 | # "{carrier_name}": [ 14 | # {"mcc": "{mcc}", "mnc": "{mnc}"}, 15 | # -------as above--------------- 16 | # ], 17 | # } 18 | # "code": "{country_code}", 19 | # "full": "{country_name}", 20 | # "prefix": "{country_prefix}" 21 | # }, 22 | # ---------next country------ 23 | #] 24 | 25 | import csv 26 | import json 27 | 28 | countries = {} 29 | with open('countries.csv', newline='') as csvfile: 30 | reader = csv.reader(csvfile) 31 | for row in reader: 32 | #print(list(row)) 33 | mcc, _, mnc, _, iso, name, code, network = row 34 | if mcc == 'MCC' or name == 'Unknown Country' or iso == 'n/a' or network == '': 35 | #first line or garbage 36 | continue 37 | if name not in countries: 38 | country = countries[name] = { 39 | "full": name, 40 | "code": iso, 41 | "prefix": "+" + code, 42 | "carriers": {}, 43 | } 44 | else: 45 | country = countries[name] 46 | if (country["full"] != name or 47 | country["code"] != iso or 48 | country["prefix"] != "+" + code): 49 | #continue 50 | print("Invalid: old country with iso {}".format(iso)) 51 | print(country) 52 | print("New country:") 53 | print(row) 54 | print(code) 55 | continue 56 | if network not in country["carriers"]: 57 | country["carriers"][network] = [] 58 | country["carriers"][network].append({"mcc": mcc, "mnc": mnc}) 59 | #output statistics 60 | networks_with_one_mccmnc = 0 61 | networks_with_more_mccmnc = 0 62 | for country in countries.values(): 63 | for mcc_mnc_list in country["carriers"].values(): 64 | if len(mcc_mnc_list) > 1: 65 | networks_with_more_mccmnc += 1 66 | else: 67 | networks_with_one_mccmnc += 1 68 | print("networks with one mccmnc: {}; with more mccmnc: {}".format( 69 | networks_with_one_mccmnc, 70 | networks_with_more_mccmnc 71 | )) 72 | with open('countries.json', 'w') as jsonfile: 73 | jsonfile.write(json.dumps(list(countries.values()), sort_keys=True, indent=4, separators=(',', ': '))) 74 | 75 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # OpenWapp 2 | 3 | ======= 4 | OpenWapp has been discontinued due to lack of time and developers. 5 | 6 | Thanks a lot for using this app during this time. Such an amazing trip 7 | 8 | If you need an app to access WhatsApp, please, search for LoquiIM on the Marketplace. 9 | 10 | Regards, the OpenWapp developers. 11 | ======= 12 | 13 | Openwapp ha sido descontinuado por falta de tiempo y de desarrolladores. 14 | 15 | Gracias por usar esta aplicación durante mucho tiempo. 16 | 17 | Si necesitas un sustituto, prueba LoquiIM, disponible en el Marketplace. 18 | 19 | Un saludo, los desarrolladores de OpenWapp. 20 | ====== 21 | 22 | Use WhatsApp in your Firefox OS device. Install it from [Firefox Marketplace](https://marketplace.firefox.com/app/openwapp)! 23 | 24 | With OpenWapp you can communicate with all your friends and family already using WhatsApp in Firefox OS or any other phone. 25 | 26 | - NO HIDDEN COSTS: Once you and your friends download the application, you can use it to chat as much as you want. 27 | - MULTIMEDIA: Send Videos, Music, Images and Location to your friends and Contacts. 28 | - GROUP CHAT: Enjoy group conversations with your contacts. Add or Remove group participants, change group subject and set a group icon. 29 | - PERSONAL: Set a profile photo which will be shown to all your contacts. 30 | 31 | Please note that current version of OpenWapp has been tested to work on versions 1.1 and 1.3 on commercial devices. Unexpected results could be found when running OpenWapp on developer devices. 32 | 33 | ## Contribute 34 | 35 | OpenWapp is an open source project and therefore you are more than welcome to download the code and contribute to the OpenWapp Library ([CoSeMe](https://github.com/mozillahispano/coseme)) and/or the [OpenWapp client](https://github.com/mozillahispano/openwapp/wiki). If you find any issue please report it to us [here](https://github.com/mozillahispano/openwapp/issues) and we will try to fix it as soon as possible. 36 | 37 | You can [compile](https://github.com/mozillahispano/openwapp/wiki/Compilation) OpenWapp by yourself and [test it](https://github.com/mozillahispano/openwapp/wiki/Testing) on your device. 38 | 39 | If you're a coder you can read [guidelines](https://github.com/mozillahispano/openwapp/wiki/Developing) about code structure and other useful information. [Here](https://github.com/mozillahispano/openwapp/wiki/Design) you can read information about app design. 40 | 41 | If you want to [translate](https://github.com/mozillahispano/openwapp/wiki/How-to-localize-Openwapp) OpenWapp in your favorite language, please visit [Transifex](https://www.transifex.com/projects/p/openwapp/). 42 | 43 | If you just want to support our work, please [leave a rating](https://marketplace.firefox.com/app/openwapp/ratings/add) on Firefox Marketplace. 44 | 45 | ## News about OpenWapp 46 | [Follow us](https://twitter.com/openwapp) on Twitter to be updated about all news. 47 | -------------------------------------------------------------------------------- /app/styles/_inbox.sass: -------------------------------------------------------------------------------- 1 | @mixin inbox-header 2 | +common-row 3 | font-size: 1.4rem 4 | line-height: 2.5rem 5 | padding-left: 0.7rem 6 | 7 | #inbox .page-wrapper 8 | min-height: -moz-calc(100% - #{$header-height}) 9 | min-height: -webkit-calc(100% - #{$header-height}) 10 | background: #fff 11 | 12 | #inbox .legend 13 | +inbox-header 14 | background: #fff 15 | text-transform: capitalize 16 | 17 | #inbox section.conversation-list 18 | h1 19 | +inbox-header 20 | background: #fafafa 21 | 22 | @mixin message-icon($name) 23 | &:before 24 | content: "" 25 | display: inline-block 26 | width: 1.4rem 27 | height: 1.4rem 28 | background: url(/images/icon_14px_#{$name}_unread2x.png) 0 0.1rem no-repeat 29 | +background-size(1.4rem 1.4rem) 30 | margin-right: 0.5rem 31 | &.read:before 32 | background-image: url(/images/icon_14px_#{$name}_read2x.png) 33 | 34 | #inbox li 35 | +common-row 36 | +contact(6rem) 37 | &.unread 38 | background-color: #e6e6e6 39 | font-weight: bold 40 | p 41 | font-size: 1.5rem 42 | line-height: 1.5rem 43 | text-align: right 44 | a 45 | white-space: nowrap 46 | &:active 47 | background-color: #cbf1fa 48 | aside:before 49 | background-image: url(/images/frame_contact_active.png) 50 | &.group dd.event 51 | background-repeat: no-repeat 52 | background-image: url(/images/shared/headers/images/icons/group.png) 53 | background-position: 0 0.35rem 54 | background-size: 1.7rem 1.7rem 55 | padding-left: 2.2rem 56 | aside 57 | vertical-align: middle 58 | display: inline-block 59 | aside img 60 | border-radius: 50% 61 | dl 62 | vertical-align: middle 63 | display: inline-block 64 | width: calc(100% - 9.4rem) // 100% - (5 + 2)rem 65 | padding-left: 1.4rem 66 | padding-top: 0.7rem 67 | padding-bottom: 0.7rem 68 | border-bottom: 0.2rem solid #efefef 69 | dd 70 | font-size: 1.4rem 71 | line-height: 2.2rem 72 | &.name 73 | font-weight: bold 74 | color: #4c4c4c 75 | font-size: 1.7rem 76 | &.event 77 | padding-right: 3rem 78 | &.text 79 | +message-icon(message) 80 | &.image, &.video, &.audio 81 | +message-icon(image) 82 | &.location 83 | +message-icon(place) 84 | &.notification 85 | +message-icon(notification) 86 | 87 | #inbox .empty-info 88 | font-size: 1.7rem 89 | line-height: 2rem 90 | padding: 2rem 91 | text-align: center 92 | color: #3d3d3d 93 | p 94 | margin-top: 1rem 95 | &:first-child 96 | margin-top: 0 97 | font-weight: bold 98 | 99 | #contacts-centinel 100 | text-align: center 101 | padding: 1rem 102 | padding-bottom: 2rem 103 | background: linear-gradient(to bottom, #dadada, #fff) 104 | p 105 | margin-bottom: 1rem 106 | font-size: small 107 | color: #3d3d3d 108 | 109 | -------------------------------------------------------------------------------- /app/scripts/templates/inbox.hbs: -------------------------------------------------------------------------------- 1 |
      2 |
      3 | 4 | profile 5 | 6 | 7 | 8 | compose 9 | 10 | new group 11 | 12 |

      {{translate 'inbox'}}

      13 |
      14 | 15 |
      16 | 19 | 20 | {{! TODO: Pending design for this message }} 21 | 25 | 26 |
      27 |
        28 |
      29 |
      30 |

      {{translate 'loadingConversations'}}

      31 | 32 |
      33 |
      34 | 35 | {{! 36 | DON'T REMOVE THIS CODE YET, it will be useful when rendering 37 | other types of messages (picture, location) 38 | 39 |
    • 40 | 44 |
      45 |
      Name
      46 |
      Lara Croft
      47 |
      Last event
      48 |
      Pirámide chunga
      49 |
      50 |
    • 51 | }} 52 | 53 | {{! 54 | DON'T REMOVE THIS CODE YET, it will be useful when rendering 55 | other types of messages (picture, location) 56 | 57 |
    • 58 | 62 |
      63 |
      Name
      64 |
      Rodrigo Díaz de Vivar el Cid Campeador
      65 |
      Last event
      66 |
      Fotillo
      67 |
      68 |
    • 69 |
    • 70 | 74 |
      75 |
      Name
      76 |
      Neo
      77 |
      Last event
      78 |
      Remember to take the red pill
      79 |
      80 |
    • 81 | }} 82 |
      83 |
      84 | -------------------------------------------------------------------------------- /app/scripts/views/settings.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone', 3 | 'zeptojs', 4 | 'global', 5 | 'underscore', 6 | 'templates', 7 | 'vendor/async-storage/async-storage', 8 | 'storage/dbmanager', 9 | 'utils/language' 10 | ], function (Backbone, $, global, _, templates, 11 | AsyncStorage, DbManager, Language) { 12 | 'use strict'; 13 | 14 | var Settings = Backbone.View.extend({ 15 | el: '#main-page', 16 | 17 | template: templates.settings, 18 | 19 | tosPage : templates.tos, 20 | 21 | privacyPage : templates.privacy, 22 | 23 | events: { 24 | 'click #unregister' : 'unregister', 25 | 'click #showToS' : 'showTerms', 26 | 'click #showPrivacy': 'showPrivacy', 27 | 'click .btn-back': 'showSettings' 28 | }, 29 | 30 | initialize: function () { 31 | }, 32 | 33 | showSettings: function () { 34 | this.$el.removeClass().addClass('page settings'); 35 | }, 36 | 37 | showTerms: function (evt) { 38 | evt.preventDefault(); 39 | if (this.$el.find('#tos-body').is(':empty')) { 40 | this.$el.find('#tos-body').html(this.tosPage({ 41 | tosLocale: Language.getLegalLocale() 42 | })); 43 | } 44 | this.$el.removeClass().addClass('page tos'); 45 | }, 46 | 47 | showPrivacy: function (evt) { 48 | evt.preventDefault(); 49 | if (this.$el.find('#privacy-body').is(':empty')) { 50 | this.$el.find('#privacy-body').html(this.privacyPage({ 51 | tosLocale: Language.getLegalLocale() 52 | })); 53 | } 54 | this.$el.removeClass().addClass('page privacy'); 55 | }, 56 | 57 | unregister: function (evt) { 58 | evt.preventDefault(); 59 | 60 | if (window.confirm(navigator.mozL10n.get('logoutAlertText'))) { 61 | this._unregisterDevice(); 62 | } 63 | }, 64 | 65 | render: function () { 66 | this.$el.html(this.template({ version: global.client.getAppVersion() })); 67 | this.showSettings(); 68 | }, 69 | 70 | _unregisterDevice: function () { 71 | global.auth.logout(); 72 | this._destroyAsyncStorage(_.bind(this._destroyOtherData, this)); 73 | }, 74 | 75 | _destroyOtherData: function () { // TODO: add spinning wheel 76 | var _this = this; 77 | this._destroyMemory(); 78 | _this._destroyIndexedDb(_.bind(_this._reloadAndLogin, _this), true); 79 | }, 80 | 81 | _destroyMemory: function () { 82 | global.contacts.unregister(); 83 | global.historyCollection.unregister(); 84 | }, 85 | 86 | _reloadAndLogin: function () { 87 | global.router.navigate('login', { trigger: true }); 88 | window.location.reload(true); 89 | }, 90 | 91 | _destroyAsyncStorage: function (callback) { 92 | AsyncStorage.clear(callback); 93 | }, 94 | 95 | _destroyIndexedDb: function (callback) { 96 | DbManager.destroySchema(callback); 97 | } 98 | 99 | }); 100 | 101 | return Settings; 102 | }); 103 | -------------------------------------------------------------------------------- /app/scripts/models/background-service.js: -------------------------------------------------------------------------------- 1 | define(['backbone', 'global'], function (Backbone, global) { 2 | 'use strict'; 3 | 4 | var alarms = window.navigator.mozAlarms; 5 | if (!alarms) { 6 | console.log('No alarms API available.'); 7 | } 8 | 9 | var BackgroundService = Backbone.Model.extend({ 10 | 11 | alarmHandlerInstalled: false, 12 | 13 | intervalId: null, 14 | 15 | defaults: function () { 16 | var awakePeriod = localStorage.getItem('awakePeriod') !== null ? 17 | parseInt(localStorage.getItem('awakePeriod'), 10) : 18 | 5 * 60 * 1000; // 5 min 19 | return { 20 | awakePeriod: awakePeriod 21 | }; 22 | }, 23 | 24 | start: function () { 25 | if (!alarms) { return; } 26 | console.log('[background service] Started'); 27 | this._installAlarmHandler(); 28 | this._resetAlarm(); 29 | this.on('change:awakePeriod', this._changePeriod); 30 | }, 31 | 32 | stop: function () { 33 | if (!alarms || !this.intervalId) { return; } 34 | console.log('[background service] Stopped'); 35 | this._cancelAlarm(); 36 | }, 37 | 38 | _changePeriod: function () { 39 | this._resetAlarm(); 40 | localStorage.awakePeriod = this.get('awakePeriod'); 41 | }, 42 | 43 | _installAlarmHandler: function () { 44 | if (this.alarmHandlerInstalled) { return; } 45 | navigator.mozSetMessageHandler('alarm', function _onAwake() { 46 | console.log('[background service] Awake signal received.'); 47 | 48 | // Reconnect 49 | if (!global.client.isOnline) { 50 | console.log('[background service] Service down, reconnecting!'); 51 | global.auth.checkCredentials(); 52 | } 53 | 54 | // Send report of new messages 55 | global.notifications.sendReport(); 56 | 57 | this._resetAlarm(); 58 | }.bind(this)); 59 | this.alarmHandlerInstalled = true; 60 | }, 61 | 62 | _resetAlarm: function () { 63 | this._cancelAlarm(); 64 | this._setNextAlarm(); 65 | }, 66 | 67 | _cancelAlarm: function () { 68 | var alarmId = parseInt(localStorage.alarmId, 10); 69 | if (alarmId) { 70 | alarms.remove(alarmId); 71 | } 72 | }, 73 | 74 | _setNextAlarm: function () { 75 | var awakePeriod = this.get('awakePeriod'); 76 | if (!awakePeriod) { 77 | console.log('[background service] Auto awake disabled!'); 78 | return; 79 | } 80 | 81 | var nextAlarm = new Date(Date.now() + awakePeriod); 82 | var alarmRequest = alarms.add(nextAlarm, 'honorTimezone', {}); 83 | alarmRequest.onsuccess = function () { 84 | var alarmId = alarmRequest.result; 85 | localStorage.alarmId = alarmId; 86 | console.log( 87 | '[background service] Next awake signal with id', 88 | alarmId, 'by', nextAlarm.toString() 89 | ); 90 | }; 91 | alarmRequest.onerror = function () { 92 | console.error('Impossible to set an alarm to keep the program alive.'); 93 | }; 94 | } 95 | }); 96 | 97 | return BackgroundService; 98 | }); 99 | -------------------------------------------------------------------------------- /app/scripts/views/contact-profile.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone', 3 | 'zeptojs', 4 | 'global', 5 | 'templates', 6 | 'models/contact', 7 | 'templates/helpers', 8 | 'utils/mimetype' 9 | ], function (Backbone, $, global, templates, Contact, helpers, MimeType) { 10 | 'use strict'; 11 | 12 | var MozActivity = window.MozActivity; 13 | 14 | var Profile = Backbone.View.extend({ 15 | 16 | currentParticipants: [], 17 | 18 | el: '#main-page', 19 | 20 | model: Contact, 21 | 22 | events: { 23 | 'click .dial': '_dialContact', 24 | 'click .send-sms': '_smsContact', 25 | 'click button.close': 'close', 26 | 'click #profile-picture': '_openProfilePicture' 27 | }, 28 | 29 | template: templates['contact-profile'], 30 | 31 | render: function () { 32 | var internationalPhone = this.model.get('phone'); 33 | this.$el.html(this.template({ 34 | displayName: this.model.get('displayName') || '+' + internationalPhone, 35 | state: this.model.get('state'), 36 | phone: internationalPhone 37 | })); 38 | this._replacePhoto(this.model.get('photo')); 39 | helpers.revealEmoji(this.$el.find('#state')); 40 | }, 41 | 42 | _openProfilePicture: function (evt) { 43 | if (evt) { evt.preventDefault(); } 44 | 45 | var blob = this.model.get('photo'); 46 | if (blob instanceof window.Blob) { 47 | var extension = MimeType.getExtension(blob.type); 48 | var fileName = this.model.get('displayName') + '.' + extension; 49 | new window.MozActivity({ 50 | name: 'open', 51 | data: { 52 | filename: global.sdManager.getPath(fileName), 53 | type: blob.type, 54 | blob: blob, 55 | allowSave: true 56 | } 57 | }); 58 | } 59 | return false; 60 | }, 61 | 62 | _dialContact: function () { 63 | new MozActivity({ 64 | name: 'dial', 65 | data: { 66 | type: 'webtelephony/number', 67 | number: '+' + this.model.get('phone') 68 | } 69 | }); 70 | }, 71 | 72 | _smsContact: function () { 73 | new MozActivity({ 74 | name: 'new', 75 | data: { 76 | type: 'websms/sms', 77 | number: '+' + this.model.get('phone') 78 | } 79 | }); 80 | }, 81 | 82 | _replacePhoto: function (source) { 83 | if (!source) { return; } 84 | 85 | this.clear(); 86 | if (source instanceof window.Blob) { 87 | this.$el.find('img').attr('src', window.URL.createObjectURL(source)); 88 | } 89 | else { 90 | console.warn('The photo is not a blob.'); 91 | } 92 | }, 93 | 94 | close: function (evt) { 95 | if (evt) { evt.preventDefault(); } 96 | var previous = 'conversation/' + this.model.get('id'); 97 | global.router.navigate(previous, { trigger: true }); 98 | }, 99 | 100 | clear: function () { 101 | window.URL.revokeObjectURL(this.$el.find('img').attr('src')); 102 | } 103 | }); 104 | 105 | return Profile; 106 | }); 107 | -------------------------------------------------------------------------------- /app/scripts/utils/sdmanager.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'underscore', 3 | 'backbone' 4 | ], 5 | function (_, Backbone) { 6 | 'use strict'; 7 | 8 | var STORAGE_TYPE = 'sdcard'; 9 | 10 | function isDeviceStorageAvailable() { 11 | return !!(navigator.getDeviceStorage && 12 | navigator.getDeviceStorage(STORAGE_TYPE)); 13 | } 14 | 15 | function SDManager() { 16 | var _this = this; 17 | this.status = null; 18 | 19 | function initDeviceStorage() { 20 | var storage = navigator.getDeviceStorage(STORAGE_TYPE); 21 | 22 | var request = storage.available(); 23 | request.onsuccess = function (e) { 24 | _this._setStatus(e.target.result); 25 | }; 26 | request.onerror = function () { 27 | _this._setStatus('unmounted'); 28 | }; 29 | return storage; 30 | } 31 | 32 | _.extend(this, Backbone.Events); 33 | 34 | if (isDeviceStorageAvailable()) { 35 | this.storage = initDeviceStorage(); 36 | } 37 | else { 38 | this.storage = null; 39 | // TODO: setup our mock or something else 40 | } 41 | } 42 | 43 | SDManager.prototype.isReady = function () { 44 | return this.status === 'available' && this.storage !== null; 45 | }; 46 | 47 | SDManager.prototype.getPath = function (filename) { 48 | return 'openwapp/' + filename; 49 | }; 50 | 51 | SDManager.prototype.save = function (filename, blob, callbacks) { 52 | var _this = this; 53 | 54 | function addFile() { 55 | var request = _this.storage.addNamed(blob, _this.getPath(filename)); 56 | request.onsuccess = function () { 57 | console.log('sdcard SAVE SUCCESS', filename); 58 | if (callbacks.success) { callbacks.success(); } 59 | }; 60 | request.onerror = function () { 61 | var err = {code: 'not_saved', details: this.error}; 62 | console.log('sdcard SAVE ERROR', filename); 63 | if (callbacks.error) { callbacks.error(err); } 64 | }; 65 | } 66 | 67 | callbacks = callbacks || {}; 68 | if (this.isReady()) { 69 | var deletereq = this.storage.delete(this.getPath(filename)); 70 | deletereq.onerror = deletereq.onsuccess = addFile; 71 | console.log('sdcard SAVE', filename); 72 | } 73 | else { 74 | if (callbacks.error) { callbacks.error({code: 'not_ready'}); } 75 | } 76 | }; 77 | 78 | // TODO: This follows node.js callback style, save not but the rest of the 79 | // application does. As we don't use save no more, it should be convenient 80 | // to refactor the callback styel of save() method. 81 | SDManager.prototype.load = function (filename, callback) { 82 | var getRequest = this.storage.get(filename); 83 | getRequest.onerror = function () { 84 | callback({ code: 'not-found', details: this.error }); 85 | }; 86 | getRequest.onsuccess = function () { 87 | callback(null, getRequest.result); 88 | }; 89 | }; 90 | 91 | SDManager.prototype._setStatus = function (status) { 92 | if (status !== this.status) { 93 | this.status = status; 94 | this.trigger('change', this.status); 95 | } 96 | }; 97 | 98 | return SDManager; 99 | }); 100 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mocha Spec Runner 6 | 7 | 8 | 9 | 10 |
      11 | 12 |
      13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 45 | 46 | 69 | 70 | 71 | 72 | 73 | 74 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /app/styles/_common_register.sass: -------------------------------------------------------------------------------- 1 | @mixin action-button 2 | +box-sizing(border-box) 3 | +background-image(linear-gradient(bottom, #df351b 0%, #f56b1b 100%)) 4 | width: 100% 5 | color: #ffffff 6 | border: 0.1rem solid #c1331e 7 | padding: 0.8rem 8 | border-radius: 0.2rem 9 | font-size: 1.7rem 10 | &:disabled 11 | background: #d7d6d6 12 | border-color: #c7c7c7 13 | color: #aaa 14 | &:active:not(:disabled) 15 | background: #c1331d 16 | border-color: #c1331e 17 | 18 | @mixin secondary-action-button 19 | +action-button 20 | +background-image(linear-gradient(bottom, #cacaca 0%, #fff 100%)) 21 | color: #333 22 | border-color: #a6a6a6 23 | &:active:not(:disabled) 24 | border-color: #a6a6a6 25 | background: #969696 26 | 27 | @mixin register-page 28 | position: absolute 29 | transition: left .4s ease 30 | width: 100% 31 | font-family: 'MozTT', sans-serif 32 | div.page-wrapper 33 | background: #e6e6e6 34 | input[type=submit], input[type=button] 35 | +action-button 36 | width: 100% 37 | input, select, legend 38 | font-size: 1.9rem 39 | vertical-align: bottom 40 | select 41 | border-top-right-radius: 0 42 | border-bottom-right-radius: 0 43 | width: 7.5rem 44 | border-right: none 45 | text-align: center 46 | padding-top: 0.7rem 47 | padding-left: 0.4rem 48 | select + input 49 | border-top-left-radius: 0 50 | border-bottom-left-radius: 0 51 | max-width: -moz-calc(100% - 8rem) 52 | max-width: -webkit-calc(100% - 8rem) 53 | fieldset 54 | position: relative 55 | width: 100% 56 | border: none 57 | legend 58 | border: 0.1rem solid #CCCCCC 59 | border-right: none 60 | border-top-left-radius: 3px 61 | border-bottom-left-radius: 3px 62 | padding: 0 63 | width: 30% 64 | height: 4rem 65 | float: left 66 | position: relative 67 | text-transform: none 68 | input 69 | width: 70% 70 | position: relative 71 | a 72 | color: #4599e1 73 | text-decoration: none 74 | section.intro, section.main 75 | text-align: center 76 | padding: 1rem 2.5rem 77 | p 78 | margin-top: 1rem 79 | &:first-child 80 | margin-top: 0 81 | section.intro 82 | +box-sizing(border-box) 83 | font-size: 1.7rem 84 | line-height: 2.25rem 85 | color: #555555 86 | background: #f2f2f2 87 | border-bottom: 0.1rem solid #fff 88 | min-height: 9rem 89 | section.main 90 | padding-top: 2rem 91 | color: #858585 92 | font-size: 1.4rem 93 | line-height: 1.75rem 94 | p 95 | margin-top: 1rem 96 | line-height: 2rem 97 | &.call-to-action 98 | font-size: 1.4rem 99 | color: #3d3d3d 100 | margin-top: 1.75rem 101 | display: table 102 | label 103 | float: left 104 | display: table-cell 105 | & + span 106 | width: -moz-calc(100% - 5rem) 107 | padding-top: 1.1rem // TODO: fix this and get proper valign 108 | text-align: left 109 | min-height: 5rem 110 | display: table-cell 111 | vertical-align: bottom 112 | float: right 113 | .spinner 114 | display: none 115 | progress 116 | margin-top: 1rem 117 | .hidden 118 | position: absolute 119 | text-indent: -10000px 120 | -------------------------------------------------------------------------------- /app/styles/_common.sass: -------------------------------------------------------------------------------- 1 | $header-height: 5rem 2 | $footer-height: 4rem 3 | $color-active-button: #20839a 4 | 5 | @mixin full-height 6 | & 7 | height: 100% 8 | body 9 | height: 100% 10 | width: 100% 11 | overflow: hidden 12 | 13 | @mixin list-button // apply this to 'a' elements who are children of 'li' 14 | +box-sizing(border-box) 15 | display: block 16 | width: 100% 17 | height: 100% 18 | color: inherit 19 | &:active 20 | color: inherit 21 | background: $color-active-button 22 | 23 | @mixin dark-bg-header 24 | +background(linear-gradient(#2b2b2f, #141415)) 25 | .icon:after 26 | background: #000 27 | 28 | @mixin ellipsis-overflow 29 | overflow: hidden 30 | text-overflow: ellipsis 31 | white-space: nowrap 32 | 33 | @mixin common-row 34 | +ellipsis-overflow 35 | background: #fff 36 | font-size: 1.7rem 37 | line-height: 3.2rem 38 | 39 | @mixin common-highlight-row 40 | background: #de321b 41 | 42 | @mixin dark-row 43 | +common-row 44 | background: #1e1e20 45 | border-top-color: #252527 46 | border-bottom-color: #030303 47 | color: #fff 48 | a 49 | color: #fff 50 | 51 | @mixin dark-highlight-row 52 | +dark-row 53 | background: #101011 54 | 55 | @mixin dark-footer 56 | +box-sizing(border-box) 57 | background: #000 58 | position: absolute 59 | bottom: 0 60 | width: 100% 61 | border-top: 0.1rem solid #252527 62 | font-size: 1.4rem 63 | color: #fff 64 | p 65 | padding-left: 0.7rem 66 | line-height: $footer-height 67 | 68 | @mixin vertical-aligned-container 69 | white-space: nowrap 70 | 71 | @mixin vertical-aligned 72 | display: inline-block 73 | vertical-alignement: middle 74 | 75 | @mixin contact($avatar-size) 76 | +vertical-aligned-container 77 | a, & > span 78 | +list-button 79 | +clearfix 80 | aside 81 | +vertical-aligned 82 | position: relative 83 | overflow: visible 84 | img 85 | width: $avatar-size 86 | height: $avatar-size 87 | 88 | dl 89 | +vertical-aligned 90 | width: -moz-calc(100% - $avatar-size) 91 | text-align: left 92 | margin-left: 0.5rem 93 | dt 94 | display: none 95 | dd 96 | +ellipsis-overflow 97 | line-height: 4rem 98 | color: #333 99 | dd.state 100 | font-style: italic 101 | font-size: 1.2rem 102 | line-height: 2rem 103 | padding: 0rem 104 | color: #666 105 | button 106 | margin-top: 0.5rem 107 | 108 | html 109 | +full-height 110 | font-size: 62.5% // NOTE: copied from gaia 111 | 112 | body 113 | font: 1.6rem/2.2rem MozTT, Sans-serif // NOTE: copied from gaia 114 | 115 | a 116 | text-decoration: none 117 | outline: none 118 | 119 | button, input[type=submit], input[type=button] 120 | &::-moz-focus-inner 121 | border: none 122 | .main-wrapper 123 | width: 100% 124 | min-height: 100% 125 | max-height: 100% 126 | position: relative 127 | 128 | .page 129 | position: absolute 130 | width: 100% 131 | height: 100% 132 | left: 0 133 | top: 0 134 | & > section 135 | min-width: 100% 136 | max-width: 100% 137 | max-height: 100% 138 | height: 100% 139 | 140 | .page-wrapper 141 | max-height: -moz-calc(100% - #{$header-height}) 142 | max-height: -webkit-calc(100% - #{$header-height}) 143 | overflow-y: auto 144 | height: -moz-calc(100% - #{$header-height}) 145 | height: -webkit-calc(100% - #{$header-height}) 146 | 147 | .not-square 148 | color: #FF3939 149 | -------------------------------------------------------------------------------- /test/spec/main.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | baseUrl: window.coverage ? 'cov/': 'scripts/', 3 | paths: { 4 | zeptojs: '../components/zepto/zepto.min', 5 | underscore: '../components/underscore/underscore-min', 6 | backbone: '../components/backbone/backbone-min', 7 | handlebars: '../components/handlebars.js/dist/handlebars.runtime', 8 | rtc: 'vendor/ottcomms-rtc-web/rtc', 9 | libphonenumber: '../components/PhoneNumber.js' 10 | }, 11 | shim: { 12 | 'zeptojs': { 13 | exports: '$' 14 | }, 15 | 'underscore': { 16 | exports: '_' 17 | }, 18 | 'backbone': { 19 | deps: ['underscore', 'zeptojs'], 20 | exports: 'Backbone' 21 | }, 22 | 'handlebars': { 23 | exports: 'Handlebars' 24 | }, 25 | 'rtc/rtc': { 26 | exports: 'RTC', 27 | deps: ['rtc/sip', 'rtc/connection'] 28 | }, 29 | 'libphonenumber/PhoneNumber': { 30 | exports: 'PhoneNumber', 31 | deps: ['libphonenumber/PhoneNumberMetaData'] 32 | }, 33 | 'vendor/async-storage/async-storage': { 34 | exports: 'asyncStorage' 35 | }, 36 | 'lib/JSCovReporter/JSCovReporter.js': { 37 | deps: ['backbone'] 38 | } 39 | } 40 | }); 41 | 42 | /* jshint maxstatements: 50 */ 43 | require([ 44 | 'backbone', 45 | 'global', 46 | 'templates/helpers', 47 | 'lib/JSCovReporter/JSCovReporter.js' 48 | ], function (Backbone, global, HandlebarsHelpers) { 49 | 'use strict'; 50 | // Create global objects so they can be easily stubbed in the tests 51 | global.router = new Backbone.Router({ show: function () {} }); 52 | global.router.navigate = function () {}; 53 | global.auth = new Backbone.Model(); 54 | global.auth.checkCredentials = function () {}; 55 | global.auth.checkAppVersion = function () {}; 56 | global.auth.register = function () {}; 57 | global.auth.validate = function () {}; 58 | global.auth.updateScreenName = function () {}; 59 | global.auth.login = function () {}; 60 | global.auth.logout = function () {}; 61 | global.auth._oldAppVersion = function () {}; 62 | global.auth.showMessageUpdateNeeded = function () {}; 63 | global.contacts = new Backbone.Collection(); 64 | global.contacts.unregister = function () {}; 65 | global.contacts.getTumeContacts = function () 66 | { return new Backbone.Collection(); }; 67 | global.notifications = new Backbone.Model(); 68 | global.notifications.send = function () {}; 69 | 70 | global.rtc = new Backbone.Model(); 71 | global.rtc.sendMessage = function () {}; 72 | global.rtc.sendDeliveredNotification = function () {}; 73 | 74 | // TODO: a better way to initialize methods 75 | global.historyCollection = new Backbone.Collection(); 76 | global.historyCollection.todaysConversations = function () {}; 77 | global.historyCollection.yesterdayConversations = function () {}; 78 | global.historyCollection.olderConversations = function () {}; 79 | global.historyCollection.loadConversations = function () {}; 80 | global.historyCollection.saveConversationList = function () {}; 81 | global.historyCollection.findAndCreateConversation = function () {}; 82 | global.historyCollection.clear = function () {}; 83 | global.historyCollection.unregister = function () {}; 84 | global.historyCollection.comparator = function () { return -1; }; 85 | 86 | global.geoPosition = new Backbone.Model(); 87 | 88 | global.historyFetcher = new Backbone.Model(); 89 | global.historyFetcher.fetchMessages = function () {}; 90 | 91 | global.language = 'es'; 92 | 93 | HandlebarsHelpers.register(); 94 | }); 95 | /* jshint maxstatements: 20 */ 96 | -------------------------------------------------------------------------------- /app/scripts/vendor/canvasToBlob/canvas-toBlob.js: -------------------------------------------------------------------------------- 1 | /* canvas-toBlob.js 2 | * A canvas.toBlob() implementation. 3 | * 2011-07-13 4 | * 5 | * By Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr 6 | * License: X11/MIT 7 | * See LICENSE.md 8 | */ 9 | 10 | /*global self */ 11 | /*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, 12 | plusplus: true */ 13 | 14 | /*! @source http://purl.eligrey.com/github/canvas-toBlob.js/blob/master/canvas-toBlob.js */ 15 | 16 | (function(view) { 17 | "use strict"; 18 | var 19 | Uint8Array = view.Uint8Array 20 | , HTMLCanvasElement = view.HTMLCanvasElement 21 | , is_base64_regex = /\s*;\s*base64\s*(?:;|$)/i 22 | , base64_ranks 23 | , decode_base64 = function(base64) { 24 | var 25 | len = base64.length 26 | , buffer = new Uint8Array(len / 4 * 3 | 0) 27 | , i = 0 28 | , outptr = 0 29 | , last = [0, 0] 30 | , state = 0 31 | , save = 0 32 | , rank 33 | , code 34 | , undef 35 | ; 36 | while (len--) { 37 | code = base64.charCodeAt(i++); 38 | rank = base64_ranks[code-43]; 39 | if (rank !== 255 && rank !== undef) { 40 | last[1] = last[0]; 41 | last[0] = code; 42 | save = (save << 6) | rank; 43 | state++; 44 | if (state === 4) { 45 | buffer[outptr++] = save >>> 16; 46 | if (last[1] !== 61 /* padding character */) { 47 | buffer[outptr++] = save >>> 8; 48 | } 49 | if (last[0] !== 61 /* padding character */) { 50 | buffer[outptr++] = save; 51 | } 52 | state = 0; 53 | } 54 | } 55 | } 56 | // 2/3 chance there's going to be some null bytes at the end, but that 57 | // doesn't really matter with most image formats. 58 | // If it somehow matters for you, truncate the buffer up outptr. 59 | return buffer; 60 | } 61 | ; 62 | if (Uint8Array) { 63 | base64_ranks = new Uint8Array([ 64 | 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1 65 | , -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 66 | , 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 67 | , -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 68 | , 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 69 | ]); 70 | } 71 | if (HTMLCanvasElement && !HTMLCanvasElement.prototype.toBlob) { 72 | HTMLCanvasElement.prototype.toBlob = function(callback, type /*, ...args*/) { 73 | if (!type) { 74 | type = "image/png"; 75 | } if (this.mozGetAsFile) { 76 | callback(this.mozGetAsFile("canvas", type)); 77 | return; 78 | } 79 | var 80 | args = Array.prototype.slice.call(arguments, 1) 81 | , dataURI = this.toDataURL.apply(this, args) 82 | , header_end = dataURI.indexOf(",") 83 | , data = dataURI.substring(header_end + 1) 84 | , is_base64 = is_base64_regex.test(dataURI.substring(0, header_end)) 85 | , blob 86 | ; 87 | if (Blob.fake) { 88 | // no reason to decode a data: URI that's just going to become a data URI again 89 | blob = new Blob 90 | if (is_base64) { 91 | blob.encoding = "base64"; 92 | } else { 93 | blob.encoding = "URI"; 94 | } 95 | blob.data = data; 96 | blob.size = data.length; 97 | } else if (Uint8Array) { 98 | if (is_base64) { 99 | blob = new Blob([decode_base64(data)], {type: type}); 100 | } else { 101 | blob = new Blob([decodeURIComponent(data)], {type: type}); 102 | } 103 | } 104 | callback(blob); 105 | }; 106 | } 107 | }(self)); 108 | -------------------------------------------------------------------------------- /app/scripts/collections/messages.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'underscore', 3 | 'backbone', 4 | 'global', 5 | 'models/message' 6 | ], function (_, Backbone, global, MessageModel) { 7 | 'use strict'; 8 | 9 | return Backbone.Collection.extend({ 10 | model: MessageModel, 11 | 12 | initialize: function () { 13 | this.listenTo(this, 'remove', this._messageRemoved); 14 | }, 15 | 16 | fetch: function () { 17 | }, 18 | 19 | setStatusReceived: function (id) { 20 | var message = this.find(function (model) { 21 | return model.get('meta').commId === id; 22 | }); 23 | if (message) { 24 | message.set('status', 'received'); 25 | message.saveToStorage(); 26 | } 27 | }, 28 | 29 | _messageRemoved: function (model) { 30 | model.removeFromStorage(); 31 | }, 32 | 33 | /** 34 | * Order the collection by date 35 | * @param two models to compare A B 36 | * @return -1 if A before B, 0 if equal, 1 if after. 37 | */ 38 | comparator: function (message) { 39 | var meta = message.get('meta'); 40 | var rv = -1; 41 | if (meta) { 42 | rv = meta.sentDate || meta.date; 43 | } 44 | 45 | return rv; 46 | }, 47 | 48 | add: function (models, options) { 49 | if (Array.isArray(models)) { 50 | // filter out duplicates in the array 51 | var c = 0; // to make sure we don't remove undefined/null commIds 52 | models = _.unique(models, function (x) { 53 | var meta = x.get && x.get('meta') || x.meta; 54 | return meta && meta.commId || c++; 55 | }); 56 | } else { 57 | models = [models]; // was just one 58 | } 59 | // filter out already existing messages 60 | models = this._clearRepeated(models); 61 | if (models) { 62 | Backbone.Collection.prototype.add.call(this, models, options); 63 | } 64 | }, 65 | 66 | // XXX: Slow but safe (for faster algorithms, we need to sort collections 67 | // first). Notice that `add()` do almost the same but for `commId`. Notice 68 | // we are using `_id` which is not overlapping with backbone's default 69 | // idAttribute which is `id`. 70 | mergeById: function (messages) { 71 | var currentMessages = this.models; 72 | 73 | function hasMessage(target) { 74 | return _.find(currentMessages, function (message) { 75 | return message.get('_id') === target.get('_id'); 76 | }); 77 | } 78 | 79 | for (var i = 0, target; (target = messages[i]); i++) { 80 | if (!hasMessage(target)) { 81 | this.add(target); 82 | } 83 | } 84 | }, 85 | 86 | push: function (model, options) { 87 | // XXX: Remember the simulator crashes on console.log() calls for 88 | // unknown reasons. 89 | if (!this._hasMessage(model)) { 90 | Backbone.Collection.prototype.push.call(this, model, options); 91 | } 92 | }, 93 | 94 | _clearRepeated: function (models) { 95 | var _this = this; 96 | return _.filter(models, function (x) { 97 | return !(_this._hasMessage(x)); 98 | }); 99 | }, 100 | 101 | _hasMessage: function (model) { 102 | if (!model) { 103 | return true; 104 | } 105 | 106 | var commId = null; 107 | if (model && model.get) { // model instance 108 | commId = model.get('meta').commId; 109 | } 110 | else { // just an Object 111 | commId = model.meta ? model.meta.commId : null; 112 | } 113 | 114 | return (commId && !!(this.find(function (x) { 115 | return x.get('meta').commId === commId; 116 | }))); 117 | } 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /app/scripts/views/compose-message.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone', 3 | 'zeptojs', 4 | 'global', 5 | 'models/message', 6 | 'views/emoji-selector', 7 | 'templates' 8 | ], function (Backbone, $, global, Message, EmojiSelector, templates) { 9 | 'use strict'; 10 | 11 | return Backbone.View.extend({ 12 | 13 | template: templates['compose-message'], 14 | 15 | model: Message, 16 | 17 | events: { 18 | 'click #conversation-send-button' : '_createTextMessage', 19 | 'click #insert-emoji': '_toggleEmojiList', 20 | 'click #message-text-input': '_hideEmojiList' 21 | }, 22 | 23 | initialize: function () { 24 | }, 25 | 26 | _toggleEmojiList: function () { 27 | var isHidden = this._emojiSelector.isHidden(); 28 | this.$el.find('#insert-emoji').get(0).classList.toggle('shown'); 29 | this._emojiSelector[isHidden ? 'show' : 'hide'](); 30 | }, 31 | 32 | _hideEmojiList: function () { 33 | this._emojiSelector.hide(); 34 | this.$el.find('#insert-emoji').get(0).classList.remove('shown'); 35 | }, 36 | 37 | clear: function () { 38 | this.stopListening(); 39 | }, 40 | 41 | render: function () { 42 | var newElement = this.template(this.model.toJSON()); 43 | this.setElement($(newElement)); 44 | 45 | this.$messageComposer = this.$el.find('#message-text-input'); 46 | 47 | this._emojiSelector = new EmojiSelector({ 48 | composer: this.$messageComposer.get(0) 49 | }); 50 | $('#conversation').after(this._emojiSelector.render().el); 51 | this._emojiSelector.changeTab('people'); 52 | 53 | if(localStorage.getItem('sendByEnter')) { 54 | var btn = this.el.querySelector('#conversation-send-button'); 55 | this.el.querySelector('#message-text-input') 56 | .addEventListener('keydown', function (e) { 57 | if(e.key === 'Enter') { 58 | btn.click(); // Send the message 59 | e.preventDefault(); // Don't write a newline in the message box 60 | } 61 | }); 62 | } 63 | }, 64 | 65 | _createTextMessage: function (event) { 66 | event.preventDefault(); 67 | this._hideEmojiList(); 68 | 69 | var html = this.$messageComposer.html().trim(); 70 | 71 | html = this._emojiToUnicode(html); 72 | var text = this._brToBreakLines(html); 73 | text = text.trim(); 74 | if (text.length === 0) { return; } 75 | 76 | var newModel = new Message({ 77 | type: 'text', 78 | contents: text, 79 | from: {msisdn: global.auth.get('msisdn')}, 80 | meta: { 81 | date: new Date(), 82 | sentDate: new Date() 83 | } 84 | }); 85 | 86 | this.$messageComposer.html('
      '); 87 | this._emojiSelector.clearCaretPosition(); 88 | this.trigger('compose:message:text', newModel); 89 | }, 90 | 91 | _emojiToUnicode: function (html) { 92 | var container = document.createElement('div'); 93 | container.innerHTML = html; 94 | var emojis = container.querySelectorAll('.emoji'); 95 | emojis = Array.prototype.slice.call(emojis); 96 | emojis.forEach(function _replace(emojiElement) { 97 | var entity = document.createTextNode(emojiElement.dataset.code); 98 | emojiElement.parentNode.replaceChild(entity, emojiElement); 99 | }); 100 | return container.innerHTML; 101 | }, 102 | 103 | _brToBreakLines: function (html) { 104 | var lines = html.split(//g); 105 | lines = lines.map(function _unscape(line) { 106 | var d = document.createElement('div'); 107 | d.innerHTML = line; 108 | return d.textContent; 109 | }); 110 | return lines.join('\n'); 111 | } 112 | }); 113 | }); 114 | -------------------------------------------------------------------------------- /app/scripts/views/validate.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'backbone', 3 | 'zeptojs', 4 | 'global', 5 | 'templates', 6 | 'utils/phonenumber', 7 | 'utils/language' 8 | ], function (Backbone, $, global, templates, PhoneNumber, Language) { 9 | 'use strict'; 10 | 11 | var Validate = Backbone.View.extend({ 12 | 13 | el: '#main-page', 14 | 15 | template: templates.validate, 16 | 17 | initialize: function(options) { 18 | this.phoneNumber = options.phoneNumber; 19 | this.countryCode = options.countryCode; 20 | }, 21 | 22 | render: function () { 23 | var internationalNumber; 24 | var fullNumber = this.countryCode + this.phoneNumber; 25 | this.$el.removeClass().addClass('page validate'); 26 | try { 27 | internationalNumber = PhoneNumber.format(fullNumber); 28 | } catch (e) { 29 | internationalNumber = fullNumber; 30 | } 31 | this.$el.html(this.template({ phoneNumber: internationalNumber })); 32 | }, 33 | 34 | events: { 35 | 'submit #validate-form': 'validate', 36 | 'click button': 'goToLogin', 37 | 'click #call-me': 'callMe', 38 | 'keyup input[name=validation-code-1]': 'checkPinInput', 39 | 'keyup input[name=validation-code-2]': 'checkPinInput' 40 | }, 41 | 42 | // Validate page functions 43 | 44 | callMe: function (evt) { 45 | evt.preventDefault(); 46 | 47 | var locale = Language.getLanguage(); 48 | global.auth.register(this.phoneNumber, locale, function () { 49 | // TODO: Implement callback here 50 | }); 51 | }, 52 | 53 | checkPinInput: function (evt) { 54 | evt.preventDefault(); 55 | var code1 = $('input[name=validation-code-1]').val(); 56 | var code2 = $('input[name=validation-code-2]').val(); 57 | var button = $(evt.target).closest('form').find('input[type=submit]'); 58 | button.prop('disabled', code1.length < 3 || code2.length < 3); 59 | }, 60 | 61 | checkNameInput: function (evt) { 62 | evt.preventDefault(); 63 | var name = $(evt.target).val(); 64 | var button = $(evt.target).closest('form').find('input[type=submit]'); 65 | button.prop('disabled', name.length < 4); 66 | }, 67 | 68 | goToLogin: function (evt) { 69 | evt.preventDefault(); 70 | global.router.navigate('login', { trigger: true }); 71 | }, 72 | 73 | validate: function (evt) { 74 | var _this = this; 75 | evt.preventDefault(); 76 | 77 | var pin = this.$el.find('input[name=validation-code-1]').val() + 78 | this.$el.find('input[name=validation-code-2]').val(); 79 | 80 | this.showSpinner('validate-page'); 81 | 82 | global.auth.validate( 83 | this.countryCode, this.phoneNumber, pin, '', 84 | function (err) { 85 | _this.hideSpinner('validate-page'); 86 | if (err) { 87 | window.alert(navigator.mozL10n.get('pinInvalidAlert')); 88 | return; 89 | } 90 | var destination = global.auth.get('screenName') ? 91 | 'inbox' : 'profile'; 92 | global.router.navigate(destination, { trigger: true }); 93 | } 94 | ); 95 | }, 96 | 97 | showSpinner: function (section) { 98 | var $section = this.$el.find('#' + section); 99 | 100 | $section.find('.spinner').show(); 101 | $section.find('section.intro > p').hide(); 102 | var button = $section.find('input[type=submit]'); 103 | button.prop('disabled', true); 104 | }, 105 | 106 | hideSpinner: function (section) { 107 | var $section = this.$el.find('#' + section); 108 | 109 | $section.find('.spinner').hide(); 110 | $section.find('section.intro > p').show(); 111 | var button = $section.find('input[type=submit]'); 112 | button.prop('disabled', false); 113 | } 114 | }); 115 | 116 | return Validate; 117 | }); 118 | -------------------------------------------------------------------------------- /app/styles/shared/_switches.scss: -------------------------------------------------------------------------------- 1 | /* ---------------------------------- 2 | * CHECKBOXES / RADIOS 3 | * ---------------------------------- */ 4 | 5 | label:not([for]) { 6 | display: inline-block; 7 | vertical-align: middle; 8 | width: 5rem; 9 | height: 5rem; 10 | position: relative; 11 | background: none; 12 | } 13 | 14 | label:not([for]) input[type="checkbox"], 15 | label:not([for]) input[type="radio"] { 16 | margin: 0; 17 | opacity: 0; 18 | position: absolute; 19 | top: 0; 20 | left: 0; 21 | } 22 | 23 | label:not([for]) input + span { 24 | position: absolute; 25 | left: 50%; 26 | top: 50%; 27 | margin: -1.1rem 0 0 -1.1rem; 28 | width: 2.2rem; 29 | height: 2.2rem; 30 | pointer-events: none; 31 | } 32 | 33 | label:not([for]) input[type="checkbox"] + span { 34 | background: url(/images/shared/switches/check/default.png) no-repeat center top / 2.2rem auto; 35 | @include respond-to(peak) { 36 | background-image: url(/images/shared/switches/check/default2x.png); 37 | } 38 | } 39 | 40 | label:not([for]) input[type="radio"] + span { 41 | background: url(/images/shared/switches/radio/default.png) no-repeat center top / 2.2rem auto; 42 | @include respond-to(peak) { 43 | background-image: url(/images/shared/switches/radio/default2x.png); 44 | } 45 | } 46 | 47 | label:not([for]) input:checked + span { 48 | background-position: center bottom; 49 | } 50 | 51 | /* 'Dangerous' switches */ 52 | 53 | label:not([for]).danger input[type="checkbox"] + span { 54 | background-image: url(/images/shared/switches/check/danger.png); 55 | @include respond-to(peak) { 56 | background-image: url(/images/shared/switches/check/danger2x.png); 57 | } 58 | } 59 | 60 | label:not([for]).danger input[type="radio"] + span { 61 | background-image: url(/images/shared/switches/radio/danger.png); 62 | @include respond-to(peak) { 63 | background-image: url(/images/shared/switches/radio/danger2x.png); 64 | } 65 | } 66 | 67 | 68 | /* ---------------------------------- 69 | * ON/OFF SWITCHES 70 | * ---------------------------------- */ 71 | 72 | label input[type="checkbox"][data-type="switch"] + span { 73 | position: absolute; 74 | left: 50%; 75 | top: 50%; 76 | margin: -1.1rem 0 0 -2.7rem; 77 | width: 5.4rem; 78 | height: 2.2rem; 79 | pointer-events: none; 80 | border: solid 0.1rem #c2c2c2; 81 | border-radius: 5.4rem; 82 | overflow: hidden; 83 | background: url(/images/shared/switches/switch/handler.png) no-repeat -0.2rem center; 84 | background-size: auto calc(100% + 0.2rem); 85 | transition: background 0.18s ease; 86 | } 87 | 88 | label input[data-type="switch"] + span:after, 89 | label input[data-type="switch"] + span:before { 90 | content: ""; 91 | position: absolute; 92 | top: 0; 93 | bottom: 0; 94 | width: 5.4rem; 95 | z-index: -1; 96 | transition: transform 0.2s ease; 97 | } 98 | 99 | label input[data-type="switch"] + span:before { 100 | right: 100%; 101 | background: #00c3ea url(/images/shared/switches/switch/icon.png) no-repeat 0.5rem center; 102 | background-size: auto calc(100% - 0.2rem); 103 | } 104 | 105 | label input[data-type="switch"] + span:after { 106 | left: 100%; 107 | background: #e6e6e6; 108 | transform: translateX(-5.4rem); 109 | } 110 | 111 | /* switch: disabled state */ 112 | label input[data-type="switch"]:disabled + span:before, 113 | label input[data-type="switch"]:disabled + span:after { 114 | opacity: 0.2; 115 | } 116 | 117 | label input[data-type="switch"]:disabled + span { 118 | opacity: 0.3; 119 | } 120 | 121 | label input[data-type="switch"]:checked:disabled + span { 122 | opacity: 1; 123 | border: solid 1px #A7DBE6; 124 | } 125 | 126 | /* 'ON' state */ 127 | 128 | label input[data-type="switch"]:checked + span { 129 | border-color: #00acce; 130 | background-position: 3.2rem center; 131 | } 132 | 133 | label input[data-type="switch"]:checked + span:before { 134 | transform: translateX(5.4rem); 135 | } 136 | 137 | label input[data-type="switch"]:checked + span:after { 138 | transform: translateX(0); 139 | } 140 | 141 | label input[type="checkbox"][data-type="switch"].uninit + span, 142 | label input[type="checkbox"][data-type="switch"].uninit + span:before, 143 | label input[type="checkbox"][data-type="switch"].uninit + span:after { 144 | transition: none; 145 | } 146 | --------------------------------------------------------------------------------