Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
21
assets/webapps/email.gaiamobile.org/autoconfig/126.com
Normal file
21
assets/webapps/email.gaiamobile.org/autoconfig/126.com
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="126.com">
|
||||
<domain>126.com</domain>
|
||||
<displayName>126.com</displayName>
|
||||
<displayShortName>126.com</displayShortName>
|
||||
<incomingServer type="imap">
|
||||
<hostname>imap.126.com</hostname>
|
||||
<port>993</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>password-cleartext</authentication>
|
||||
</incomingServer>
|
||||
<outgoingServer type="smtp">
|
||||
<hostname>smtp.126.com</hostname>
|
||||
<port>465</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>password-cleartext</authentication>
|
||||
</outgoingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
21
assets/webapps/email.gaiamobile.org/autoconfig/163.com
Normal file
21
assets/webapps/email.gaiamobile.org/autoconfig/163.com
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="163.com">
|
||||
<domain>163.com</domain>
|
||||
<displayName>163.com</displayName>
|
||||
<displayShortName>163.com</displayShortName>
|
||||
<incomingServer type="imap">
|
||||
<hostname>imap.163.com</hostname>
|
||||
<port>993</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>password-cleartext</authentication>
|
||||
</incomingServer>
|
||||
<outgoingServer type="smtp">
|
||||
<hostname>smtp.163.com</hostname>
|
||||
<port>465</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>password-cleartext</authentication>
|
||||
</outgoingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
30
assets/webapps/email.gaiamobile.org/autoconfig/gmail.com
Normal file
30
assets/webapps/email.gaiamobile.org/autoconfig/gmail.com
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="googlemail.com">
|
||||
<domain>gmail.com</domain>
|
||||
<domain>googlemail.com</domain>
|
||||
<!-- MX, for Google Apps -->
|
||||
<domain>google.com</domain>
|
||||
<displayName>Google Mail</displayName>
|
||||
<displayShortName>GMail</displayShortName>
|
||||
<incomingServer type="imap">
|
||||
<hostname>imap.gmail.com</hostname>
|
||||
<port>993</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>xoauth2</authentication>
|
||||
</incomingServer>
|
||||
<outgoingServer type="smtp">
|
||||
<hostname>smtp.gmail.com</hostname>
|
||||
<port>465</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>xoauth2</authentication>
|
||||
</outgoingServer>
|
||||
<oauth2Settings>
|
||||
<secretGroup>google</secretGroup>
|
||||
<authEndpoint>https://accounts.google.com/o/oauth2/auth</authEndpoint>
|
||||
<tokenEndpoint>https://accounts.google.com/o/oauth2/token</tokenEndpoint>
|
||||
<scope>https://mail.google.com/</scope>
|
||||
</oauth2Settings>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
30
assets/webapps/email.gaiamobile.org/autoconfig/google.com
Normal file
30
assets/webapps/email.gaiamobile.org/autoconfig/google.com
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="googlemail.com">
|
||||
<domain>gmail.com</domain>
|
||||
<domain>googlemail.com</domain>
|
||||
<!-- MX, for Google Apps -->
|
||||
<domain>google.com</domain>
|
||||
<displayName>Google Mail</displayName>
|
||||
<displayShortName>GMail</displayShortName>
|
||||
<incomingServer type="imap">
|
||||
<hostname>imap.gmail.com</hostname>
|
||||
<port>993</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>xoauth2</authentication>
|
||||
</incomingServer>
|
||||
<outgoingServer type="smtp">
|
||||
<hostname>smtp.gmail.com</hostname>
|
||||
<port>465</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>xoauth2</authentication>
|
||||
</outgoingServer>
|
||||
<oauth2Settings>
|
||||
<secretGroup>google</secretGroup>
|
||||
<authEndpoint>https://accounts.google.com/o/oauth2/auth</authEndpoint>
|
||||
<tokenEndpoint>https://accounts.google.com/o/oauth2/token</tokenEndpoint>
|
||||
<scope>https://mail.google.com/</scope>
|
||||
</oauth2Settings>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="googlemail.com">
|
||||
<domain>gmail.com</domain>
|
||||
<domain>googlemail.com</domain>
|
||||
<!-- MX, for Google Apps -->
|
||||
<domain>google.com</domain>
|
||||
<displayName>Google Mail</displayName>
|
||||
<displayShortName>GMail</displayShortName>
|
||||
<incomingServer type="imap">
|
||||
<hostname>imap.gmail.com</hostname>
|
||||
<port>993</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>xoauth2</authentication>
|
||||
</incomingServer>
|
||||
<outgoingServer type="smtp">
|
||||
<hostname>smtp.gmail.com</hostname>
|
||||
<port>465</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>xoauth2</authentication>
|
||||
</outgoingServer>
|
||||
<oauth2Settings>
|
||||
<secretGroup>google</secretGroup>
|
||||
<authEndpoint>https://accounts.google.com/o/oauth2/auth</authEndpoint>
|
||||
<tokenEndpoint>https://accounts.google.com/o/oauth2/token</tokenEndpoint>
|
||||
<scope>https://mail.google.com/</scope>
|
||||
</oauth2Settings>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.co.jp
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.co.jp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.co.uk
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.co.uk
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.com
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.com
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.de
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.de
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.es
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.es
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.fr
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.fr
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.it
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/hotmail.it
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/live.co.jp
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/live.co.jp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/live.co.uk
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/live.co.uk
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/live.com
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/live.com
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/live.de
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/live.de
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/live.fr
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/live.fr
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/live.it
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/live.it
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/live.jp
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/live.jp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
30
assets/webapps/email.gaiamobile.org/autoconfig/mozilla.com
Normal file
30
assets/webapps/email.gaiamobile.org/autoconfig/mozilla.com
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="googlemail.com">
|
||||
<domain>gmail.com</domain>
|
||||
<domain>googlemail.com</domain>
|
||||
<!-- MX, for Google Apps -->
|
||||
<domain>google.com</domain>
|
||||
<displayName>Google Mail</displayName>
|
||||
<displayShortName>GMail</displayShortName>
|
||||
<incomingServer type="imap">
|
||||
<hostname>imap.gmail.com</hostname>
|
||||
<port>993</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>xoauth2</authentication>
|
||||
</incomingServer>
|
||||
<outgoingServer type="smtp">
|
||||
<hostname>smtp.gmail.com</hostname>
|
||||
<port>465</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>xoauth2</authentication>
|
||||
</outgoingServer>
|
||||
<oauth2Settings>
|
||||
<secretGroup>google</secretGroup>
|
||||
<authEndpoint>https://accounts.google.com/o/oauth2/auth</authEndpoint>
|
||||
<tokenEndpoint>https://accounts.google.com/o/oauth2/token</tokenEndpoint>
|
||||
<scope>https://mail.google.com/</scope>
|
||||
</oauth2Settings>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="googlemail.com">
|
||||
<domain>gmail.com</domain>
|
||||
<domain>googlemail.com</domain>
|
||||
<!-- MX, for Google Apps -->
|
||||
<domain>google.com</domain>
|
||||
<displayName>Google Mail</displayName>
|
||||
<displayShortName>GMail</displayShortName>
|
||||
<incomingServer type="imap">
|
||||
<hostname>imap.gmail.com</hostname>
|
||||
<port>993</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>xoauth2</authentication>
|
||||
</incomingServer>
|
||||
<outgoingServer type="smtp">
|
||||
<hostname>smtp.gmail.com</hostname>
|
||||
<port>465</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>xoauth2</authentication>
|
||||
</outgoingServer>
|
||||
<oauth2Settings>
|
||||
<secretGroup>google</secretGroup>
|
||||
<authEndpoint>https://accounts.google.com/o/oauth2/auth</authEndpoint>
|
||||
<tokenEndpoint>https://accounts.google.com/o/oauth2/token</tokenEndpoint>
|
||||
<scope>https://mail.google.com/</scope>
|
||||
</oauth2Settings>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/msn.com
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/msn.com
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
27
assets/webapps/email.gaiamobile.org/autoconfig/outlook.com
Normal file
27
assets/webapps/email.gaiamobile.org/autoconfig/outlook.com
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="hotmail.com">
|
||||
<domain>hotmail.com</domain>
|
||||
<domain>hotmail.co.uk</domain>
|
||||
<domain>hotmail.co.jp</domain>
|
||||
<domain>hotmail.com.br</domain>
|
||||
<domain>hotmail.de</domain>
|
||||
<domain>hotmail.fr</domain>
|
||||
<domain>hotmail.it</domain>
|
||||
<domain>hotmail.es</domain>
|
||||
<domain>live.com</domain>
|
||||
<domain>live.co.uk</domain>
|
||||
<domain>live.co.jp</domain>
|
||||
<domain>live.de</domain>
|
||||
<domain>live.fr</domain>
|
||||
<domain>live.it</domain>
|
||||
<domain>live.jp</domain>
|
||||
<domain>msn.com</domain>
|
||||
<domain>outlook.com</domain>
|
||||
<displayName>Microsoft Live Hotmail</displayName>
|
||||
<displayShortName>Hotmail</displayShortName>
|
||||
<incomingServer type="activesync">
|
||||
<server>https://m.hotmail.com</server>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
</incomingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
36
assets/webapps/email.gaiamobile.org/autoconfig/t-online.de
Normal file
36
assets/webapps/email.gaiamobile.org/autoconfig/t-online.de
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="t-online.de">
|
||||
<domain>t-online.de</domain>
|
||||
<displayName>T-Online</displayName>
|
||||
<displayShortName>T-Online</displayShortName>
|
||||
<incomingServer type="imap">
|
||||
<hostname>secureimap.t-online.de</hostname>
|
||||
<port>993</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>password-cleartext</authentication>
|
||||
</incomingServer>
|
||||
<incomingServer type="pop3">
|
||||
<hostname>securepop.t-online.de</hostname>
|
||||
<port>995</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>password-cleartext</authentication>
|
||||
</incomingServer>
|
||||
<outgoingServer type="smtp">
|
||||
<hostname>securesmtp.t-online.de</hostname>
|
||||
<port>587</port>
|
||||
<socketType>STARTTLS</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>password-cleartext</authentication>
|
||||
</outgoingServer>
|
||||
<enable visiturl="https://kundencenter.telekom.de/kundencenter/registriert/dienste-abos/ngcs/email-einstellungen/index.html">
|
||||
<instruction>You need to set up an E-Mail-Password. Attention: The normal password for the T-Online-Website and Webmail does *not* work for email aplications.</instruction>
|
||||
<instruction lang="de">Sie müssen ein E-Mail-Passwort einrichten. Achtung: Das Passwort für die T-Online-Website und Webmail funktioniert *nicht* für E-Mail-Programme!</instruction>
|
||||
</enable>
|
||||
<documentation url="http://hilfe.telekom.de/hsp/cms/content/HSP/de/3378/theme-45858870/theme-45858718/theme-45858716/theme-45858715/faq-45855229">
|
||||
<descr lang="en">Set up your email address</descr>
|
||||
<descr>Richten Sie Ihre Email-Adresse ein</descr>
|
||||
</documentation>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
21
assets/webapps/email.gaiamobile.org/autoconfig/yahoo.co.jp
Normal file
21
assets/webapps/email.gaiamobile.org/autoconfig/yahoo.co.jp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="yahoo.com">
|
||||
<domain>yahoo.com</domain>
|
||||
<displayName>Yahoo! Mail</displayName>
|
||||
<displayShortName>Yahoo</displayShortName>
|
||||
<incomingServer type="imap">
|
||||
<hostname>imap.mail.yahoo.co.jp</hostname>
|
||||
<port>993</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>password-cleartext</authentication>
|
||||
</incomingServer>
|
||||
<outgoingServer type="smtp">
|
||||
<hostname>smtp.mail.yahoo.co.jp</hostname>
|
||||
<port>465</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>password-cleartext</authentication>
|
||||
</outgoingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
21
assets/webapps/email.gaiamobile.org/autoconfig/yahoo.com
Normal file
21
assets/webapps/email.gaiamobile.org/autoconfig/yahoo.com
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<clientConfig version="1.1">
|
||||
<emailProvider id="yahoo.com">
|
||||
<domain>yahoo.com</domain>
|
||||
<displayName>Yahoo! Mail</displayName>
|
||||
<displayShortName>Yahoo</displayShortName>
|
||||
<incomingServer type="imap">
|
||||
<hostname>imap.mail.yahoo.com</hostname>
|
||||
<port>993</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>password-cleartext</authentication>
|
||||
</incomingServer>
|
||||
<outgoingServer type="smtp">
|
||||
<hostname>smtp.mail.yahoo.com</hostname>
|
||||
<port>465</port>
|
||||
<socketType>SSL</socketType>
|
||||
<username>%EMAILADDRESS%</username>
|
||||
<authentication>password-cleartext</authentication>
|
||||
</outgoingServer>
|
||||
</emailProvider>
|
||||
</clientConfig>
|
||||
21
assets/webapps/email.gaiamobile.org/gaia_shared.json
Normal file
21
assets/webapps/email.gaiamobile.org/gaia_shared.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"js": [
|
||||
"mime_mapper.js",
|
||||
"notification_helper.js",
|
||||
"gesture_detector.js",
|
||||
"accessibility_helper.js"
|
||||
],
|
||||
"style": [
|
||||
"input_areas.css",
|
||||
"headers.css",
|
||||
"switches.css",
|
||||
"buttons.css",
|
||||
"status.css",
|
||||
"confirm.css",
|
||||
"action_menu.css",
|
||||
"progress_activity.css",
|
||||
"tabs.css",
|
||||
"value_selector.css"
|
||||
],
|
||||
"style_unstable": []
|
||||
}
|
||||
63
assets/webapps/email.gaiamobile.org/index.html
Normal file
63
assets/webapps/email.gaiamobile.org/index.html
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!-- The theme color starts off black. The card logic wants to choose a start
|
||||
card first, since the color schemes are different for New Account vs Message
|
||||
List. This means email starts up with a black header that fades to the correct
|
||||
color once the card logic makes a choice. Due to the html cache, a black
|
||||
status color is shown at first, then fades to the right color. This is
|
||||
preferred to the alternative of starting up in default orange. If New Account
|
||||
is the start card, it looks gross to have the orange on top of the white card
|
||||
for a moment. -->
|
||||
<meta name="theme-color" data-statuscolor="#ff7d14" content="#000000">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<title>Email</title>
|
||||
<link rel="stylesheet" type="text/css" href="style/mail.css">
|
||||
<!-- Localization -->
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="ar:201601260730, bg:201601260730, bn-BD:201601260730, ca:201601260730, cs:201601260730, da:201601260730, de:201601260730, el:201601260730, en-US:201601260730, es:201601260730, eu:201601260730, fr:201601260730, gl:201601260730, hr:201601260730, hu:201601260730, it:201601260730, ja:201601260730, km:201601260730, ko:201601260730, lt:201601260730, mk:201601260730, ms:201601260730, ne-NP:201601260730, nl:201601260730, pa:201601260730, pl:201601260730, pt-BR:201601260730, ro:201601260730, ru:201601260730, sk:201601260730, sq:201601260730, sr-Cyrl:201601260730, sr-Latn:201601260730, sv-SE:201601260730, tr:201601260730, zh-CN:201601260730, zh-TW:201601260730">
|
||||
<link rel="localization" href="/locales-obj/index.{locale}.json">
|
||||
|
||||
|
||||
|
||||
<!-- These are lazy loaded. Need to be included here so the build picks them up
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/input_areas.css" >
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/buttons.css" >
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/headers.css" >
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/switches.css" >
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/status.css" >
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/confirm.css" >
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/action_menu.css" >
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/progress_activity.css" >
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/tabs.css" >
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/value_selector.css" >
|
||||
-->
|
||||
|
||||
<!--
|
||||
The following scripts are lazy loaded but left here to ensure
|
||||
they get copied over from shared during the build
|
||||
-->
|
||||
<!-- <script defer src="/shared/js/l10n.js"></script> -->
|
||||
<!-- <script defer src="/shared/js/moz_intl.js"></script> -->
|
||||
<!-- <script defer src="/shared/js/mime_mapper.js"></script> -->
|
||||
<!-- <script defer src="/shared/js/gesture_detector.js"></script> -->
|
||||
<!-- <script defer src="/shared/js/notification_helper.js"></script> -->
|
||||
<!-- <script defer src="/shared/js/accessibility_helper.js"></script> -->
|
||||
<meta content="2.5" name="appVersion"></head>
|
||||
|
||||
<body role="application">
|
||||
<!-- All the active cards live (horizontally) in this container. We leave
|
||||
it up to the layout engine to be smart about not painting cards that are
|
||||
not visible, but we will revisit that after profiling. (display: hidden
|
||||
should be fairly safe, for example.) -->
|
||||
<div id="cardContainer">
|
||||
<div id="cards" style="left: 0px;"></div>
|
||||
</div>
|
||||
<script data-targetid="cards" data-loadsrc="js/config.js" src="js/html_cache_restore.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
define([
|
||||
'require',
|
||||
'l10n!',
|
||||
'cards'
|
||||
], function (require) {
|
||||
var mozL10n = require('l10n!'), cards = require('cards');
|
||||
return {
|
||||
_bindPrefs: function (checkIntervalClassName, notifyEmailClassName, soundOnSendClassName, signatureEnabledClassName, signatureButtonClassName) {
|
||||
if (checkIntervalClassName) {
|
||||
var checkIntervalNode = this.nodeFromClass(checkIntervalClassName), currentInterval = this.account.syncInterval, syncIntervalString = String(currentInterval), extraOptions = [];
|
||||
if (typeof _secretDebug !== 'undefined' && _secretDebug.fastSync) {
|
||||
extraOptions = extraOptions.concat(_secretDebug.fastSync);
|
||||
}
|
||||
var hasOption = Array.slice(checkIntervalNode.options, 0).some(function (option) {
|
||||
return syncIntervalString === option.value;
|
||||
});
|
||||
if (!hasOption && extraOptions.indexOf(currentInterval) === -1) {
|
||||
extraOptions.push(currentInterval);
|
||||
}
|
||||
extraOptions.forEach(function (interval) {
|
||||
var node = document.createElement('option'), seconds = interval / 1000;
|
||||
node.value = String(interval);
|
||||
mozL10n.setAttributes(node, 'settings-check-dynamic', { n: seconds });
|
||||
checkIntervalNode.appendChild(node);
|
||||
});
|
||||
checkIntervalNode.value = syncIntervalString;
|
||||
checkIntervalNode.addEventListener('change', this.onChangeSyncInterval.bind(this), false);
|
||||
}
|
||||
if (notifyEmailClassName) {
|
||||
var notifyMailNode = this.nodeFromClass(notifyEmailClassName);
|
||||
notifyMailNode.addEventListener('click', this.onNotifyEmailClick.bind(this), false);
|
||||
notifyMailNode.checked = this.account.notifyOnNew;
|
||||
}
|
||||
if (soundOnSendClassName) {
|
||||
var soundOnSendNode = this.nodeFromClass(soundOnSendClassName);
|
||||
soundOnSendNode.addEventListener('click', this.onSoundOnSendClick.bind(this), false);
|
||||
soundOnSendNode.checked = this.account.playSoundOnSend;
|
||||
}
|
||||
if (signatureEnabledClassName) {
|
||||
var signatureEnabledNode = this.nodeFromClass(signatureEnabledClassName);
|
||||
signatureEnabledNode.addEventListener('click', this.onSignatureEnabledClick.bind(this), false);
|
||||
signatureEnabledNode.checked = !!this.identity.signatureEnabled;
|
||||
}
|
||||
if (signatureButtonClassName) {
|
||||
this.signatureButton = this.nodeFromClass(signatureButtonClassName);
|
||||
this.updateSignatureButton();
|
||||
this.signatureButton.addEventListener('click', this.onClickSignature.bind(this), false);
|
||||
}
|
||||
},
|
||||
nodeFromClass: function (className) {
|
||||
return this.getElementsByClassName(className)[0];
|
||||
},
|
||||
onChangeSyncInterval: function (event) {
|
||||
var value = parseInt(event.target.value, 10);
|
||||
console.log('sync interval changed to', value);
|
||||
this.account.modifyAccount({ syncInterval: value });
|
||||
},
|
||||
onNotifyEmailClick: function (event) {
|
||||
var checked = event.target.checked;
|
||||
console.log('notifyOnNew changed to: ' + checked);
|
||||
this.account.modifyAccount({ notifyOnNew: checked });
|
||||
},
|
||||
onSoundOnSendClick: function (event) {
|
||||
var checked = event.target.checked;
|
||||
console.log('playSoundOnSend changed to: ' + checked);
|
||||
this.account.modifyAccount({ playSoundOnSend: checked });
|
||||
},
|
||||
onSignatureEnabledClick: function (event) {
|
||||
var checked = event.target.checked;
|
||||
console.log('signatureEnabled changed to: ' + checked);
|
||||
this.identity.modifyIdentity({ signatureEnabled: checked });
|
||||
},
|
||||
updateSignatureButton: function () {
|
||||
var text = this.identity.signature || '', isEmpty = text.trim().length === 0, node = this.signatureButton.firstElementChild;
|
||||
node.textContent = text;
|
||||
node.classList.toggle('empty-placeholder', isEmpty);
|
||||
if (isEmpty) {
|
||||
mozL10n.setAttributes(node, 'settings-empty-signature-label');
|
||||
} else {
|
||||
node.removeAttribute('data-l10n-id');
|
||||
}
|
||||
},
|
||||
onClickSignature: function (index) {
|
||||
cards.pushCard('settings_signature', 'animate', {
|
||||
account: this.account,
|
||||
index: index
|
||||
}, 'right');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<li class="cmp-attachment-item">
|
||||
<span class="cmp-attachment-icon" role="presentation"></span>
|
||||
<span class="cmp-attachment-fileinfo">
|
||||
<span dir="auto" class="cmp-attachment-filename"></span>
|
||||
<span class="cmp-attachment-filesize"></span>
|
||||
</span>
|
||||
<span class="cmp-attachment-remove" role="button"
|
||||
data-l10n-id="compose-attachment-remove"></span>
|
||||
</li>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<li class="cmp-autocomplete-item">
|
||||
<!-- Using an a tag so that screen reader properly activates a selection
|
||||
when tapped. Using a div meant the ul element got the click. To get proper full bleed on the active/selected color, need to use another div inside
|
||||
the a tag to get that while keeping the margins on text and border. -->
|
||||
<a class="cmp-autocomplete-item-link" href="#">
|
||||
<div class="cmp-autocomplete-item-details">
|
||||
<span class="cmp-autocomplete-name" dir="auto" aria-hidden="true"></span>
|
||||
<span class="cmp-autocomplete-email" dir="auto" aria-hidden="true"></span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
define(['require'], function (require) {
|
||||
var contacts = navigator.mozContacts;
|
||||
function makeResult(query, contacts) {
|
||||
return {
|
||||
query: query,
|
||||
contacts: contacts
|
||||
};
|
||||
}
|
||||
return function match(query) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (!contacts || !query) {
|
||||
return resolve(makeResult(query, []));
|
||||
}
|
||||
var request = contacts.find({
|
||||
filterBy: [
|
||||
'email',
|
||||
'name',
|
||||
'givenName',
|
||||
'familyName'
|
||||
],
|
||||
filterOp: 'startsWith',
|
||||
filterLimit: 30,
|
||||
filterValue: query
|
||||
});
|
||||
request.onsuccess = function () {
|
||||
resolve(makeResult(query, this.result));
|
||||
};
|
||||
request.onerror = function (err) {
|
||||
console.error('autocomplete_source contacts.find error: ' + err);
|
||||
resolve(makeResult(query, []));
|
||||
};
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<form class="cmp-contact-menu" role="dialog" data-type="action">
|
||||
<header></header>
|
||||
<menu>
|
||||
<button class="cmp-contact-menu-edit" data-l10n-id="message-edit-menu-edit">
|
||||
</button>
|
||||
<button class="cmp-contact-menu-delete" data-l10n-id="message-edit-menu-delete">
|
||||
</button>
|
||||
<span class="last-button-container">
|
||||
<button class="cmp-contact-menu-cancel" data-l10n-id="message-multiedit-cancel">
|
||||
</button>
|
||||
</span>
|
||||
</menu>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<form role="dialog" class="cmp-draft-menu" data-type="action">
|
||||
<menu>
|
||||
<button id="cmp-draft-save" data-l10n-id="compose-draft-save"></button>
|
||||
<button id="cmp-draft-discard" class="danger" data-l10n-id="compose-delete-confirm"></button>
|
||||
<span class="last-button-container">
|
||||
<button id="cmp-draft-cancel" data-l10n-id="message-multiedit-cancel"></button>
|
||||
</span>
|
||||
</menu>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<form role="dialog" data-type="confirm">
|
||||
<section>
|
||||
<h1 data-l10n-id="compose-invalid-addresses-title"></h1>
|
||||
<p data-l10n-id="compose-invalid-addresses-description"></p>
|
||||
</section>
|
||||
<menu>
|
||||
<button id="cmp-confirm-invalid-addresses"
|
||||
class="confirm-dialog-ok full recommend"
|
||||
data-l10n-id="dialog-button-ok"></button>
|
||||
</menu>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<div class="cmp-peep-bubble peep-bubble" dir="auto" role="button">
|
||||
<span class="cmp-peep-name"></span>
|
||||
<span class="cmp-peep-address collapsed"></span>
|
||||
</div>
|
||||
119
assets/webapps/email.gaiamobile.org/js/cards/compose.html
Normal file
119
assets/webapps/email.gaiamobile.org/js/cards/compose.html
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
<section class="cmp-compose-header" role="region" data-statuscolor="default">
|
||||
<header data-prop="headerNode">
|
||||
<a role="button" data-event="click:onBack" href="#" class="cmp-back-btn"
|
||||
data-l10n-id="back-button">
|
||||
<span class="icon icon-back"></span>
|
||||
</a>
|
||||
<menu type="toolbar">
|
||||
<a role="button" href="#" data-event="click:onAttachmentAdd"
|
||||
class="cmp-attachment-btn" data-l10n-id="compose-attachment-button">
|
||||
<span class="icon icon-attachment"></span>
|
||||
</a>
|
||||
<a role="button" data-prop="sendButton" data-event="click:onSend"
|
||||
href="#" class="cmp-send-btn" data-l10n-id="compose-send-button">
|
||||
<span class="icon icon-send"></span>
|
||||
</a>
|
||||
</menu>
|
||||
<h1 class="cmp-compose-header-label"
|
||||
data-l10n-id="compose-new-header"></h1>
|
||||
</header>
|
||||
</section>
|
||||
<div data-prop="scrollContainer"
|
||||
class="scrollregion-below-header cmp-autocomplete-origin">
|
||||
<div data-prop="addrBar">
|
||||
<div class="cmp-autocomplete-input-container">
|
||||
<div class="cmp-autocomplete-input-list">
|
||||
<div data-prop="firstEnvelopeLine"
|
||||
data-event="click:onContainerClick"
|
||||
class="cmp-envelope-line cmp-combo cmp-autocomplete-input-label"
|
||||
role="group" data-l10n-id="to-group">
|
||||
<span class="cmp-to-label cmp-addr-label"
|
||||
data-l10n-id="compose-to" aria-hidden="true"></span>
|
||||
<div class="cmp-to-container cmp-addr-container">
|
||||
<div class="cmp-bubble-container">
|
||||
<input data-prop="toNode"
|
||||
data-event="keydown:onAddressKeydown,input:onAddressInput"
|
||||
dir="auto"
|
||||
class="cmp-to-text cmp-addr-text" type="email" />
|
||||
</div>
|
||||
</div>
|
||||
<div data-event="click:onContactAdd"
|
||||
class="cmp-to-add cmp-contact-add"
|
||||
role="button"
|
||||
data-l10n-id="compose-contact-add-recipient"></div>
|
||||
</div>
|
||||
<!-- XXX: spec calls for showing cc/bcc merged until selected,
|
||||
but there is also the case where replying itself might need
|
||||
to expand, so we are deferring that feature -->
|
||||
<div data-event="click:onContainerClick"
|
||||
class="cmp-envelope-line cmp-combo cmp-autocomplete-input-label"
|
||||
role="group" data-l10n-id="cc-group">
|
||||
<span class="cmp-cc-label cmp-addr-label"
|
||||
data-l10n-id="compose-cc" aria-hidden="true"></span>
|
||||
<div class="cmp-cc-container cmp-addr-container">
|
||||
<div class="cmp-bubble-container">
|
||||
<input data-prop="ccNode"
|
||||
data-event="keydown:onAddressKeydown,input:onAddressInput"
|
||||
dir="auto"
|
||||
class="cmp-cc-text cmp-addr-text" type="email" />
|
||||
</div>
|
||||
</div>
|
||||
<div data-event="click:onContactAdd"
|
||||
class="cmp-cc-add cmp-contact-add"
|
||||
role="button"
|
||||
data-l10n-id="compose-contact-add-recipient"></div>
|
||||
</div>
|
||||
<div data-event="click:onContainerClick"
|
||||
class="cmp-envelope-line cmp-combo cmp-autocomplete-input-label"
|
||||
role="group" data-l10n-id="bcc-group">
|
||||
<span class="cmp-bcc-label cmp-addr-label"
|
||||
data-l10n-id="compose-bcc" aria-hidden="true"></span>
|
||||
<div class="cmp-bcc-container cmp-addr-container">
|
||||
<div class="cmp-bubble-container">
|
||||
<input data-prop="bccNode"
|
||||
data-event="keydown:onAddressKeydown,input:onAddressInput"
|
||||
dir="auto"
|
||||
class="cmp-bcc-text cmp-addr-text" type="email" />
|
||||
</div>
|
||||
</div>
|
||||
<div data-event="click:onContactAdd"
|
||||
class="cmp-bcc-add cmp-contact-add"
|
||||
role="button"
|
||||
data-l10n-id="compose-contact-add-recipient"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cmp-envelope-line cmp-subject">
|
||||
<span class="cmp-subject-label"
|
||||
data-l10n-id="compose-subject" aria-hidden="true"></span>
|
||||
<input data-prop="subjectNode"
|
||||
dir="auto"
|
||||
class="cmp-subject-text" type="text" data-l10n-id="subject" />
|
||||
</div>
|
||||
<div data-prop="errorMessage" class="cmp-error-message collapsed"></div>
|
||||
<div data-prop="attachmentTotal"
|
||||
class="cmp-envelope-line cmp-attachment-total collapsed"
|
||||
aria-hidden="true">
|
||||
<span data-prop="attachmentLabel"
|
||||
class="cmp-attachment-label cmp-addr-label"
|
||||
data-l10n-id="compose-attachments[zero]"></span>
|
||||
<span data-prop="attachmentsSize" class="cmp-attachment-size"></span>
|
||||
</div>
|
||||
<ul data-prop="attachmentsContainer" class="cmp-attachment-container">
|
||||
</ul>
|
||||
</div>
|
||||
<div data-prop="textBodyNode" class="cmp-body-text" contenteditable="true" role="textbox" aria-multiline="true"
|
||||
data-l10n-id="compose-text"></div>
|
||||
<div data-prop="htmlBodyContainer"
|
||||
data-event="click:_focusEditorWithCursorAtEnd"
|
||||
dir="auto"
|
||||
class="cmp-body-html"
|
||||
data-l10n-id="message-body-container">
|
||||
</div>
|
||||
<!-- Autocomplete at the bottom since it should have the highest z order
|
||||
placement, so that the autocomplete matches are visible. -->
|
||||
<cmp-autocomplete data-prop="autocomplete"
|
||||
data-event="autocompleteSelected"
|
||||
data-source="cards/cmp/autocomplete_source">
|
||||
</cmp-autocomplete>
|
||||
</div>
|
||||
1082
assets/webapps/email.gaiamobile.org/js/cards/compose.js
Normal file
1082
assets/webapps/email.gaiamobile.org/js/cards/compose.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,13 @@
|
|||
<div class="card-confirm-dialog card">
|
||||
<form data-statuscolor="background"
|
||||
role="dialog" data-type="confirm" class="collapsed confirm-dialog-form">
|
||||
<section>
|
||||
<h1 data-l10n-id="confirm-dialog-title"></h1>
|
||||
<p class="confirm-dialog-message"></p>
|
||||
</section>
|
||||
<menu>
|
||||
<button class="confirm-dialog-cancel" data-l10n-id="message-multiedit-cancel"></button>
|
||||
<button class="confirm-dialog-ok recommend" data-l10n-id="dialog-button-ok"></button>
|
||||
</menu>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<a class="fld-account-item">
|
||||
<span class="selected-indicator"></span>
|
||||
<span class="fld-account-name"></span>
|
||||
</a>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<a class="fld-folder-item">
|
||||
<span class="selected-indicator"></span>
|
||||
<span dir="auto" class="fld-folder-name"></span>
|
||||
<span class="fld-folder-unread"></span>
|
||||
</a>
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<!-- Our card does not have a header of its own; it reuses the message_list's
|
||||
header. We want to steal clicks on its menu button too, hence this node
|
||||
and fld-header-back which is a transparent button that overlays the menu
|
||||
button. -->
|
||||
<div class="fld-header-placeholder" data-statuscolor="default">
|
||||
<!-- Unlike a generic back button that navigates to a different screen, folder
|
||||
picker header button triggers the folders and settings overlay closure.
|
||||
Thus the screen reader user requires more context as to what activating
|
||||
the button would do. -->
|
||||
<button aria-expanded="true" aria-controls="cards-folder-picker"
|
||||
data-l10n-id="message-list-menu"
|
||||
data-event="click:_closeCard"
|
||||
class="fld-header-back"></button>
|
||||
</div>
|
||||
<!-- Backing semi-opaque layer for everything below the header so that
|
||||
anything that the folder drawer does not cover looks inactive-ish.
|
||||
Clicking on this makes the drawer go away. -->
|
||||
<div data-event="click:_closeCard" class="fld-shield"></div>
|
||||
<!-- This exists to clip fld-content so that when it animates in/out using
|
||||
translateY it doesn't paint over-top of the header. -->
|
||||
<div class="fld-content-container">
|
||||
<!-- This is the actual drawer thing that slides up/down using translateY.
|
||||
-->
|
||||
<div class="fld-content">
|
||||
<!-- Scrollable container holds everything but the non-scrolling settings
|
||||
button. -->
|
||||
<div class="fld-acct-scrollouter">
|
||||
<!-- Combo-box style widget that shows the current account name when
|
||||
collapsed and the fld-acct-header-account-header label when
|
||||
expanded. The actual items live in fld-accountlist-container
|
||||
for animation reasons; see below. -->
|
||||
<a data-prop="accountHeader"
|
||||
data-event="click:toggleAccounts"
|
||||
class="fld-acct-header closed" role="region">
|
||||
<!--
|
||||
The is on purpose, so that initial height measurement of the
|
||||
element be correct even before this element has real content.
|
||||
-->
|
||||
<span class="fld-acct-header-account-label"> </span>
|
||||
<span class="fld-acct-header-account-header"
|
||||
data-l10n-id="drawer-accounts-header"></span>
|
||||
<span class="fld-account-switch-arrow"></span>
|
||||
</a>
|
||||
<!-- Exists to clip fld-acct-container which uses translateY to hide the
|
||||
list of accounts in negative-space until the account list is
|
||||
opened. This has an explicit height style set based on whether the
|
||||
list of accounts is displayed or not because fld-acct-scrollinner
|
||||
will always have the same height for layout purposes because the
|
||||
translateY hiding does not affect layout. -->
|
||||
<div data-prop="fldAcctScrollInner" class="fld-acct-scrollinner">
|
||||
<!-- The list of all accounts and the folders for the current account.
|
||||
As noted above, translateY is used to hide the list of accounts
|
||||
when we don't want to be displaying it. -->
|
||||
<div data-prop="fldAcctContainer" class="fld-acct-container">
|
||||
<!-- The list of accounts -->
|
||||
<div data-prop="accountListContainer"
|
||||
class="fld-accountlist-container">
|
||||
</div>
|
||||
<!-- The list of folders for the current account. -->
|
||||
<div data-prop="foldersContainer"
|
||||
class="fld-folders-container">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- settings button; always present; does not scroll -->
|
||||
<a data-event="click:onShowSettings" class="fld-nav-toolbar bottom-toolbar">
|
||||
<span class="fld-settings-link" data-l10n-id="drawer-settings-link"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
213
assets/webapps/email.gaiamobile.org/js/cards/folder_picker.js
Normal file
213
assets/webapps/email.gaiamobile.org/js/cards/folder_picker.js
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
define([
|
||||
'require',
|
||||
'container_listen',
|
||||
'tmpl!./fld/folder_item.html',
|
||||
'tmpl!./fld/account_item.html',
|
||||
'folder_depth_classes',
|
||||
'cards',
|
||||
'evt',
|
||||
'transition_end',
|
||||
'css!style/folder_cards',
|
||||
'./base_card',
|
||||
'template!./folder_picker.html'
|
||||
], function (require) {
|
||||
var containerListen = require('container_listen'), fldFolderItemNode = require('tmpl!./fld/folder_item.html'), fldAccountItemNode = require('tmpl!./fld/account_item.html'), FOLDER_DEPTH_CLASSES = require('folder_depth_classes'), cards = require('cards'), evt = require('evt'), transitionEnd = require('transition_end');
|
||||
require('css!style/folder_cards');
|
||||
return [
|
||||
require('./base_card')(require('template!./folder_picker.html')),
|
||||
{
|
||||
createdCallback: function () {
|
||||
containerListen(this.foldersContainer, 'click', this.onClickFolder.bind(this));
|
||||
containerListen(this.accountListContainer, 'click', this.onClickAccount.bind(this));
|
||||
transitionEnd(this, this.onTransitionEnd.bind(this));
|
||||
this.updateAccount = this.updateAccount.bind(this);
|
||||
},
|
||||
onArgs: function (args) {
|
||||
var model = this.model = args.model;
|
||||
model.latest('account', this.updateAccount);
|
||||
var accountCount = model.getAccountCount();
|
||||
if (accountCount > 1) {
|
||||
this.classList.remove('one-account');
|
||||
this.currentAccountContainerHeight = this.accountHeader.getBoundingClientRect().height * accountCount;
|
||||
this.hideAccounts();
|
||||
}
|
||||
this.acctsSlice = model.api.viewAccounts(false);
|
||||
this.acctsSlice.onsplice = this.onAccountsSplice.bind(this);
|
||||
this.acctsSlice.onchange = this.onAccountsChange.bind(this);
|
||||
},
|
||||
extraClasses: [
|
||||
'anim-vertical',
|
||||
'anim-overlay',
|
||||
'one-account'
|
||||
],
|
||||
onShowSettings: function (event) {
|
||||
cards.pushCard('settings_main', 'animate');
|
||||
},
|
||||
updateAccount: function (account) {
|
||||
var oldAccount = this.curAccount;
|
||||
this.mostRecentSyncTimestamp = 0;
|
||||
if (oldAccount !== account) {
|
||||
this.foldersContainer.innerHTML = '';
|
||||
this.model.latestOnce('folder', function (folder) {
|
||||
this.curAccount = account;
|
||||
this.querySelector('.fld-acct-header-account-label').textContent = account.name;
|
||||
if (!this.curFolder) {
|
||||
this.curFolder = folder;
|
||||
}
|
||||
if (this.foldersSlice) {
|
||||
this.foldersSlice.onsplice = null;
|
||||
this.foldersSlice.onchange = null;
|
||||
}
|
||||
this.foldersSlice = this.model.foldersSlice;
|
||||
this.onFoldersSplice(0, 0, this.foldersSlice.items, true, false);
|
||||
this.foldersSlice.onsplice = this.onFoldersSplice.bind(this);
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
onClickAccount: function (accountNode, event) {
|
||||
var oldAccountId = this.curAccount.id, accountId = accountNode.account.id;
|
||||
this.curAccount = accountNode.account;
|
||||
if (oldAccountId !== accountId) {
|
||||
this._waitingAccountId = accountId;
|
||||
this._closeCard();
|
||||
}
|
||||
},
|
||||
toggleAccounts: function () {
|
||||
var hadAnimated = this.fldAcctContainer.classList.contains('animated');
|
||||
if (!hadAnimated) {
|
||||
this.fldAcctContainer.classList.add('animated');
|
||||
this.fldAcctContainer.clientWidth;
|
||||
}
|
||||
if (this.accountHeader.classList.contains('closed')) {
|
||||
this.showAccounts();
|
||||
} else {
|
||||
this.hideAccounts();
|
||||
}
|
||||
},
|
||||
showAccounts: function () {
|
||||
var height = this.currentAccountContainerHeight;
|
||||
this.fldAcctScrollInner.style.height = height + this.foldersContainer.getBoundingClientRect().height + 'px';
|
||||
this.fldAcctContainer.style.transform = 'translateY(0)';
|
||||
this.accountHeader.classList.remove('closed');
|
||||
},
|
||||
hideAccounts: function () {
|
||||
var foldersHeight = this.foldersContainer.getBoundingClientRect().height;
|
||||
if (foldersHeight) {
|
||||
this.fldAcctScrollInner.style.height = foldersHeight + 'px';
|
||||
}
|
||||
this.fldAcctContainer.style.transform = 'translateY(-' + this.currentAccountContainerHeight + 'px)';
|
||||
this.accountHeader.classList.add('closed');
|
||||
},
|
||||
onAccountsSplice: function (index, howMany, addedItems, requested, moreExpected) {
|
||||
var accountListContainer = this.accountListContainer;
|
||||
var postSliceCount = this.acctsSlice.items.length + addedItems.length - howMany;
|
||||
this.classList.toggle('one-account', postSliceCount <= 1);
|
||||
var account;
|
||||
if (howMany) {
|
||||
for (var i = index + howMany - 1; i >= index; i--) {
|
||||
account = this.acctsSlice.items[i];
|
||||
if (account.element) {
|
||||
accountListContainer.removeChild(account.element);
|
||||
}
|
||||
}
|
||||
}
|
||||
var insertBuddy = index >= accountListContainer.childElementCount ? null : accountListContainer.children[index];
|
||||
addedItems.forEach(function (account) {
|
||||
var accountNode = account.element = fldAccountItemNode.cloneNode(true);
|
||||
accountNode.account = account;
|
||||
this.updateAccountDom(account, true);
|
||||
accountListContainer.insertBefore(accountNode, insertBuddy);
|
||||
}.bind(this));
|
||||
this.currentAccountContainerHeight = this.accountHeader.getBoundingClientRect().height * accountListContainer.children.length;
|
||||
this.hideAccounts();
|
||||
},
|
||||
onAccountsChange: function (account) {
|
||||
this.updateAccountDom(account, false);
|
||||
},
|
||||
updateAccountDom: function (account, firstTime) {
|
||||
var accountNode = account.element;
|
||||
if (firstTime) {
|
||||
accountNode.querySelector('.fld-account-name').textContent = account.name;
|
||||
if (this.curAccount && this.curAccount.id === account.id) {
|
||||
accountNode.classList.add('fld-account-selected');
|
||||
}
|
||||
}
|
||||
},
|
||||
onFoldersSplice: function (index, howMany, addedItems, requested, moreExpected) {
|
||||
var foldersContainer = this.foldersContainer;
|
||||
var folder;
|
||||
if (howMany) {
|
||||
for (var i = index + howMany - 1; i >= index; i--) {
|
||||
folder = this.foldersSlice.items[i];
|
||||
foldersContainer.removeChild(folder.element);
|
||||
}
|
||||
}
|
||||
var insertBuddy = index >= foldersContainer.childElementCount ? null : foldersContainer.children[index], self = this;
|
||||
addedItems.forEach(function (folder) {
|
||||
var folderNode = folder.element = fldFolderItemNode.cloneNode(true);
|
||||
folderNode.folder = folder;
|
||||
self.updateFolderDom(folder, true);
|
||||
foldersContainer.insertBefore(folderNode, insertBuddy);
|
||||
});
|
||||
},
|
||||
updateFolderDom: function (folder, firstTime) {
|
||||
var folderNode = folder.element;
|
||||
if (firstTime) {
|
||||
if (!folder.selectable) {
|
||||
folderNode.classList.add('fld-folder-unselectable');
|
||||
}
|
||||
var depthIdx = Math.min(FOLDER_DEPTH_CLASSES.length - 1, folder.depth);
|
||||
folderNode.classList.add(FOLDER_DEPTH_CLASSES[depthIdx]);
|
||||
if (depthIdx > 0) {
|
||||
folderNode.classList.add('fld-folder-depthnonzero');
|
||||
}
|
||||
folderNode.querySelector('.fld-folder-name').textContent = folder.name;
|
||||
folderNode.dataset.type = folder.type;
|
||||
}
|
||||
if (folder === this.curFolder) {
|
||||
folderNode.classList.add('fld-folder-selected');
|
||||
} else {
|
||||
folderNode.classList.remove('fld-folder-selected');
|
||||
}
|
||||
},
|
||||
onClickFolder: function (folderNode, event) {
|
||||
var folder = folderNode.folder;
|
||||
if (!folder.selectable) {
|
||||
return;
|
||||
}
|
||||
var oldFolder = this.curFolder;
|
||||
this.curFolder = folder;
|
||||
this.updateFolderDom(oldFolder);
|
||||
this.updateFolderDom(folder);
|
||||
this._showFolder(folder);
|
||||
this._closeCard();
|
||||
},
|
||||
onTransitionEnd: function (event) {
|
||||
if (!this.classList.contains('opened') && event.target.classList.contains('fld-content')) {
|
||||
cards.removeCardAndSuccessors(this, 'none');
|
||||
if (this._waitingAccountId) {
|
||||
var model = this.model;
|
||||
model.changeAccountFromId(this._waitingAccountId, function () {
|
||||
model.selectInbox();
|
||||
});
|
||||
this._waitingAccountId = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
_closeCard: function () {
|
||||
evt.emit('folderPickerClosing');
|
||||
this.classList.remove('opened');
|
||||
},
|
||||
_showFolder: function (folder) {
|
||||
this.model.changeFolder(folder);
|
||||
},
|
||||
onCardVisible: function () {
|
||||
this.classList.add('opened');
|
||||
},
|
||||
die: function () {
|
||||
this.acctsSlice.die();
|
||||
this.model.removeListener('account', this.updateAccount);
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<section role="region">
|
||||
<header>
|
||||
<a href="#"
|
||||
data-event="click:domEvt"
|
||||
data-domevt-name="editHeaderClose"
|
||||
role="button"
|
||||
data-l10n-id="close-button">
|
||||
<span class="icon icon-close"></span>
|
||||
</a>
|
||||
<h1 data-prop="headerNode"></h1>
|
||||
</header>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<!-- Toolbar for multi-edit state -->
|
||||
<ul class="bb-tablist" role="toolbar">
|
||||
<li role="presentation">
|
||||
<button data-prop="deleteBtn"
|
||||
data-event="click:domEvt"
|
||||
data-domevt-name="onDeleteMessages"
|
||||
class="icon msg-delete-btn"
|
||||
data-l10n-id="message-delete-button"></button>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button data-prop="starBtn"
|
||||
data-event="click:domEvt"
|
||||
data-domevt-name="onStarMessages"
|
||||
class="icon msg-star-btn"
|
||||
data-l10n-id="message-star-button"></button>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button data-prop="readBtn"
|
||||
data-event="click:domEvt"
|
||||
data-domevt-name="onMarkMessagesRead"
|
||||
class="icon msg-mark-read-btn"
|
||||
data-l10n-id="message-mark-read-button"></button>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button data-prop="moveBtn"
|
||||
data-event="click:domEvt"
|
||||
data-domevt-name="onMoveMessages"
|
||||
class="icon msg-move-btn"
|
||||
data-l10n-id="message-move-button"></button>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<div data-prop="vScrollContainer" class="msg-vscroll-container"></div>
|
||||
<!-- maintain vertical space for the syncing/sync more div's
|
||||
regardless of their displayed status so we don't scroll them
|
||||
out of the way -->
|
||||
<div class="msg-messages-sync-container">
|
||||
<p data-prop="syncingNode" class="msg-messages-syncing collapsed"
|
||||
role="progressbar" data-l10n-id="messages-syncing-progressbar">
|
||||
<span data-l10n-id="messages-syncing"></span>
|
||||
</p>
|
||||
<p data-prop="syncMoreNode"
|
||||
data-event="click:onGetMoreMessages"
|
||||
class="msg-messages-sync-more collapsed"
|
||||
role="button">
|
||||
<span data-l10n-id="messages-load-more"></span>
|
||||
</p>
|
||||
</div>
|
||||
<div data-prop="messageEmptyContainer"
|
||||
class="msg-list-empty-container collapsed">
|
||||
<p data-prop="messageEmptyText"
|
||||
class="msg-list-empty-message-text"
|
||||
data-l10n-id="messages-folder-empty"></p>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
<!-- Non-search header -->
|
||||
<section data-prop="normalHeader"
|
||||
class="msg-list-header msg-nonsearch-only"
|
||||
data-statuscolor="default"
|
||||
role="region">
|
||||
<header>
|
||||
<!-- Unlike a generic back button that navigates to a different screen,
|
||||
folder list header button triggers the folders and settings overlay. Thus
|
||||
the screen reader user requires more context as to what activating the
|
||||
button would do. -->
|
||||
<a href="#" class="msg-folder-list-btn" data-event="click:onShowFolders"
|
||||
aria-expanded="false" aria-controls="cards-folder-picker"
|
||||
role="button" data-l10n-id="message-list-menu">
|
||||
<span class="icon icon-menu"></span>
|
||||
</a>
|
||||
<menu data-prop="headerMenuNode" type="toolbar" class="anim-opacity">
|
||||
<a href="#" class="msg-compose-btn" data-event="click:onCompose"
|
||||
data-l10n-id="message-list-compose">
|
||||
<span class="icon icon-compose"></span>
|
||||
</a>
|
||||
</menu>
|
||||
<h1 data-prop="folderLabel"
|
||||
class="msg-list-header-folder-label header-label">
|
||||
<span data-prop="folderNameNode"
|
||||
dir="auto"
|
||||
class="msg-list-header-folder-name"></span>
|
||||
<span data-prop="folderUnread"
|
||||
class="msg-list-header-folder-unread collapsed"></span>
|
||||
</h1>
|
||||
</header>
|
||||
</section>
|
||||
|
||||
<!-- Multi-edit state header -->
|
||||
<lst-edit-header data-prop="editHeader"
|
||||
data-event="editHeaderClose"></lst-edit-header>
|
||||
<!-- Scroll region -->
|
||||
<div data-prop="scrollContainer" class="msg-list-scrollouter">
|
||||
<!-- exists so we can force a minimum height -->
|
||||
<div class="msg-list-scrollinner">
|
||||
<!-- The search textbox hides under the lip of the messages.
|
||||
As soon as any typing happens in it, we push the search
|
||||
controls card. -->
|
||||
<form role="search" data-prop="searchBar"
|
||||
class="msg-search-tease-bar msg-nonsearch-only">
|
||||
<p>
|
||||
<input data-event="focus:onSearchButton"
|
||||
data-prop="searchTextTease"
|
||||
class="msg-search-text-tease" type="text"
|
||||
dir="auto"
|
||||
data-l10n-id="message-search-input" />
|
||||
</p>
|
||||
</form>
|
||||
<lst-msg-vscroll data-prop="msgVScroll"
|
||||
data-event="messageClick:onClickMessage"
|
||||
data-empty-l10n-id="messages-folder-empty">
|
||||
</lst-msg-vscroll>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- New email notification bar -->
|
||||
<div class="message-list-topbar"></div>
|
||||
|
||||
<!-- Toolbar for non-multi-edit state -->
|
||||
<ul data-prop="normalToolbar" class="bb-tablist msg-list-action-toolbar"
|
||||
role="toolbar">
|
||||
<li role="presentation" class="msg-nonsearch-only">
|
||||
<button data-prop="refreshBtn" data-event="click:onRefresh"
|
||||
class="icon msg-refresh-btn" data-state="synchronized"
|
||||
data-l10n-id="messages-refresh-button">
|
||||
</button>
|
||||
</li>
|
||||
<li role="status" class="msg-nonsearch-only msg-last-sync">
|
||||
<span data-prop="lastSyncedLabel"
|
||||
class="msg-last-synced-label"
|
||||
data-l10n-id="folder-last-synced-label"></span>
|
||||
<span data-prop="lastSyncedAtNode"
|
||||
class="msg-last-synced-value"></span>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button data-prop="editBtn" data-event="click:setEditModeStart"
|
||||
class="icon msg-edit-btn" data-l10n-id="edit-button"></button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<lst-edit-toolbar data-prop="editToolbar"
|
||||
data-event="onDeleteMessages,onStarMessages,
|
||||
onMarkMessagesRead,onMoveMessages">
|
||||
</lst-edit-toolbar>
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<!-- Search header -->
|
||||
<section data-prop="searchHeader"
|
||||
class="msg-search-header"
|
||||
role="region">
|
||||
<form role="search" data-event="submit:onSearchSubmit">
|
||||
<button data-event="click:onCancelSearch"
|
||||
class="msg-search-cancel" type="submit"
|
||||
data-l10n-id="message-search-cancel"></button>
|
||||
<p>
|
||||
<input data-prop="searchInput" data-event="input:onSearchTextChange"
|
||||
type="text" required="required" class="msg-search-text"
|
||||
autocorrect="off"
|
||||
inputmode="verbatim"
|
||||
x-inputmode="verbatim"
|
||||
dir="auto"
|
||||
data-l10n-id="message-search-input" />
|
||||
<button type="reset"
|
||||
data-l10n-id="form-clear-input"
|
||||
data-event="click:onClearSearch"></button>
|
||||
</p>
|
||||
</form>
|
||||
<!-- Search filter switcher -->
|
||||
<header class="msg-search-controls-bar">
|
||||
<ul role="tablist" class="bb-tablist filter" data-type="filter">
|
||||
<li role="presentation" class="msg-search-from msg-search-filter"
|
||||
data-filter="author">
|
||||
<a data-l10n-id="message-search-from" role="tab"
|
||||
aria-selected="false"></a></li>
|
||||
<li role="presentation" class="msg-search-to msg-search-filter"
|
||||
data-filter="recipients">
|
||||
<a data-l10n-id="message-search-to" role="tab"
|
||||
aria-selected="false"></a></li>
|
||||
<li role="presentation" class="msg-search-subject msg-search-filter"
|
||||
data-filter="subject">
|
||||
<a data-l10n-id="message-search-subject" role="tab"
|
||||
aria-selected="false"></a>
|
||||
</li>
|
||||
<li role="presentation" class="msg-search-body msg-search-filter"
|
||||
data-filter="body">
|
||||
<a data-l10n-id="message-search-body" role="tab"
|
||||
aria-selected="false"></a></li>
|
||||
<li role="presentation" class="msg-search-body msg-search-filter"
|
||||
data-filter="all">
|
||||
<a data-l10n-id="message-search-all" role="tab"
|
||||
aria-selected="true"></a></li>
|
||||
</ul>
|
||||
</header>
|
||||
</section>
|
||||
|
||||
<!-- Multi-edit state header -->
|
||||
<lst-edit-header data-prop="editHeader"
|
||||
data-event="editHeaderClose"></lst-edit-header>
|
||||
|
||||
<!-- Scroll region -->
|
||||
<div data-prop="scrollContainer" class="msg-list-scrollouter">
|
||||
<!-- exists so we can force a minimum height -->
|
||||
<div class="msg-list-scrollinner">
|
||||
<lst-msg-vscroll data-prop="msgVScroll"
|
||||
data-event="messageClick:onClickMessage"
|
||||
data-empty-l10n-id="messages-search-empty">
|
||||
</lst-msg-vscroll>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- New email notification bar -->
|
||||
<div class="message-list-topbar"></div>
|
||||
|
||||
<!-- Toolbar for non-multi-edit state -->
|
||||
<ul data-prop="normalToolbar" class="bb-tablist msg-list-action-toolbar"
|
||||
role="toolbar">
|
||||
<li role="presentation">
|
||||
<button data-prop="editBtn" data-event="click:setEditModeStart"
|
||||
class="icon msg-edit-btn" data-l10n-id="edit-button"></button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<lst-edit-toolbar data-prop="editToolbar"
|
||||
data-event="onDeleteMessages,onStarMessages,
|
||||
onMarkMessagesRead,onMoveMessages">
|
||||
</lst-edit-toolbar>
|
||||
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'shared/js/accessibility_helper',
|
||||
'cards',
|
||||
'container_listen',
|
||||
'date',
|
||||
'header_cursor',
|
||||
'message_display',
|
||||
'message_list_topbar',
|
||||
'./lst/peep_dom',
|
||||
'./lst/default_vscroll_data',
|
||||
'./base_card',
|
||||
'template!./message_list_search.html',
|
||||
'./lst/edit_controller',
|
||||
'./lst/msg_click'
|
||||
], function (require, exports) {
|
||||
var accessibilityHelper = require('shared/js/accessibility_helper'), cards = require('cards'), containerListen = require('container_listen'), date = require('date'), HeaderCursor = require('header_cursor'), messageDisplay = require('message_display'), MessageListTopBar = require('message_list_topbar'), updatePeepDom = require('./lst/peep_dom').update;
|
||||
var defaultSearchVScrollData = {
|
||||
header: require('./lst/default_vscroll_data'),
|
||||
matches: []
|
||||
};
|
||||
var MATCHED_TEXT_CLASS = 'highlight';
|
||||
function appendMatchItemTo(matchItem, node) {
|
||||
var text = matchItem.text;
|
||||
var idx = 0;
|
||||
for (var iRun = 0; iRun <= matchItem.matchRuns.length; iRun++) {
|
||||
var run;
|
||||
if (iRun === matchItem.matchRuns.length) {
|
||||
run = {
|
||||
start: text.length,
|
||||
length: 0
|
||||
};
|
||||
} else {
|
||||
run = matchItem.matchRuns[iRun];
|
||||
}
|
||||
if (run.start > idx) {
|
||||
var tnode = document.createTextNode(text.substring(idx, run.start));
|
||||
node.appendChild(tnode);
|
||||
}
|
||||
if (!run.length) {
|
||||
continue;
|
||||
}
|
||||
var hspan = document.createElement('span');
|
||||
hspan.classList.add(MATCHED_TEXT_CLASS);
|
||||
hspan.textContent = text.substr(run.start, run.length);
|
||||
node.appendChild(hspan);
|
||||
idx = run.start + run.length;
|
||||
}
|
||||
}
|
||||
return [
|
||||
require('./base_card')(require('template!./message_list_search.html')),
|
||||
require('./lst/edit_controller'),
|
||||
require('./lst/msg_click'),
|
||||
{
|
||||
createdCallback: function () {
|
||||
containerListen(this.querySelector('.filter'), 'click', this.onSearchFilterClick.bind(this));
|
||||
this.searchFilterTabs = this.querySelectorAll('.filter [role="tab"]');
|
||||
this.isFirstTimeVisible = true;
|
||||
this._folderChanged = this._folderChanged.bind(this);
|
||||
this.msgVScroll.on('emptyLayoutShown', function () {
|
||||
this.editBtn.disabled = true;
|
||||
}.bind(this));
|
||||
this.msgVScroll.on('emptyLayoutHidden', function () {
|
||||
this.editBtn.disabled = false;
|
||||
}.bind(this));
|
||||
this.msgVScroll.on('messagesChange', function (message, index) {
|
||||
this.updateMatchedMessageDom(message);
|
||||
}.bind(this));
|
||||
this.msgVScroll.on('messagesComplete', function (newEmailCount) {
|
||||
if (!this.msgVScroll.vScroll.list) {
|
||||
this.msgVScroll.vScroll.setData(this.msgVScroll.listFunc);
|
||||
}
|
||||
}.bind(this));
|
||||
var vScrollBindData = function bindSearch(model, node) {
|
||||
model.element = node;
|
||||
node.message = model.header;
|
||||
this.updateMatchedMessageDom(model);
|
||||
}.bind(this);
|
||||
this.msgVScroll.init(this.scrollContainer, vScrollBindData, defaultSearchVScrollData);
|
||||
this._topBar = new MessageListTopBar(this.querySelector('.message-list-topbar'));
|
||||
this._topBar.bindToElements(this.scrollContainer, this.msgVScroll.vScroll);
|
||||
},
|
||||
onArgs: function (args) {
|
||||
var model = this.model = args.model;
|
||||
var headerCursor = this.headerCursor = args.headerCursor || new HeaderCursor(model);
|
||||
this.msgVScroll.setHeaderCursor(headerCursor);
|
||||
model.latest('folder', this._folderChanged);
|
||||
},
|
||||
onCardVisible: function () {
|
||||
if (this.isFirstTimeVisible) {
|
||||
this.searchInput.focus();
|
||||
}
|
||||
this.msgVScroll.vScroll.nowVisible();
|
||||
this.isFirstTimeVisible = false;
|
||||
},
|
||||
showSearch: function (phrase, filter) {
|
||||
console.log('sf: showSearch. phrase:', phrase, phrase.length);
|
||||
this.curFolder = this.model.folder;
|
||||
this.editModeEnabled = true;
|
||||
this.msgVScroll.vScroll.clearDisplay();
|
||||
this.curPhrase = phrase;
|
||||
this.curFilter = filter;
|
||||
this.msgVScroll._snippetRequestPending = false;
|
||||
this.msgVScroll.waitingOnChunk = true;
|
||||
this.headerCursor.startSearch(phrase, {
|
||||
author: filter === 'all' || filter === 'author',
|
||||
recipients: filter === 'all' || filter === 'recipients',
|
||||
subject: filter === 'all' || filter === 'subject',
|
||||
body: filter === 'all' || filter === 'body'
|
||||
});
|
||||
return true;
|
||||
},
|
||||
onSearchFilterClick: function (filterNode, event) {
|
||||
accessibilityHelper.setAriaSelected(filterNode.firstElementChild, this.searchFilterTabs);
|
||||
this.showSearch(this.searchInput.value, filterNode.dataset.filter);
|
||||
},
|
||||
onSearchTextChange: function (event) {
|
||||
console.log('sf: typed, now:', this.searchInput.value);
|
||||
this.showSearch(this.searchInput.value, this.curFilter);
|
||||
},
|
||||
onSearchSubmit: function (event) {
|
||||
event.preventDefault();
|
||||
this.searchInput.blur();
|
||||
},
|
||||
onCancelSearch: function (event) {
|
||||
if (event.explicitOriginalTarget !== event.target) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.headerCursor.endSearch();
|
||||
} catch (ex) {
|
||||
console.error('problem killing slice:', ex, '\n', ex.stack);
|
||||
}
|
||||
cards.removeCardAndSuccessors(this, 'animate');
|
||||
},
|
||||
onClearSearch: function () {
|
||||
this.showSearch('', this.curFilter);
|
||||
},
|
||||
editModeChanged: function (enabled) {
|
||||
if (enabled) {
|
||||
cards.setStatusColor(this.editHeader);
|
||||
} else {
|
||||
cards.setStatusColor(this);
|
||||
}
|
||||
},
|
||||
updateMatchedMessageDom: function (matchedHeader) {
|
||||
var msgNode = matchedHeader.element, matches = matchedHeader.matches, message = matchedHeader.header;
|
||||
if (!msgNode) {
|
||||
return;
|
||||
}
|
||||
var classAction = message.isPlaceholderData ? 'add' : 'remove';
|
||||
var defaultDataClass = this.msgVScroll.vScroll.itemDefaultDataClass;
|
||||
msgNode.classList[classAction](defaultDataClass);
|
||||
msgNode.dataset.id = matchedHeader.id;
|
||||
var dateNode = msgNode.querySelector('.msg-header-date');
|
||||
var subjectNode = msgNode.querySelector('.msg-header-subject');
|
||||
var authorNode = msgNode.querySelector('.msg-header-author');
|
||||
if (matches.author) {
|
||||
authorNode.textContent = '';
|
||||
appendMatchItemTo(matches.author, authorNode);
|
||||
} else {
|
||||
message.author.element = authorNode;
|
||||
message.author.onchange = updatePeepDom;
|
||||
message.author.onchange(message.author);
|
||||
}
|
||||
var dateTime = dateNode.dataset.time = message.date.valueOf();
|
||||
date.relativeDateElement(dateNode, dateTime);
|
||||
if (matches.subject) {
|
||||
subjectNode.textContent = '';
|
||||
appendMatchItemTo(matches.subject[0], subjectNode);
|
||||
} else {
|
||||
messageDisplay.subject(subjectNode, message);
|
||||
}
|
||||
var snippetNode = msgNode.querySelector('.msg-header-snippet');
|
||||
if (matches.body) {
|
||||
snippetNode.textContent = '';
|
||||
appendMatchItemTo(matches.body[0], snippetNode);
|
||||
} else {
|
||||
snippetNode.textContent = message.snippet;
|
||||
}
|
||||
var attachmentsNode = msgNode.querySelector('.msg-header-attachments');
|
||||
attachmentsNode.classList.toggle('msg-header-attachments-yes', message.hasAttachments);
|
||||
snippetNode.classList.toggle('icon-short', message.hasAttachments);
|
||||
msgNode.classList.toggle('unread', !message.isRead);
|
||||
var starNode = msgNode.querySelector('.msg-header-star');
|
||||
starNode.classList.toggle('msg-header-star-starred', message.isStarred);
|
||||
subjectNode.classList.toggle('icon-short', message.isStarred);
|
||||
this.updateDomSelectState(msgNode, message);
|
||||
},
|
||||
_folderChanged: function (folder) {
|
||||
if (!this.model.foldersSlice) {
|
||||
return;
|
||||
}
|
||||
this.curFolder = folder;
|
||||
this.showSearch('', 'all');
|
||||
},
|
||||
die: function () {
|
||||
this.msgVScroll.die();
|
||||
this.model.removeListener('folder', this._folderChanged);
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<section class="msg-reader-header" role="region" data-statuscolor="default">
|
||||
<header>
|
||||
<a data-event="click:onBack" href="#" class="msg-back-btn"
|
||||
role="button" data-l10n-id="back-button">
|
||||
<span class="icon icon-back"></span>
|
||||
</a>
|
||||
<menu type="toolbar">
|
||||
<button data-prop="previousBtn" class="msg-up-btn"
|
||||
data-l10n-id="message-reader-next-button">
|
||||
<span data-prop="previousIcon" class="icon icon-up"></span>
|
||||
</button>
|
||||
<button data-prop="nextBtn" class="msg-down-btn"
|
||||
data-l10n-id="message-reader-previous-button">
|
||||
<span data-prop="nextIcon" class="icon icon-down"></span>
|
||||
</button>
|
||||
</menu>
|
||||
<h1 dir="auto" class="msg-reader-header-label"></h1>
|
||||
</header>
|
||||
</section>
|
||||
<div data-prop="scrollContainer"
|
||||
class="scrollregion-below-header scrollregion-horizontal-too">
|
||||
<div data-prop="envelopeBar" class="msg-envelope-bar">
|
||||
<div class="msg-envelope-line msg-envelope-from-line"
|
||||
role="listbox" data-l10n-id="from-group">
|
||||
<span class="msg-envelope-key"
|
||||
data-l10n-id="envelope-from" aria-hidden="true"></span>
|
||||
</div>
|
||||
<!-- the details starts out collapsed, but can be toggled -->
|
||||
<div class="msg-envelope-details">
|
||||
<div class="msg-envelope-line msg-envelope-to-line"
|
||||
role="listbox" data-l10n-id="to-group">
|
||||
<span class="msg-envelope-key"
|
||||
data-l10n-id="envelope-to" aria-hidden="true"></span>
|
||||
</div>
|
||||
<div class="msg-envelope-line msg-envelope-cc-line"
|
||||
role="listbox" data-l10n-id="cc-group">
|
||||
<span class="msg-envelope-key"
|
||||
data-l10n-id="envelope-cc" aria-hidden="true"></span>
|
||||
</div>
|
||||
<div class="msg-envelope-line msg-envelope-bcc-line"
|
||||
role="listbox" data-l10n-id="bcc-group">
|
||||
<span class="msg-envelope-key"
|
||||
data-l10n-id="envelope-bcc" aria-hidden="true"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="msg-envelope-subject-container">
|
||||
<h3 aria-level="2" class="msg-envelope-subject"
|
||||
data-l10n-id="subject" dir="auto"></h3>
|
||||
<span role="heading" aria-level="3" class="msg-envelope-date"
|
||||
data-l10n-id="message-reader-date-received"></span>
|
||||
</div>
|
||||
</div>
|
||||
<ul data-prop="attachmentsContainer"
|
||||
class="msg-attachments-container collapsed"
|
||||
data-l10n-id="attachments-container">
|
||||
</ul>
|
||||
<!-- Tells us about remote/not downloaded images, asks to show -->
|
||||
<div data-prop="loadBar" class="msg-reader-load-infobar collapsed"
|
||||
role="button">
|
||||
<p data-prop="loadBarText" class="msg-reader-load-infobar-text"></p>
|
||||
<button class="msg-reader-load-infobar-button"
|
||||
aria-hidden="true"></button>
|
||||
</div>
|
||||
<div data-prop="rootBodyNode"
|
||||
class="msg-body-container"
|
||||
data-l10n-id="message-body-container">
|
||||
<progress data-l10n-id="message-body-container-progress"></progress>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="bb-tablist msg-reader-action-toolbar" role="toolbar">
|
||||
<li role="presentation">
|
||||
<button data-prop="deleteBtn" class="icon msg-delete-btn"
|
||||
data-l10n-id="message-delete-button"></button>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button data-prop="starBtn" class="icon msg-star-btn"
|
||||
data-l10n-id="message-star-button"></button>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button data-prop="readBtn" class="icon msg-mark-read-btn"
|
||||
data-l10n-id="message-mark-read-button"></button>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button data-prop="moveBtn" class="icon msg-move-btn"
|
||||
data-l10n-id="message-move-button"></button>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button data-prop="replyBtn" class="icon msg-reply-btn"
|
||||
data-l10n-id="message-reply-forward-button"></button>
|
||||
</li>
|
||||
</ul>
|
||||
876
assets/webapps/email.gaiamobile.org/js/cards/message_reader.js
Normal file
876
assets/webapps/email.gaiamobile.org/js/cards/message_reader.js
Normal file
|
|
@ -0,0 +1,876 @@
|
|||
define('tmpl!cards/msg/contact_menu.html', ['tmpl'], function (tmpl) {
|
||||
return tmpl.toDom('<form class="msg-contact-menu" role="dialog" data-type="action">\n\xA0 \xA0<header></header>\n\xA0 \xA0<menu>\n <button class="msg-contact-menu-new collapsed" data-l10n-id="message-contact-menu-new">\n </button>\n <button class="msg-contact-menu-reply collapsed" data-l10n-id="message-contact-menu-reply">\n </button>\n <button class="msg-contact-menu-view collapsed" data-l10n-id="message-contact-menu-view">\n </button>\n <button class="msg-contact-menu-create-contact collapsed" data-l10n-id="message-contact-menu-create">\n </button>\n <button class="msg-contact-menu-add-to-existing-contact collapsed" data-l10n-id="message-contact-menu-add-existing">\n </button>\n <span class="last-button-container">\n <button class="msg-contact-menu-cancel" data-l10n-id="message-multiedit-cancel">\n </button>\n </span>\n\xA0 \xA0</menu>\n\xA0</form>\n');
|
||||
});
|
||||
define('tmpl!cards/msg/reply_menu.html', ['tmpl'], function (tmpl) {
|
||||
return tmpl.toDom('<form class="msg-reply-menu" role="dialog" data-type="action">\n <header></header>\n <menu>\n <button class="msg-reply-menu-reply" data-l10n-id="message-reply-menu-reply">\n </button>\n <button class="msg-reply-menu-reply-all" data-l10n-id="message-reply-menu-reply-all">\n </button>\n <button class="msg-reply-menu-forward" data-l10n-id="message-reply-menu-forward">\n </button>\n <span class="last-button-container">\n <button class="msg-reply-menu-cancel" data-l10n-id="message-reply-menu-cancel">\n </button>\n </span>\n </menu>\n</form>\n');
|
||||
});
|
||||
define('tmpl!cards/msg/browse_confirm.html', ['tmpl'], function (tmpl) {
|
||||
return tmpl.toDom('<form role="dialog" class="msg-browse-confirm" data-type="confirm">\n <section>\n <h1 data-l10n-id="confirm-dialog-title"></h1>\n <p></p>\n </section>\n <menu>\n <button id="msg-browse-cancel" data-l10n-id="message-multiedit-cancel"></button>\n <button id="msg-browse-ok" class="recommend" data-l10n-id="dialog-button-ok"></button>\n </menu>\n</form>');
|
||||
});
|
||||
define('tmpl!cards/msg/peep_bubble.html', ['tmpl'], function (tmpl) {
|
||||
return tmpl.toDom('<div class="msg-peep-bubble peep-bubble" dir="auto" role="option">\n <span class="msg-peep-content"></span>\n</div>\n');
|
||||
});
|
||||
define('tmpl!cards/msg/attachment_item.html', ['tmpl'], function (tmpl) {
|
||||
return tmpl.toDom('<li class="msg-attachment-item">\n <span class="msg-attachment-icon" role="presentation"></span>\n <span class="msg-attachment-fileinfo">\n <span dir="auto" class="msg-attachment-filename"></span>\n <span class="msg-attachment-filesize"></span>\n <span data-l10n-id="message-attachment-too-large"\n class="msg-attachment-too-large"></span>\n </span>\n <button class="msg-attachment-download" data-l10n-id="download-button">\n <span class="icon icon-download"></span></button>\n <span class="msg-attachment-downloading">\n <progress class="small" data-l10n-id="downloading-progress"></progress>\n </span>\n <button class="msg-attachment-view" data-l10n-id="view-button">\n <span data-l10n-id="message-attachment-view" class="icon icon-view"></span>\n </button>\n</li>\n');
|
||||
});
|
||||
define('tmpl!cards/msg/attachment_disabled_confirm.html', ['tmpl'], function (tmpl) {
|
||||
return tmpl.toDom('<form role="dialog" class="msg-attachment-disabled-confirm" data-type="confirm">\n <section>\n <p data-l10n-id="message-send-attachment-disabled-confirm"></p>\n </section>\n <menu>\n <button id="msg-attachment-disabled-cancel" data-l10n-id="message-multiedit-cancel"></button>\n <button id="msg-attachment-disabled-ok" data-l10n-id="dialog-button-ok"></button>\n </menu>\n</form>\n');
|
||||
});
|
||||
define('tmpl!cards/msg/attachment_did_not_open_alert.html', ['tmpl'], function (tmpl) {
|
||||
return tmpl.toDom('<form role="dialog" class="msg-attachment-did-not-open" data-type="confirm">\n <section>\n <h1 data-l10n-id="message-attachment-did-not-open-label"></h1>\n <p><span data-l10n-id="message-attachment-did-not-open-body"></span></p>\n </section>\n <menu>\n <button id="msg-attachment-did-not-open-ok" data-l10n-id="dialog-button-ok"></button>\n </menu>\n</form>\n');
|
||||
});
|
||||
define('marquee', [], function () {
|
||||
var Marquee = {
|
||||
timingFunction: [
|
||||
'linear',
|
||||
'ease'
|
||||
],
|
||||
setup: function marquee_setup(text, headerNode) {
|
||||
this._headerNode = headerNode;
|
||||
this._headerWrapper = document.getElementById('marquee-h-wrapper');
|
||||
if (!this._headerWrapper) {
|
||||
this._headerWrapper = document.createElement('div');
|
||||
this._headerWrapper.id = 'marquee-h-wrapper';
|
||||
this._headerNode.appendChild(this._headerWrapper);
|
||||
}
|
||||
var headerText = document.getElementById('marquee-h-text');
|
||||
if (!headerText) {
|
||||
headerText = document.createElement('div');
|
||||
headerText.id = 'marquee-h-text';
|
||||
this._headerWrapper.dir = 'auto';
|
||||
this._headerWrapper.appendChild(headerText);
|
||||
}
|
||||
headerText.textContent = text;
|
||||
},
|
||||
activate: function marquee_activate(behavior, timingFun) {
|
||||
if (!this._headerNode || !this._headerWrapper) {
|
||||
return;
|
||||
}
|
||||
var mode = behavior || 'scroll';
|
||||
var tf = timingFun || null;
|
||||
var timing = Marquee.timingFunction.indexOf(tf) >= 0 ? tf : 'linear';
|
||||
var marqueeCssClass = 'marquee';
|
||||
var titleText = document.getElementById('marquee-h-text');
|
||||
var cssClass, width;
|
||||
if (this._headerWrapper.clientWidth < this._headerWrapper.scrollWidth) {
|
||||
this._marqueeCssClassList = [];
|
||||
switch (mode) {
|
||||
case 'scroll':
|
||||
cssClass = marqueeCssClass + '-rtl';
|
||||
width = this._headerWrapper.scrollWidth;
|
||||
titleText.style.width = width + 'px';
|
||||
titleText.classList.add(cssClass + '-start-' + timing);
|
||||
this._marqueeCssClassList.push(cssClass + '-start-' + timing);
|
||||
var self = this;
|
||||
titleText.addEventListener('animationend', function () {
|
||||
titleText.classList.remove(cssClass + '-start-' + timing);
|
||||
this._marqueeCssClassList.pop();
|
||||
var visibleWidth = self._headerWrapper.clientWidth + 'px';
|
||||
titleText.style.transform = 'translateX(' + visibleWidth + ')';
|
||||
titleText.classList.add(cssClass);
|
||||
this._marqueeCssClassList.push(cssClass);
|
||||
});
|
||||
break;
|
||||
case 'alternate':
|
||||
var dirSuffix = '';
|
||||
if (window.getComputedStyle(titleText).direction === 'rtl') {
|
||||
dirSuffix = '-rtl';
|
||||
}
|
||||
timing += dirSuffix;
|
||||
cssClass = marqueeCssClass + '-alt-';
|
||||
width = this._headerWrapper.scrollWidth - this._headerWrapper.clientWidth;
|
||||
titleText.style.width = width + 'px';
|
||||
titleText.classList.add(cssClass + timing);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!this._marqueeCssClassList) {
|
||||
return;
|
||||
}
|
||||
for (var titleCssClass in this._marqueeCssClassList) {
|
||||
titleText.classList.remove(titleCssClass);
|
||||
}
|
||||
titleText.style.transform = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
return Marquee;
|
||||
});
|
||||
define('mime_to_class', [], function () {
|
||||
return function mimeToClass(mimeType) {
|
||||
mimeType = mimeType || '';
|
||||
return 'mime-' + (mimeType.split('/')[0] || '');
|
||||
};
|
||||
});
|
||||
define('file_display', [
|
||||
'require',
|
||||
'l10n!'
|
||||
], function (require) {
|
||||
var mozL10n = require('l10n!');
|
||||
return {
|
||||
fileSize: function (node, sizeInBytes) {
|
||||
var kilos = Math.ceil(sizeInBytes / 1024);
|
||||
mozL10n.setAttributes(node, 'attachment-size-kib', { kilobytes: kilos });
|
||||
}
|
||||
};
|
||||
});
|
||||
define('template!cards/message_reader.html', ['template'], function (template) {
|
||||
return {
|
||||
createdCallback: template.templateCreatedCallback,
|
||||
template: template.objToFn({
|
||||
'id': 'cards/message_reader.html',
|
||||
'deps': [],
|
||||
'text': '<section class="msg-reader-header" role="region" data-statuscolor="default">\n <header>\n <a data-event="click:onBack" href="#" class="msg-back-btn"\n role="button" data-l10n-id="back-button">\n <span class="icon icon-back"></span>\n </a>\n <menu type="toolbar">\n <button data-prop="previousBtn" class="msg-up-btn"\n data-l10n-id="message-reader-next-button">\n <span data-prop="previousIcon" class="icon icon-up"></span>\n </button>\n <button data-prop="nextBtn" class="msg-down-btn"\n data-l10n-id="message-reader-previous-button">\n <span data-prop="nextIcon" class="icon icon-down"></span>\n </button>\n </menu>\n <h1 dir="auto" class="msg-reader-header-label"></h1>\n </header>\n</section>\n<div data-prop="scrollContainer"\n class="scrollregion-below-header scrollregion-horizontal-too">\n <div data-prop="envelopeBar" class="msg-envelope-bar">\n <div class="msg-envelope-line msg-envelope-from-line"\n role="listbox" data-l10n-id="from-group">\n <span class="msg-envelope-key"\n data-l10n-id="envelope-from" aria-hidden="true"></span>\n </div>\n <!-- the details starts out collapsed, but can be toggled -->\n <div class="msg-envelope-details">\n <div class="msg-envelope-line msg-envelope-to-line"\n role="listbox" data-l10n-id="to-group">\n <span class="msg-envelope-key"\n data-l10n-id="envelope-to" aria-hidden="true"></span>\n </div>\n <div class="msg-envelope-line msg-envelope-cc-line"\n role="listbox" data-l10n-id="cc-group">\n <span class="msg-envelope-key"\n data-l10n-id="envelope-cc" aria-hidden="true"></span>\n </div>\n <div class="msg-envelope-line msg-envelope-bcc-line"\n role="listbox" data-l10n-id="bcc-group">\n <span class="msg-envelope-key"\n data-l10n-id="envelope-bcc" aria-hidden="true"></span>\n </div>\n </div>\n <div class="msg-envelope-subject-container">\n <h3 aria-level="2" class="msg-envelope-subject"\n data-l10n-id="subject" dir="auto"></h3>\n <span role="heading" aria-level="3" class="msg-envelope-date"\n data-l10n-id="message-reader-date-received"></span>\n </div>\n </div>\n <ul data-prop="attachmentsContainer"\n class="msg-attachments-container collapsed"\n data-l10n-id="attachments-container">\n </ul>\n <!-- Tells us about remote/not downloaded images, asks to show -->\n <div data-prop="loadBar" class="msg-reader-load-infobar collapsed"\n role="button">\n <p data-prop="loadBarText" class="msg-reader-load-infobar-text"></p>\n <button class="msg-reader-load-infobar-button"\n aria-hidden="true"></button>\n </div>\n <div data-prop="rootBodyNode"\n class="msg-body-container"\n data-l10n-id="message-body-container">\n <progress data-l10n-id="message-body-container-progress"></progress>\n </div>\n</div>\n\n<ul class="bb-tablist msg-reader-action-toolbar" role="toolbar">\n <li role="presentation">\n <button data-prop="deleteBtn" class="icon msg-delete-btn"\n data-l10n-id="message-delete-button"></button>\n </li>\n <li role="presentation">\n <button data-prop="starBtn" class="icon msg-star-btn"\n data-l10n-id="message-star-button"></button>\n </li>\n <li role="presentation">\n <button data-prop="readBtn" class="icon msg-mark-read-btn"\n data-l10n-id="message-mark-read-button"></button>\n </li>\n <li role="presentation">\n <button data-prop="moveBtn" class="icon msg-move-btn"\n data-l10n-id="message-move-button"></button>\n </li>\n <li role="presentation">\n <button data-prop="replyBtn" class="icon msg-reply-btn"\n data-l10n-id="message-reply-forward-button"></button>\n </li>\n</ul>\n'
|
||||
})
|
||||
};
|
||||
});
|
||||
define('cards/message_reader', [
|
||||
'require',
|
||||
'tmpl!./msg/delete_confirm.html',
|
||||
'tmpl!./msg/contact_menu.html',
|
||||
'tmpl!./msg/reply_menu.html',
|
||||
'tmpl!./msg/browse_confirm.html',
|
||||
'tmpl!./msg/peep_bubble.html',
|
||||
'tmpl!./msg/attachment_item.html',
|
||||
'tmpl!./msg/attachment_disabled_confirm.html',
|
||||
'tmpl!./msg/attachment_did_not_open_alert.html',
|
||||
'cards',
|
||||
'confirm_dialog',
|
||||
'date',
|
||||
'toaster',
|
||||
'header_cursor',
|
||||
'evt',
|
||||
'iframe_shims',
|
||||
'marquee',
|
||||
'l10n!',
|
||||
'query_uri',
|
||||
'mime_to_class',
|
||||
'file_display',
|
||||
'message_display',
|
||||
'./base_card',
|
||||
'template!./message_reader.html'
|
||||
], function (require) {
|
||||
var MimeMapper, msgDeleteConfirmNode = require('tmpl!./msg/delete_confirm.html'), msgContactMenuNode = require('tmpl!./msg/contact_menu.html'), msgReplyMenuNode = require('tmpl!./msg/reply_menu.html'), msgBrowseConfirmNode = require('tmpl!./msg/browse_confirm.html'), msgPeepBubbleNode = require('tmpl!./msg/peep_bubble.html'), msgAttachmentItemNode = require('tmpl!./msg/attachment_item.html'), msgAttachmentDisabledConfirmNode = require('tmpl!./msg/attachment_disabled_confirm.html'), msgAttachmentDidNotOpenAlertNode = require('tmpl!./msg/attachment_did_not_open_alert.html'), cards = require('cards'), ConfirmDialog = require('confirm_dialog'), date = require('date'), toaster = require('toaster'), HeaderCursor = require('header_cursor'), evt = require('evt'), iframeShims = require('iframe_shims'), Marquee = require('marquee'), mozL10n = require('l10n!'), queryURI = require('query_uri'), mimeToClass = require('mime_to_class'), fileDisplay = require('file_display'), messageDisplay = require('message_display');
|
||||
var CONTENT_TYPES_TO_CLASS_NAMES = [
|
||||
null,
|
||||
'msg-body-content',
|
||||
'msg-body-signature',
|
||||
'msg-body-leadin',
|
||||
null,
|
||||
'msg-body-disclaimer',
|
||||
'msg-body-list',
|
||||
'msg-body-product',
|
||||
'msg-body-ads'
|
||||
];
|
||||
var CONTENT_QUOTE_CLASS_NAMES = [
|
||||
'msg-body-q1',
|
||||
'msg-body-q2',
|
||||
'msg-body-q3',
|
||||
'msg-body-q4',
|
||||
'msg-body-q5',
|
||||
'msg-body-q6',
|
||||
'msg-body-q7',
|
||||
'msg-body-q8',
|
||||
'msg-body-q9'
|
||||
];
|
||||
var MAX_QUOTE_CLASS_NAME = 'msg-body-qmax';
|
||||
var OCTET_STREAM_TYPE = 'application/octet-stream';
|
||||
function sendActivity(obj) {
|
||||
return new MozActivity(obj);
|
||||
}
|
||||
return [
|
||||
require('./base_card')(require('template!./message_reader.html')),
|
||||
{
|
||||
createdCallback: function () {
|
||||
this.htmlBodyNodes = [];
|
||||
this._on('msg-up-btn', 'click', 'onPrevious');
|
||||
this._on('msg-down-btn', 'click', 'onNext');
|
||||
this._on('msg-reply-btn', 'click', 'onReplyMenu');
|
||||
this._on('msg-delete-btn', 'click', 'onDelete');
|
||||
this._on('msg-star-btn', 'click', 'onToggleStar');
|
||||
this._on('msg-move-btn', 'click', 'onMove');
|
||||
this._on('msg-mark-read-btn', 'click', 'onMarkRead');
|
||||
this._on('msg-envelope-bar', 'click', 'onEnvelopeClick');
|
||||
this._on('msg-reader-load-infobar', 'click', 'onLoadBarClick');
|
||||
this._emittedContentEvents = false;
|
||||
this.disableReply();
|
||||
this._builtBodyDom = false;
|
||||
this.handleBodyChange = this.handleBodyChange.bind(this);
|
||||
this.onMessageSuidNotFound = this.onMessageSuidNotFound.bind(this);
|
||||
this.onCurrentMessage = this.onCurrentMessage.bind(this);
|
||||
},
|
||||
onArgs: function (args) {
|
||||
this.model = args.model;
|
||||
this.messageSuid = args.messageSuid;
|
||||
var headerCursor = this.headerCursor = args.headerCursor || new HeaderCursor(this.model);
|
||||
headerCursor.on('messageSuidNotFound', this.onMessageSuidNotFound);
|
||||
headerCursor.latest('currentMessage', this.onCurrentMessage);
|
||||
headerCursor.setCurrentMessage(this.header);
|
||||
},
|
||||
_contextMenuType: {
|
||||
VIEW_CONTACT: 1,
|
||||
CREATE_CONTACT: 2,
|
||||
ADD_TO_CONTACT: 4,
|
||||
REPLY: 8,
|
||||
NEW_MESSAGE: 16
|
||||
},
|
||||
skipEmitContentEvents: true,
|
||||
_on: function (className, eventName, method, skipProtection) {
|
||||
this.getElementsByClassName(className)[0].addEventListener(eventName, function (evt) {
|
||||
if (this.header || skipProtection) {
|
||||
return this[method](evt);
|
||||
}
|
||||
}.bind(this), false);
|
||||
},
|
||||
_setHeader: function (header) {
|
||||
this.header = header.makeCopy();
|
||||
this.hackMutationHeader = header;
|
||||
if (!this.header.isRead) {
|
||||
this.header.setRead(true);
|
||||
} else {
|
||||
this.readBtn.classList.remove('unread');
|
||||
mozL10n.setAttributes(this.readBtn, 'message-mark-read-button');
|
||||
}
|
||||
this.starBtn.classList.toggle('msg-star-btn-on', this.hackMutationHeader.isStarred);
|
||||
this.starBtn.setAttribute('aria-pressed', this.hackMutationHeader.isStarred);
|
||||
this.emit('header');
|
||||
},
|
||||
postInsert: function () {
|
||||
this._inDom = true;
|
||||
if (this._afterInDomMessage) {
|
||||
this.onCurrentMessage(this._afterInDomMessage);
|
||||
this._afterInDomMessage = null;
|
||||
}
|
||||
},
|
||||
told: function (args) {
|
||||
if (args.messageSuid) {
|
||||
this.messageSuid = args.messageSuid;
|
||||
}
|
||||
},
|
||||
handleBodyChange: function (evt) {
|
||||
this.buildBodyDom(evt.changeDetails);
|
||||
},
|
||||
onBack: function (event) {
|
||||
cards.removeCardAndSuccessors(this, 'animate');
|
||||
},
|
||||
onPrevious: function (event) {
|
||||
this.headerCursor.advance('previous');
|
||||
},
|
||||
onNext: function (event) {
|
||||
this.headerCursor.advance('next');
|
||||
},
|
||||
onMessageSuidNotFound: function (messageSuid) {
|
||||
if (this.messageSuid === messageSuid) {
|
||||
this.onBack();
|
||||
}
|
||||
},
|
||||
onCurrentMessage: function (currentMessage) {
|
||||
if (!this._inDom) {
|
||||
this._afterInDomMessage = currentMessage;
|
||||
return;
|
||||
}
|
||||
if (this.header && this.header.id === currentMessage.header.id) {
|
||||
return;
|
||||
}
|
||||
this.messageSuid = null;
|
||||
this._setHeader(currentMessage.header);
|
||||
this.clearDom();
|
||||
this.latestOnce('header', function () {
|
||||
this.buildHeaderDom(this);
|
||||
this.header.getBody({ downloadBodyReps: true }, function (body) {
|
||||
if (this.header.id !== body.id) {
|
||||
return;
|
||||
}
|
||||
this.body = body;
|
||||
body.onchange = this.handleBodyChange;
|
||||
if (body.bodyRepsDownloaded) {
|
||||
this.buildBodyDom();
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
var hasPrevious = currentMessage.siblings.hasPrevious;
|
||||
this.previousBtn.disabled = !hasPrevious;
|
||||
this.previousIcon.classList[hasPrevious ? 'remove' : 'add']('icon-disabled');
|
||||
var hasNext = currentMessage.siblings.hasNext;
|
||||
this.nextBtn.disabled = !hasNext;
|
||||
this.nextIcon.classList[hasNext ? 'remove' : 'add']('icon-disabled');
|
||||
},
|
||||
reply: function () {
|
||||
cards.eatEventsUntilNextCard();
|
||||
var composer = this.header.replyToMessage(null, () => {
|
||||
cards.pushCard('compose', 'animate', {
|
||||
model: this.model,
|
||||
composer: composer
|
||||
});
|
||||
});
|
||||
},
|
||||
replyAll: function () {
|
||||
cards.eatEventsUntilNextCard();
|
||||
var composer = this.header.replyToMessage('all', () => {
|
||||
cards.pushCard('compose', 'animate', {
|
||||
model: this.model,
|
||||
composer: composer
|
||||
});
|
||||
});
|
||||
},
|
||||
forward: function () {
|
||||
var needToPrompt = this.header.hasAttachments || this.body.embeddedImageCount > 0;
|
||||
var forwardMessage = function () {
|
||||
cards.eatEventsUntilNextCard();
|
||||
var composer = this.header.forwardMessage('inline', () => {
|
||||
cards.pushCard('compose', 'animate', {
|
||||
model: this.model,
|
||||
composer: composer
|
||||
});
|
||||
});
|
||||
}.bind(this);
|
||||
if (needToPrompt) {
|
||||
var dialog = msgAttachmentDisabledConfirmNode.cloneNode(true);
|
||||
ConfirmDialog.show(dialog, {
|
||||
id: 'msg-attachment-disabled-ok',
|
||||
handler: function () {
|
||||
forwardMessage();
|
||||
}
|
||||
}, {
|
||||
id: 'msg-attachment-disabled-cancel',
|
||||
handler: null
|
||||
});
|
||||
} else {
|
||||
forwardMessage();
|
||||
}
|
||||
},
|
||||
canReplyAll: function () {
|
||||
var myAddresses = this.model.account.identities.map(function (ident) {
|
||||
return ident.address;
|
||||
});
|
||||
var otherAddresses = (this.header.to || []).concat(this.header.cc || []);
|
||||
if (this.header.replyTo && this.header.replyTo.author) {
|
||||
otherAddresses.push(this.header.replyTo.author);
|
||||
}
|
||||
for (var i = 0; i < otherAddresses.length; i++) {
|
||||
var otherAddress = otherAddresses[i];
|
||||
if (otherAddress.address && myAddresses.indexOf(otherAddress.address) == -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onReplyMenu: function (event) {
|
||||
var contents = msgReplyMenuNode.cloneNode(true);
|
||||
cards.setStatusColor(contents);
|
||||
document.body.appendChild(contents);
|
||||
var formSubmit = function (evt) {
|
||||
cards.setStatusColor();
|
||||
document.body.removeChild(contents);
|
||||
switch (evt.explicitOriginalTarget.className) {
|
||||
case 'msg-reply-menu-reply':
|
||||
this.reply();
|
||||
break;
|
||||
case 'msg-reply-menu-reply-all':
|
||||
this.replyAll();
|
||||
break;
|
||||
case 'msg-reply-menu-forward':
|
||||
this.forward();
|
||||
break;
|
||||
case 'msg-reply-menu-cancel':
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}.bind(this);
|
||||
contents.addEventListener('submit', formSubmit);
|
||||
if (!this.canReplyAll()) {
|
||||
contents.querySelector('.msg-reply-menu-reply-all').classList.add('collapsed');
|
||||
}
|
||||
},
|
||||
onDelete: function () {
|
||||
var dialog = msgDeleteConfirmNode.cloneNode(true);
|
||||
var content = dialog.getElementsByTagName('p')[0];
|
||||
mozL10n.setAttributes(content, 'message-edit-delete-confirm');
|
||||
ConfirmDialog.show(dialog, {
|
||||
id: 'msg-delete-ok',
|
||||
handler: function () {
|
||||
var op = this.header.deleteMessage();
|
||||
cards.removeCardAndSuccessors(this, 'animate');
|
||||
toaster.toastOperation(op);
|
||||
}.bind(this)
|
||||
}, {
|
||||
id: 'msg-delete-cancel',
|
||||
handler: null
|
||||
});
|
||||
},
|
||||
onToggleStar: function () {
|
||||
this.starBtn.classList.toggle('msg-star-btn-on', !this.hackMutationHeader.isStarred);
|
||||
this.hackMutationHeader.isStarred = !this.hackMutationHeader.isStarred;
|
||||
this.starBtn.setAttribute('aria-pressed', this.hackMutationHeader.isStarred);
|
||||
this.header.setStarred(this.hackMutationHeader.isStarred);
|
||||
},
|
||||
onMove: function () {
|
||||
cards.folderSelector(this.model, function (folder) {
|
||||
var op = this.header.moveMessage(folder);
|
||||
cards.removeCardAndSuccessors(this, 'animate');
|
||||
toaster.toastOperation(op);
|
||||
}.bind(this), function (folder) {
|
||||
return folder.isValidMoveTarget;
|
||||
});
|
||||
},
|
||||
setRead: function (isRead) {
|
||||
this.hackMutationHeader.isRead = isRead;
|
||||
this.header.setRead(isRead);
|
||||
this.readBtn.classList.toggle('unread', !isRead);
|
||||
mozL10n.setAttributes(this.readBtn, isRead ? 'message-mark-read-button' : 'message-mark-unread-button');
|
||||
},
|
||||
onMarkRead: function () {
|
||||
this.setRead(!this.hackMutationHeader.isRead);
|
||||
},
|
||||
onEnvelopeClick: function (event) {
|
||||
var target = event.target;
|
||||
if (!target.classList.contains('msg-peep-bubble')) {
|
||||
return;
|
||||
}
|
||||
this.onPeepClick(target);
|
||||
},
|
||||
onPeepClick: function (target) {
|
||||
var contents = msgContactMenuNode.cloneNode(true);
|
||||
var peep = target.peep;
|
||||
var headerNode = contents.getElementsByTagName('header')[0];
|
||||
Marquee.setup(peep.address, headerNode);
|
||||
document.body.appendChild(contents);
|
||||
Marquee.activate('alternate', 'ease');
|
||||
var formSubmit = function (evt) {
|
||||
document.body.removeChild(contents);
|
||||
switch (evt.explicitOriginalTarget.className) {
|
||||
case 'msg-contact-menu-new':
|
||||
cards.pushCard('compose', 'animate', {
|
||||
model: this.model,
|
||||
composerData: {
|
||||
message: this.header,
|
||||
onComposer: function (composer) {
|
||||
composer.to = [{
|
||||
address: peep.address,
|
||||
name: peep.name
|
||||
}];
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'msg-contact-menu-view':
|
||||
sendActivity({
|
||||
name: 'open',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: { 'id': peep.contactId }
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'msg-contact-menu-create-contact':
|
||||
var params = { 'email': peep.address };
|
||||
if (peep.name) {
|
||||
params.givenName = peep.name;
|
||||
}
|
||||
sendActivity({
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: params
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'msg-contact-menu-add-to-existing-contact':
|
||||
sendActivity({
|
||||
name: 'update',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: { 'email': peep.address }
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'msg-contact-menu-reply':
|
||||
var composer = this.header.replyToMessage(null, () => {
|
||||
cards.pushCard('compose', 'animate', {
|
||||
model: this.model,
|
||||
composer: composer
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}.bind(this);
|
||||
contents.addEventListener('submit', formSubmit);
|
||||
var contextMenuOptions = this._contextMenuType.NEW_MESSAGE;
|
||||
var messageType = peep.type;
|
||||
if (messageType === 'from') {
|
||||
contextMenuOptions |= this._contextMenuType.REPLY;
|
||||
}
|
||||
if (peep.isContact) {
|
||||
contextMenuOptions |= this._contextMenuType.VIEW_CONTACT;
|
||||
} else {
|
||||
contextMenuOptions |= this._contextMenuType.CREATE_CONTACT;
|
||||
contextMenuOptions |= this._contextMenuType.ADD_TO_CONTACT;
|
||||
}
|
||||
if (contextMenuOptions & this._contextMenuType.VIEW_CONTACT) {
|
||||
contents.querySelector('.msg-contact-menu-view').classList.remove('collapsed');
|
||||
}
|
||||
if (contextMenuOptions & this._contextMenuType.CREATE_CONTACT) {
|
||||
contents.querySelector('.msg-contact-menu-create-contact').classList.remove('collapsed');
|
||||
}
|
||||
if (contextMenuOptions & this._contextMenuType.ADD_TO_CONTACT) {
|
||||
contents.querySelector('.msg-contact-menu-add-to-existing-contact').classList.remove('collapsed');
|
||||
}
|
||||
if (contextMenuOptions & this._contextMenuType.REPLY) {
|
||||
contents.querySelector('.msg-contact-menu-reply').classList.remove('collapsed');
|
||||
}
|
||||
if (contextMenuOptions & this._contextMenuType.NEW_MESSAGE) {
|
||||
contents.querySelector('.msg-contact-menu-new').classList.remove('collapsed');
|
||||
}
|
||||
},
|
||||
onLoadBarClick: function (event) {
|
||||
var self = this;
|
||||
var loadBar = this.loadBar;
|
||||
if (!this.body.embeddedImagesDownloaded) {
|
||||
this.body.downloadEmbeddedImages(function () {
|
||||
if (!self.body) {
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < self.htmlBodyNodes.length; i++) {
|
||||
self.body.showEmbeddedImages(self.htmlBodyNodes[i], self.iframeResizeHandler);
|
||||
}
|
||||
});
|
||||
loadBar.classList.add('collapsed');
|
||||
} else {
|
||||
for (var i = 0; i < this.htmlBodyNodes.length; i++) {
|
||||
this.body.showExternalImages(this.htmlBodyNodes[i], this.iframeResizeHandler);
|
||||
}
|
||||
loadBar.classList.add('collapsed');
|
||||
}
|
||||
},
|
||||
getAttachmentBlob: function (attachment, callback) {
|
||||
try {
|
||||
var storageType = attachment._file[0];
|
||||
var filename = attachment._file[1];
|
||||
var storage = navigator.getDeviceStorage(storageType);
|
||||
var getreq = storage.get(filename);
|
||||
getreq.onerror = function () {
|
||||
console.warn('Could not open attachment file: ', filename, getreq.error.name);
|
||||
};
|
||||
getreq.onsuccess = function () {
|
||||
var blob = getreq.result;
|
||||
callback(blob);
|
||||
};
|
||||
} catch (ex) {
|
||||
console.warn('Exception getting attachment from device storage:', attachment._file, '\n', ex, '\n', ex.stack);
|
||||
}
|
||||
},
|
||||
onDownloadAttachmentClick: function (node, attachment) {
|
||||
node.setAttribute('state', 'downloading');
|
||||
var registerWithDownloadManager = true;
|
||||
attachment.download(function downloaded() {
|
||||
if (!attachment._file) {
|
||||
return;
|
||||
}
|
||||
node.setAttribute('state', 'downloaded');
|
||||
}, null, registerWithDownloadManager);
|
||||
},
|
||||
onViewAttachmentClick: function (node, attachment) {
|
||||
console.log('trying to open', attachment._file, 'known type:', attachment.mimetype);
|
||||
if (!attachment._file) {
|
||||
return;
|
||||
}
|
||||
if (attachment.isDownloaded) {
|
||||
this.getAttachmentBlob(attachment, function (blob) {
|
||||
try {
|
||||
if (!blob) {
|
||||
throw new Error('Blob does not exist');
|
||||
}
|
||||
var useType = attachment.mimetype;
|
||||
if (!useType || useType === OCTET_STREAM_TYPE) {
|
||||
useType = blob.type;
|
||||
}
|
||||
if (!useType || useType === OCTET_STREAM_TYPE) {
|
||||
useType = MimeMapper.guessTypeFromFileProperties(attachment.filename, OCTET_STREAM_TYPE);
|
||||
}
|
||||
if (!useType) {
|
||||
useType = OCTET_STREAM_TYPE;
|
||||
}
|
||||
console.log('triggering open activity with MIME type:', useType);
|
||||
var activity = new MozActivity({
|
||||
name: 'open',
|
||||
data: {
|
||||
type: useType,
|
||||
filename: attachment.filename,
|
||||
blob: blob,
|
||||
url: attachment.filename
|
||||
}
|
||||
});
|
||||
activity.onerror = function () {
|
||||
console.warn('Problem with "open" activity', activity.error.name);
|
||||
if (activity.error.name === 'NO_PROVIDER') {
|
||||
var dialog = msgAttachmentDidNotOpenAlertNode.cloneNode(true);
|
||||
ConfirmDialog.show(dialog, {
|
||||
id: 'msg-attachment-did-not-open-ok',
|
||||
handler: null
|
||||
}, { handler: null });
|
||||
}
|
||||
};
|
||||
activity.onsuccess = function () {
|
||||
console.log('"open" activity allegedly succeeded');
|
||||
};
|
||||
} catch (ex) {
|
||||
console.warn('Problem creating "open" activity:', ex, '\n', ex.stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onHyperlinkClick: function (event, linkNode, linkUrl, linkText) {
|
||||
if (/^mailto:/i.test(linkUrl)) {
|
||||
var data = queryURI(linkUrl);
|
||||
cards.pushCard('compose', 'animate', {
|
||||
model: this.model,
|
||||
composerData: {
|
||||
onComposer: (composer, composeCard) => {
|
||||
Object.keys(data).forEach(key => {
|
||||
if (key === 'to' || key === 'cc' || key === 'bcc') {
|
||||
composer[key] = data[key].map(addr => {
|
||||
return this.model.api.parseMailbox(addr).address;
|
||||
}).filter(value => {
|
||||
return value;
|
||||
});
|
||||
} else {
|
||||
composer[key] = data[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var dialog = msgBrowseConfirmNode.cloneNode(true);
|
||||
var content = dialog.getElementsByTagName('p')[0];
|
||||
mozL10n.setAttributes(content, 'browse-to-url-prompt', { url: linkUrl });
|
||||
ConfirmDialog.show(dialog, {
|
||||
id: 'msg-browse-ok',
|
||||
handler: function () {
|
||||
sendActivity({
|
||||
name: 'view',
|
||||
data: {
|
||||
type: 'url',
|
||||
url: linkUrl
|
||||
}
|
||||
});
|
||||
}.bind(this)
|
||||
}, {
|
||||
id: 'msg-browse-cancel',
|
||||
handler: null
|
||||
});
|
||||
}
|
||||
},
|
||||
_populatePlaintextBodyNode: function (bodyNode, rep) {
|
||||
for (var i = 0; i < rep.length; i += 2) {
|
||||
var node = document.createElement('div'), cname;
|
||||
var etype = rep[i] & 15;
|
||||
if (etype === 4) {
|
||||
var qdepth = (rep[i] >> 8 & 255) + 1;
|
||||
if (qdepth > 8) {
|
||||
cname = MAX_QUOTE_CLASS_NAME;
|
||||
} else {
|
||||
cname = CONTENT_QUOTE_CLASS_NAMES[qdepth];
|
||||
}
|
||||
} else {
|
||||
cname = CONTENT_TYPES_TO_CLASS_NAMES[etype];
|
||||
}
|
||||
if (cname) {
|
||||
node.setAttribute('class', cname);
|
||||
}
|
||||
var subnodes = this.model.api.utils.linkifyPlain(rep[i + 1], document);
|
||||
for (var iNode = 0; iNode < subnodes.length; iNode++) {
|
||||
node.appendChild(subnodes[iNode]);
|
||||
}
|
||||
bodyNode.appendChild(node);
|
||||
}
|
||||
},
|
||||
buildHeaderDom: function (domNode) {
|
||||
var header = this.header;
|
||||
function updatePeep(peep) {
|
||||
var nameNode = peep.element.querySelector('.msg-peep-content');
|
||||
if (peep.type === 'from') {
|
||||
domNode.querySelector('.msg-reader-header-label').textContent = peep.name || peep.address;
|
||||
nameNode.textContent = peep.address;
|
||||
nameNode.classList.add('msg-peep-address');
|
||||
} else {
|
||||
nameNode.textContent = peep.name || peep.address;
|
||||
if (!peep.name && peep.address) {
|
||||
nameNode.classList.add('msg-peep-address');
|
||||
} else {
|
||||
nameNode.classList.remove('msg-peep-address');
|
||||
}
|
||||
}
|
||||
}
|
||||
function addHeaderEmails(type, peeps) {
|
||||
var lineClass = 'msg-envelope-' + type + '-line';
|
||||
var lineNode = domNode.getElementsByClassName(lineClass)[0];
|
||||
if (!peeps || !peeps.length) {
|
||||
lineNode.classList.add('collapsed');
|
||||
return;
|
||||
}
|
||||
lineNode.classList.remove('collapsed');
|
||||
var peepTemplate = msgPeepBubbleNode;
|
||||
for (var i = 0; i < peeps.length; i++) {
|
||||
var peep = peeps[i];
|
||||
peep.type = type;
|
||||
peep.element = peepTemplate.cloneNode(true);
|
||||
peep.element.peep = peep;
|
||||
peep.onchange = updatePeep;
|
||||
updatePeep(peep);
|
||||
lineNode.appendChild(peep.element);
|
||||
}
|
||||
}
|
||||
addHeaderEmails('from', [header.author]);
|
||||
addHeaderEmails('to', header.to);
|
||||
addHeaderEmails('cc', header.cc);
|
||||
addHeaderEmails('bcc', header.bcc);
|
||||
var dateNode = domNode.querySelector('.msg-envelope-date');
|
||||
var dateTime = dateNode.dataset.time = header.date.valueOf();
|
||||
date.relativeDateElement(dateNode, dateTime);
|
||||
messageDisplay.subject(domNode.querySelector('.msg-envelope-subject'), header);
|
||||
},
|
||||
clearDom: function () {
|
||||
Array.slice(this.querySelectorAll('.msg-peep-bubble')).forEach(function (node) {
|
||||
node.parentNode.removeChild(node);
|
||||
});
|
||||
var attachmentsContainer = this.querySelector('.msg-attachments-container');
|
||||
attachmentsContainer.innerHTML = '';
|
||||
this.rootBodyNode.innerHTML = '<progress data-l10n-id="message-body-container-progress"></progress>';
|
||||
this.loadBar.classList.add('collapsed');
|
||||
},
|
||||
buildBodyDom: function (changeDetails) {
|
||||
var body = this.body;
|
||||
if (!body || this.header.id !== body.id) {
|
||||
return;
|
||||
}
|
||||
var domNode = this, rootBodyNode = this.rootBodyNode, reps = body.bodyReps, hasExternalImages = false, showEmbeddedImages = body.embeddedImageCount && body.embeddedImagesDownloaded;
|
||||
if (!this._builtBodyDom) {
|
||||
iframeShims.bindSanitizedClickHandler(rootBodyNode, this.onHyperlinkClick.bind(this), rootBodyNode, null);
|
||||
this._builtBodyDom = true;
|
||||
}
|
||||
if (reps.length && reps[0].isDownloaded) {
|
||||
var progressNode = rootBodyNode.querySelector('progress');
|
||||
if (progressNode) {
|
||||
progressNode.parentNode.removeChild(progressNode);
|
||||
}
|
||||
}
|
||||
for (var iRep = 0; iRep < reps.length; iRep++) {
|
||||
var rep = reps[iRep];
|
||||
var repNode = rootBodyNode.childNodes[iRep];
|
||||
if (!repNode) {
|
||||
repNode = rootBodyNode.appendChild(document.createElement('div'));
|
||||
}
|
||||
if (changeDetails && changeDetails.bodyReps && changeDetails.bodyReps.indexOf(iRep) === -1) {
|
||||
continue;
|
||||
}
|
||||
repNode.innerHTML = '';
|
||||
if (rep.type === 'plain') {
|
||||
this._populatePlaintextBodyNode(repNode, rep.content);
|
||||
} else if (rep.type === 'html') {
|
||||
var iframeShim = iframeShims.createAndInsertIframeForContent(rep.content, this.scrollContainer, repNode, null, 'interactive', this.onHyperlinkClick.bind(this));
|
||||
var iframe = iframeShim.iframe;
|
||||
var bodyNode = iframe.contentDocument.body;
|
||||
this.iframeResizeHandler = iframeShim.resizeHandler;
|
||||
this.model.api.utils.linkifyHTML(iframe.contentDocument);
|
||||
this.htmlBodyNodes.push(bodyNode);
|
||||
if (body.checkForExternalImages(bodyNode)) {
|
||||
hasExternalImages = true;
|
||||
}
|
||||
if (showEmbeddedImages) {
|
||||
body.showEmbeddedImages(bodyNode, this.iframeResizeHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
var loadBar = this.loadBar;
|
||||
if (body.embeddedImageCount && !body.embeddedImagesDownloaded) {
|
||||
loadBar.classList.remove('collapsed');
|
||||
mozL10n.setAttributes(this.loadBarText, 'message-download-images-tap', { n: body.embeddedImageCount });
|
||||
} else if (hasExternalImages) {
|
||||
loadBar.classList.remove('collapsed');
|
||||
mozL10n.setAttributes(this.loadBarText, 'message-show-external-images');
|
||||
} else {
|
||||
loadBar.classList.add('collapsed');
|
||||
}
|
||||
var attachmentsContainer = domNode.querySelector('.msg-attachments-container');
|
||||
if (body.attachments && body.attachments.length) {
|
||||
attachmentsContainer.classList.remove('collapsed');
|
||||
require(['shared/js/mime_mapper'], function (mapper) {
|
||||
if (!MimeMapper) {
|
||||
MimeMapper = mapper;
|
||||
}
|
||||
var attTemplate = msgAttachmentItemNode, filenameTemplate = attTemplate.querySelector('.msg-attachment-filename'), filesizeTemplate = attTemplate.querySelector('.msg-attachment-filesize');
|
||||
for (var iAttach = 0; iAttach < body.attachments.length; iAttach++) {
|
||||
var attNode = attachmentsContainer.childNodes[iAttach];
|
||||
if (!attNode) {
|
||||
attNode = attachmentsContainer.appendChild(document.createElement('li'));
|
||||
}
|
||||
if (changeDetails && changeDetails.attachments && changeDetails.attachments.indexOf(iAttach) === -1) {
|
||||
continue;
|
||||
}
|
||||
var attachment = body.attachments[iAttach], state;
|
||||
var extension = attachment.filename.split('.').pop();
|
||||
var MAX_ATTACHMENT_SIZE = 25 * 1024 * 1024;
|
||||
var attachmentDownloadable = true;
|
||||
var mimeClass = mimeToClass(attachment.mimetype || MimeMapper.guessTypeFromExtension(extension));
|
||||
if (attachment.isDownloaded) {
|
||||
state = 'downloaded';
|
||||
} else if (!attachment.isDownloadable) {
|
||||
state = 'nodownload';
|
||||
attachmentDownloadable = false;
|
||||
} else if (attachment.sizeEstimateInBytes > MAX_ATTACHMENT_SIZE) {
|
||||
state = 'toolarge';
|
||||
attachmentDownloadable = false;
|
||||
} else {
|
||||
state = 'downloadable';
|
||||
}
|
||||
attTemplate.setAttribute('state', state);
|
||||
filenameTemplate.textContent = attachment.filename;
|
||||
fileDisplay.fileSize(filesizeTemplate, attachment.sizeEstimateInBytes);
|
||||
var attachmentNode = attTemplate.cloneNode(true);
|
||||
attachmentNode.classList.add(mimeClass);
|
||||
attachmentsContainer.replaceChild(attachmentNode, attNode);
|
||||
var downloadButton = attachmentNode.querySelector('.msg-attachment-download');
|
||||
downloadButton.disabled = !attachmentDownloadable;
|
||||
if (attachmentDownloadable) {
|
||||
downloadButton.addEventListener('click', this.onDownloadAttachmentClick.bind(this, attachmentNode, attachment));
|
||||
}
|
||||
attachmentNode.setAttribute('aria-disabled', !attachmentDownloadable);
|
||||
attachmentNode.querySelector('.msg-attachment-view').addEventListener('click', this.onViewAttachmentClick.bind(this, attachmentNode, attachment));
|
||||
this.enableReply();
|
||||
}
|
||||
}.bind(this));
|
||||
} else {
|
||||
attachmentsContainer.classList.add('collapsed');
|
||||
this.enableReply();
|
||||
}
|
||||
},
|
||||
disableReply: function () {
|
||||
var btn = this.querySelector('.msg-reply-btn');
|
||||
btn.setAttribute('aria-disabled', true);
|
||||
},
|
||||
enableReply: function () {
|
||||
var btn = this.querySelector('.msg-reply-btn');
|
||||
btn.removeAttribute('aria-disabled');
|
||||
if (!this._emittedContentEvents) {
|
||||
evt.emit('metrics:contentDone');
|
||||
this._emittedContentEvents = true;
|
||||
}
|
||||
},
|
||||
die: function () {
|
||||
var headerCursor = this.headerCursor;
|
||||
headerCursor.removeListener('messageSuidNotFound', this.onMessageSuidNotFound);
|
||||
headerCursor.removeListener('currentMessage', this.onCurrentMessage);
|
||||
if (this.header) {
|
||||
this.header.__die();
|
||||
this.header = null;
|
||||
}
|
||||
if (this.body) {
|
||||
this.body.die();
|
||||
this.body = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<form role="dialog" class="msg-attach-confirm" data-type="confirm">
|
||||
<section>
|
||||
<h1></h1>
|
||||
<p></p>
|
||||
</section>
|
||||
<menu>
|
||||
<button id="msg-attach-ok" class="full" data-l10n-id="dialog-button-ok"></button>
|
||||
</menu>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<form role="dialog" class="msg-attachment-did-not-open" data-type="confirm">
|
||||
<section>
|
||||
<h1 data-l10n-id="message-attachment-did-not-open-label"></h1>
|
||||
<p><span data-l10n-id="message-attachment-did-not-open-body"></span></p>
|
||||
</section>
|
||||
<menu>
|
||||
<button id="msg-attachment-did-not-open-ok" data-l10n-id="dialog-button-ok"></button>
|
||||
</menu>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<form role="dialog" class="msg-attachment-disabled-confirm" data-type="confirm">
|
||||
<section>
|
||||
<p data-l10n-id="message-send-attachment-disabled-confirm"></p>
|
||||
</section>
|
||||
<menu>
|
||||
<button id="msg-attachment-disabled-cancel" data-l10n-id="message-multiedit-cancel"></button>
|
||||
<button id="msg-attachment-disabled-ok" data-l10n-id="dialog-button-ok"></button>
|
||||
</menu>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<li class="msg-attachment-item">
|
||||
<span class="msg-attachment-icon" role="presentation"></span>
|
||||
<span class="msg-attachment-fileinfo">
|
||||
<span dir="auto" class="msg-attachment-filename"></span>
|
||||
<span class="msg-attachment-filesize"></span>
|
||||
<span data-l10n-id="message-attachment-too-large"
|
||||
class="msg-attachment-too-large"></span>
|
||||
</span>
|
||||
<button class="msg-attachment-download" data-l10n-id="download-button">
|
||||
<span class="icon icon-download"></span></button>
|
||||
<span class="msg-attachment-downloading">
|
||||
<progress class="small" data-l10n-id="downloading-progress"></progress>
|
||||
</span>
|
||||
<button class="msg-attachment-view" data-l10n-id="view-button">
|
||||
<span data-l10n-id="message-attachment-view" class="icon icon-view"></span>
|
||||
</button>
|
||||
</li>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<form role="dialog" class="msg-browse-confirm" data-type="confirm">
|
||||
<section>
|
||||
<h1 data-l10n-id="confirm-dialog-title"></h1>
|
||||
<p></p>
|
||||
</section>
|
||||
<menu>
|
||||
<button id="msg-browse-cancel" data-l10n-id="message-multiedit-cancel"></button>
|
||||
<button id="msg-browse-ok" class="recommend" data-l10n-id="dialog-button-ok"></button>
|
||||
</menu>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<form class="msg-contact-menu" role="dialog" data-type="action">
|
||||
<header></header>
|
||||
<menu>
|
||||
<button class="msg-contact-menu-new collapsed" data-l10n-id="message-contact-menu-new">
|
||||
</button>
|
||||
<button class="msg-contact-menu-reply collapsed" data-l10n-id="message-contact-menu-reply">
|
||||
</button>
|
||||
<button class="msg-contact-menu-view collapsed" data-l10n-id="message-contact-menu-view">
|
||||
</button>
|
||||
<button class="msg-contact-menu-create-contact collapsed" data-l10n-id="message-contact-menu-create">
|
||||
</button>
|
||||
<button class="msg-contact-menu-add-to-existing-contact collapsed" data-l10n-id="message-contact-menu-add-existing">
|
||||
</button>
|
||||
<span class="last-button-container">
|
||||
<button class="msg-contact-menu-cancel" data-l10n-id="message-multiedit-cancel">
|
||||
</button>
|
||||
</span>
|
||||
</menu>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<form role="dialog" class="msg-delete-confirm" data-type="confirm">
|
||||
<section>
|
||||
<h1 data-l10n-id="confirm-dialog-title"></h1>
|
||||
<p></p>
|
||||
</section>
|
||||
<menu>
|
||||
<button id="msg-delete-cancel" data-l10n-id="message-multiedit-cancel"></button>
|
||||
<button id="msg-delete-ok" class="danger" data-l10n-id="message-edit-menu-delete"></button>
|
||||
</menu>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<a class="msg-header-item" role="option" tabindex="0">
|
||||
<label class="pack-checkbox negative" aria-hidden="true">
|
||||
<input type="checkbox"><span></span>
|
||||
</label>
|
||||
<div class="msg-header-details-section">
|
||||
<span dir="auto" class="msg-header-author"></span>
|
||||
<span dir="auto" class="msg-header-subject"></span>
|
||||
<span dir="auto" class="msg-header-date"></span>
|
||||
<span dir="auto" class="msg-header-snippet"></span>
|
||||
</div>
|
||||
<div class="msg-header-syncing-section"></div>
|
||||
<div class="msg-header-unread-section"
|
||||
data-l10n-id="message-header-unread"></div>
|
||||
<div class="msg-header-icons-section">
|
||||
<span class="msg-header-star" data-l10n-id="message-header-starred"></span>
|
||||
<span class="msg-header-attachments"
|
||||
data-l10n-id="message-header-attachments"></span>
|
||||
</div><div class="msg-header-avatar-section" aria-hidden="true">
|
||||
</div></a>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<form role="dialog" class="msg-large-message-confirm" data-type="confirm">
|
||||
<section>
|
||||
<h1 data-l10n-id="confirm-dialog-title"></h1>
|
||||
<p><span data-l10n-id="message-large-message-confirm"></span></p>
|
||||
</section>
|
||||
<menu>
|
||||
<button id="msg-large-message-cancel" data-l10n-id="message-large-message-cancel"></button>
|
||||
<button id="msg-large-message-ok" data-l10n-id="message-large-message-ok"></button>
|
||||
</menu>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<div class="msg-peep-bubble peep-bubble" dir="auto" role="option">
|
||||
<span class="msg-peep-content"></span>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<form class="msg-reply-menu" role="dialog" data-type="action">
|
||||
<header></header>
|
||||
<menu>
|
||||
<button class="msg-reply-menu-reply" data-l10n-id="message-reply-menu-reply">
|
||||
</button>
|
||||
<button class="msg-reply-menu-reply-all" data-l10n-id="message-reply-menu-reply-all">
|
||||
</button>
|
||||
<button class="msg-reply-menu-forward" data-l10n-id="message-reply-menu-forward">
|
||||
</button>
|
||||
<span class="last-button-container">
|
||||
<button class="msg-reply-menu-cancel" data-l10n-id="message-reply-menu-cancel">
|
||||
</button>
|
||||
</span>
|
||||
</menu>
|
||||
</form>
|
||||
136
assets/webapps/email.gaiamobile.org/js/cards/oauth2/fetch.js
Normal file
136
assets/webapps/email.gaiamobile.org/js/cards/oauth2/fetch.js
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
define([
|
||||
'require',
|
||||
'query_string',
|
||||
'services',
|
||||
'cards'
|
||||
], function (require) {
|
||||
var queryString = require('query_string');
|
||||
var services = require('services');
|
||||
var cards = require('cards');
|
||||
var oauthSecrets = services.oauth2;
|
||||
var curOauthSecrets;
|
||||
var redeemingCode = false;
|
||||
var TIMEOUT_MS = 30 * 1000;
|
||||
var redirectUri = 'http://localhost';
|
||||
var deferred, p, oauthSettings;
|
||||
function onBrowserComplete(message) {
|
||||
var data = message.data;
|
||||
if (message.type === 'oauth2Complete') {
|
||||
if (!data.code) {
|
||||
reset('reject', new Error('no code returned'));
|
||||
return;
|
||||
}
|
||||
console.log('oauth redirect returned with code');
|
||||
redeemingCode = true;
|
||||
redeemCode(data.code).then(function (redeemed) {
|
||||
var expiresInMS = parseInt(redeemed.expires_in, 10) * 1000;
|
||||
var expireTimeMS = Date.now() + Math.max(0, expiresInMS - TIMEOUT_MS);
|
||||
var result = {
|
||||
status: 'success',
|
||||
tokens: {
|
||||
accessToken: redeemed.access_token,
|
||||
refreshToken: redeemed.refresh_token,
|
||||
expireTimeMS: expireTimeMS
|
||||
},
|
||||
secrets: curOauthSecrets
|
||||
};
|
||||
reset('resolve', result);
|
||||
}, function (err) {
|
||||
reset('reject', err);
|
||||
});
|
||||
} else {
|
||||
reset('resolve', { status: 'cancel' });
|
||||
}
|
||||
}
|
||||
function redeemCode(code) {
|
||||
console.log('redeeming oauth code');
|
||||
return new Promise(function (xhrResolve, xhrReject) {
|
||||
var xhr = new XMLHttpRequest({ mozSystem: true });
|
||||
xhr.open('POST', oauthSettings.tokenEndpoint, true);
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||
xhr.timeout = TIMEOUT_MS;
|
||||
xhr.onload = function () {
|
||||
if (xhr.status < 200 || xhr.status >= 300) {
|
||||
console.error('token redemption failed', xhr.status, xhr.responseText);
|
||||
xhrReject('status' + xhr.status);
|
||||
} else {
|
||||
try {
|
||||
var data = JSON.parse(xhr.responseText);
|
||||
console.log('oauth code redeemed. access_token? ' + !!data.access_token + ', refresh_token? ' + !!data.refresh_token);
|
||||
xhrResolve(data);
|
||||
} catch (ex) {
|
||||
console.error('badly formed JSON response for token redemption:', xhr.responseText);
|
||||
xhrReject(ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.onerror = function (err) {
|
||||
console.error('token redemption weird error:', err);
|
||||
xhrReject(err);
|
||||
};
|
||||
xhr.ontimeout = function () {
|
||||
console.error('token redemption timeout');
|
||||
xhrReject('timeout');
|
||||
};
|
||||
var redemptionArgs = {
|
||||
code: code,
|
||||
client_id: curOauthSecrets.clientId,
|
||||
client_secret: curOauthSecrets.clientSecret,
|
||||
redirect_uri: redirectUri,
|
||||
grant_type: 'authorization_code'
|
||||
};
|
||||
xhr.send(queryString.fromObject(redemptionArgs));
|
||||
});
|
||||
}
|
||||
function reset(actionName, value) {
|
||||
console.log('oauth2 fetch reset with action: ' + actionName);
|
||||
var action = deferred[actionName];
|
||||
deferred = p = null;
|
||||
redeemingCode = false;
|
||||
action(value);
|
||||
}
|
||||
return function oauth2Fetch(o2Settings, extraQueryObject) {
|
||||
if (deferred) {
|
||||
reset('reject', new Error('Multiple oauth calls, starting new one'));
|
||||
}
|
||||
p = new Promise(function (res, rej) {
|
||||
deferred = {
|
||||
resolve: res,
|
||||
reject: rej
|
||||
};
|
||||
});
|
||||
oauthSettings = o2Settings;
|
||||
curOauthSecrets = undefined;
|
||||
if (o2Settings.clientId && o2Settings.clientSecret) {
|
||||
curOauthSecrets = {
|
||||
clientId: o2Settings.clientId,
|
||||
clientSecret: o2Settings.clientSecret
|
||||
};
|
||||
} else if (o2Settings.secretGroup) {
|
||||
curOauthSecrets = oauthSecrets[o2Settings.secretGroup];
|
||||
}
|
||||
if (!curOauthSecrets) {
|
||||
reset('reject', new Error('no secrets for group: ' + o2Settings.secretGroup));
|
||||
return p;
|
||||
}
|
||||
var authEndpointQuery = {
|
||||
client_id: curOauthSecrets.clientId,
|
||||
redirect_uri: redirectUri,
|
||||
response_type: 'code',
|
||||
scope: o2Settings.scope,
|
||||
max_auth_age: 0
|
||||
};
|
||||
var url = o2Settings.authEndpoint + '?' + queryString.fromObject(authEndpointQuery);
|
||||
if (extraQueryObject) {
|
||||
var extraArgs = queryString.fromObject(extraQueryObject);
|
||||
if (extraArgs) {
|
||||
url += '&' + extraArgs;
|
||||
}
|
||||
}
|
||||
cards.pushCard('setup_oauth2', 'animate', {
|
||||
url: url,
|
||||
onBrowserComplete: onBrowserComplete
|
||||
});
|
||||
return p;
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
<!-- Per-account settings menu; largely read-only for now -->
|
||||
<!-- need this section element to keep building blocks happy. Too bad it
|
||||
expects a section element -->
|
||||
<section class="skin-organic bbshim settings-account-common" role="region">
|
||||
<header class="tng-account-header">
|
||||
<a data-event="click:onBack"
|
||||
href="#" class="tng-back-btn back-btn header-start-btn"
|
||||
role="button" data-l10n-id="back-button">
|
||||
<span class="icon icon-back"></span>
|
||||
</a>
|
||||
<h1 data-prop="headerLabel"
|
||||
class="tng-account-header-label header-label"></h1>
|
||||
</header>
|
||||
<section class="scrollregion-below-header skin-organic" role="region">
|
||||
<header class="collapsed"></header>
|
||||
<header class="tng-main-accounts-label">
|
||||
<h2 data-l10n-id="settings-sub-header-account">
|
||||
AccounT
|
||||
</h2>
|
||||
</header>
|
||||
<ul class="tng-list" data-l10n-id="settings-account-group">
|
||||
<li class="list-text-with-sub">
|
||||
<span data-l10n-id="settings-account-name" class="list-text">
|
||||
YouR NamE</span>
|
||||
<span data-prop="accountNameNode" dir="auto"
|
||||
class="tng-account-name list-sub-text"></span>
|
||||
</li>
|
||||
<li>
|
||||
<label class="tng-notify-mail-label pack-switch list-pack-switch">
|
||||
<input class="tng-notify-mail" type="checkbox" />
|
||||
<span data-l10n-id="settings-account-display-notifications"
|
||||
class="list-text list-pack-switch-text">
|
||||
DisplaY NotificationS FoR NeW MessageS</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="tng-sound-onsend-label pack-switch list-pack-switch">
|
||||
<input class="tng-sound-onsend" type="checkbox" />
|
||||
<span data-l10n-id="settings-account-play-sound-onsend"
|
||||
class="list-text list-pack-switch-text">
|
||||
PlaY SounD AfteR MessagE SenT</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label data-prop="defaultLabelNode"
|
||||
class="pack-checkbox tng-default-label list-pack-checkbox">
|
||||
<input data-prop="defaultInputNode"
|
||||
class="tng-default-input" type="checkbox">
|
||||
<span data-l10n-id="settings-default-account"
|
||||
class="list-text list-pack-checkbox-text">
|
||||
SeT AS DefaulT EmaiL</span>
|
||||
</label>
|
||||
</li>
|
||||
<li class="settings-signature settings-input-list">
|
||||
<label class="pack-switch tng-default-label list-pack-switch">
|
||||
<input class="tng-signature-input" type="checkbox">
|
||||
<span data-l10n-id="settings-account-signature-label"
|
||||
class="list-text list-pack-switch-text">
|
||||
SignaturE</span>
|
||||
</label>
|
||||
<button class="signature-button button icon">
|
||||
<div dir="auto" class="signature-button-content"></div>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<header class="tng-main-accounts-label">
|
||||
<h2 data-l10n-id="settings-sub-header-data">
|
||||
DatA
|
||||
</h2>
|
||||
</header>
|
||||
<ul class="tng-list" data-l10n-id="settings-data-group">
|
||||
<li class="syncinterval-setting select-item">
|
||||
<label data-l10n-id="settings-account-check-mail" class="list-text">
|
||||
ChecK FoR NeW MessageS</label>
|
||||
<span class="button icon icon-dialog">
|
||||
<select class="tng-account-check-interval mail-select"
|
||||
data-l10n-id="settings-account-check-mail-select">
|
||||
<option value="0" data-l10n-id="settings-check-every-manual">
|
||||
ManuallY</option>
|
||||
<option value="300000" data-l10n-id="settings-check-every-5min">
|
||||
5 MinuteS</option>
|
||||
<option value="600000" data-l10n-id="settings-check-every-10min">
|
||||
10 MinuteS</option>
|
||||
<option value="900000" data-l10n-id="settings-check-every-15min">
|
||||
15 MinuteS</option>
|
||||
<option value="1800000" data-l10n-id="settings-check-every-30min">
|
||||
30 MinuteS</option>
|
||||
<option value="3600000" data-l10n-id="settings-check-every-60min">
|
||||
60 MinuteS</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
<li data-prop="syncSettingNode" class="synchronize-setting select-item">
|
||||
<label data-l10n-id="settings-account-synchronize-window"
|
||||
class="list-text"></label>
|
||||
<span class="button icon icon-dialog">
|
||||
<select data-prop="synchronizeNode"
|
||||
data-event="change:onChangeSynchronize"
|
||||
class="mail-select tng-account-synchronize"
|
||||
data-l10n-id="settings-account-synchronize-window-select">
|
||||
<option value="auto" data-l10n-id="settings-synchronize-auto">
|
||||
AutomatiC</option>
|
||||
<option value="1d" data-l10n-id="settings-synchronize-one-day">
|
||||
1 DaY</option>
|
||||
<option value="3d" data-l10n-id="settings-synchronize-three-days">
|
||||
3 DayS</option>
|
||||
<option value="1w" data-l10n-id="settings-synchronize-one-week">
|
||||
1 WeeK</option>
|
||||
<option value="2w" data-l10n-id="settings-synchronize-two-weeks">
|
||||
2 WeekS</option>
|
||||
<option value="1m" data-l10n-id="settings-synchronize-one-month">
|
||||
1 MontH</option>
|
||||
<option value="all" data-l10n-id="settings-synchronize-all">
|
||||
AlL MessageS</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<header class="tng-main-accounts-label">
|
||||
<h2 data-l10n-id="settings-sub-header-server">
|
||||
ServeR SettingS
|
||||
</h2>
|
||||
</header>
|
||||
<ul data-prop="serversContainer" data-l10n-id="settings-server-group"
|
||||
class="tng-list tng-account-server-container">
|
||||
<li class="item-with-children">
|
||||
<a href="#" data-prop="accountCredNode"
|
||||
data-event="click:onClickCredentials"
|
||||
class="tng-account-credentials list-text"></a>
|
||||
</li>
|
||||
<li class="list-text-pair">
|
||||
<span data-l10n-id="settings-account-type" role="presentation"
|
||||
class="list-text aside start">
|
||||
AccounT TypE</span>
|
||||
<div role="presentation">
|
||||
<span data-prop="accountTypeNode" dir="auto" role="presentation"
|
||||
class="tng-account-type"></span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<button href="#" data-l10n-id="settings-account-delete"
|
||||
data-event="click:onDelete"
|
||||
class="tng-account-delete danger"></button>
|
||||
</section>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
define([
|
||||
'require',
|
||||
'tmpl!./tng/account_settings_server.html',
|
||||
'tmpl!./tng/account_delete_confirm.html',
|
||||
'evt',
|
||||
'l10n!',
|
||||
'cards',
|
||||
'confirm_dialog',
|
||||
'./base_card',
|
||||
'template!./settings_account.html',
|
||||
'./account_prefs_mixins'
|
||||
], function (require) {
|
||||
var tngAccountSettingsServerNode = require('tmpl!./tng/account_settings_server.html'), tngAccountDeleteConfirmNode = require('tmpl!./tng/account_delete_confirm.html'), evt = require('evt'), mozL10n = require('l10n!'), cards = require('cards'), ConfirmDialog = require('confirm_dialog');
|
||||
return [
|
||||
require('./base_card')(require('template!./settings_account.html')),
|
||||
require('./account_prefs_mixins'),
|
||||
{
|
||||
onArgs: function (args) {
|
||||
this.account = args.account;
|
||||
this.identity = this.account.identities[0];
|
||||
this.headerLabel.textContent = args.account.name;
|
||||
this._bindPrefs('tng-account-check-interval', 'tng-notify-mail', 'tng-sound-onsend', 'tng-signature-input', 'signature-button');
|
||||
this.accountNameNode.textContent = this.identity && this.identity.name || this.account.name;
|
||||
this.accountTypeNode.textContent = this.account.type === 'activesync' ? 'ActiveSync' : this.account.type === 'imap+smtp' ? 'IMAP+SMTP' : 'POP3+SMTP';
|
||||
if (this.account.isDefault) {
|
||||
this.defaultInputNode.disabled = true;
|
||||
this.defaultInputNode.checked = true;
|
||||
} else {
|
||||
this.defaultLabelNode.addEventListener('click', this.onChangeDefaultAccount.bind(this), false);
|
||||
}
|
||||
if (this.account.type === 'activesync') {
|
||||
this.synchronizeNode.value = this.account.syncRange;
|
||||
} else {
|
||||
this.syncSettingNode.parentNode.removeChild(this.syncSettingNode);
|
||||
}
|
||||
this.account.servers.forEach(function (server, index) {
|
||||
var serverNode = tngAccountSettingsServerNode.cloneNode(true);
|
||||
var serverLabel = serverNode.querySelector('.tng-account-server-label');
|
||||
mozL10n.setAttributes(serverLabel, 'settings-' + server.type + '-label');
|
||||
serverLabel.addEventListener('click', this.onClickServers.bind(this, index), false);
|
||||
this.serversContainer.appendChild(serverNode);
|
||||
}.bind(this));
|
||||
var credL10nId = 'settings-account-userpass';
|
||||
if (this.account.authMechanism === 'oauth2') {
|
||||
credL10nId = 'settings-account-useroauth2';
|
||||
}
|
||||
mozL10n.setAttributes(this.accountCredNode, credL10nId);
|
||||
},
|
||||
onBack: function () {
|
||||
cards.removeCardAndSuccessors(this, 'animate', 1);
|
||||
},
|
||||
onCardVisible: function () {
|
||||
this.updateSignatureButton();
|
||||
},
|
||||
onClickCredentials: function () {
|
||||
cards.pushCard('settings_account_credentials', 'animate', { account: this.account }, 'right');
|
||||
},
|
||||
onClickServers: function (index) {
|
||||
cards.pushCard('settings_account_servers', 'animate', {
|
||||
account: this.account,
|
||||
index: index
|
||||
}, 'right');
|
||||
},
|
||||
onChangeDefaultAccount: function (event) {
|
||||
event.stopPropagation();
|
||||
if (event.preventBubble) {
|
||||
event.preventBubble();
|
||||
}
|
||||
if (!this.defaultInputNode.disabled) {
|
||||
this.defaultInputNode.disabled = true;
|
||||
this.defaultInputNode.checked = true;
|
||||
this.account.modifyAccount({ setAsDefault: true });
|
||||
}
|
||||
},
|
||||
onChangeSynchronize: function (event) {
|
||||
this.account.modifyAccount({ syncRange: event.target.value });
|
||||
},
|
||||
onDelete: function () {
|
||||
var account = this.account;
|
||||
var dialog = tngAccountDeleteConfirmNode.cloneNode(true);
|
||||
var content = dialog.getElementsByTagName('p')[0];
|
||||
mozL10n.setAttributes(content, 'settings-account-delete-prompt', { account: account.name.replace(/@/, '\u200B@\u200B') });
|
||||
ConfirmDialog.show(dialog, {
|
||||
id: 'account-delete-ok',
|
||||
handler: function () {
|
||||
account.deleteAccount();
|
||||
evt.emit('accountDeleted', account);
|
||||
}
|
||||
}, {
|
||||
id: 'account-delete-cancel',
|
||||
handler: null
|
||||
});
|
||||
},
|
||||
die: function () {
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<!-- Per-account credentials settings menu -->
|
||||
<!-- need this section element to keep building blocks happy. Too bad it
|
||||
expects a section element -->
|
||||
<section class="skin-organic bbshim" role="region">
|
||||
<header class="tng-account-server-header">
|
||||
<a data-event="click:onBack"
|
||||
href="#" class="tng-back-btn back-btn header-start-btn"
|
||||
role="button" data-l10n-id="back-button">
|
||||
<span class="icon icon-back"></span>
|
||||
</a>
|
||||
<h1 data-prop="headerLabel"
|
||||
class="tng-account-header-label header-label"></h1>
|
||||
</header>
|
||||
<section class="scrollregion-below-header skin-organic" role="region">
|
||||
<header class="collapsed"></header>
|
||||
<header>
|
||||
<h2 data-l10n-id="settings-account-credentials"
|
||||
class="tng-account-credentials-label"></h2>
|
||||
</header>
|
||||
<div class="tng-form">
|
||||
<li class="tng-account-server-username settings-input-list">
|
||||
<h2 data-l10n-id="settings-username"
|
||||
class="tng-server-username-title" aria-level="3">
|
||||
UsernamE</h2>
|
||||
<input disabled data-prop="usernameNodeInput"
|
||||
dir="auto"
|
||||
class="tng-server-username-input" type="text" />
|
||||
</li>
|
||||
<li class="tng-account-server-password settings-input-list">
|
||||
<h2 data-l10n-id="settings-password"
|
||||
class="tng-server-password-title" aria-level="3">
|
||||
PassworD</h2>
|
||||
<!-- When the input is focused, a resize event will be fired
|
||||
and will trigger _showCard() in mail-common.js
|
||||
the UI state will become wrong after _showCard() is called
|
||||
so disable the input first to prevent the wrong state
|
||||
-->
|
||||
<input data-prop="passwordNodeInput"
|
||||
class="tng-server-password-input" type="password"
|
||||
data-l10n-id="setup-new-password" />
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<button data-l10n-id="settings-save"
|
||||
data-prop="saveButton"
|
||||
data-event="click:onClickSave"
|
||||
class="tng-account-save settings-button recommend"></button>
|
||||
|
||||
<button data-l10n-id="settings-reauth"
|
||||
data-prop="reauthButton"
|
||||
data-event="click:onClickReauth"
|
||||
class="tng-account-reauth settings-button recommend collapsed">
|
||||
</button>
|
||||
</section>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
define([
|
||||
'require',
|
||||
'l10n!',
|
||||
'cards',
|
||||
'./oauth2/fetch',
|
||||
'./base_card',
|
||||
'template!./settings_account_credentials.html'
|
||||
], function (require) {
|
||||
var mozL10n = require('l10n!'), cards = require('cards'), oauthFetch = require('./oauth2/fetch');
|
||||
return [
|
||||
require('./base_card')(require('template!./settings_account_credentials.html')),
|
||||
{
|
||||
onArgs: function (args) {
|
||||
this.account = args.account;
|
||||
this.headerLabel.textContent = this.account.name;
|
||||
if (this.account.authMechanism !== 'password') {
|
||||
this.querySelector('.tng-account-server-password').classList.add('collapsed');
|
||||
this.saveButton.classList.add('collapsed');
|
||||
}
|
||||
if (this.account.authMechanism === 'oauth2') {
|
||||
this.reauthButton.classList.remove('collapsed');
|
||||
}
|
||||
this.usernameNodeInput.value = this.account.username;
|
||||
},
|
||||
onBack: function () {
|
||||
cards.removeCardAndSuccessors(this, 'animate', 1);
|
||||
},
|
||||
onClickSave: function () {
|
||||
var password = this.passwordNodeInput.value;
|
||||
if (password) {
|
||||
this.account.modifyAccount({ password: password });
|
||||
this.account.clearProblems();
|
||||
} else {
|
||||
alert(mozL10n.get('settings-password-empty'));
|
||||
}
|
||||
this.onBack();
|
||||
},
|
||||
onClickReauth: function () {
|
||||
var oauth2 = this.account._wireRep.credentials.oauth2;
|
||||
oauthFetch(oauth2, { login_hint: this.account.username }).then(function (response) {
|
||||
if (response.status === 'success') {
|
||||
this.account.modifyAccount({ oauthTokens: response.tokens });
|
||||
this.account.clearProblems();
|
||||
this.onBack();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
die: function () {
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<!-- Per-account server settings menu -->
|
||||
<!-- need this section element to keep building blocks happy. Too bad it
|
||||
expects a section element -->
|
||||
<section class="skin-organic bbshim" role="region">
|
||||
<header class="tng-account-server-header">
|
||||
<a data-event="click:onBack"
|
||||
href="#" class="tng-back-btn back-btn header-start-btn"
|
||||
role="button" data-l10n-id="back-button">
|
||||
<span class="icon icon-back"></span>
|
||||
</a>
|
||||
<h1 data-prop="headerLabel"
|
||||
class="tng-account-header-label header-label">
|
||||
AccounT NamE</h1>
|
||||
</header>
|
||||
<section class="scrollregion-below-header skin-organic" role="region">
|
||||
<header class="collapsed"></header>
|
||||
<header>
|
||||
<h2 data-prop="serverLabel"
|
||||
class="tng-account-server-label"></h2>
|
||||
</header>
|
||||
<div class="tng-form">
|
||||
<li class="tng-account-server-hostname settings-input-list">
|
||||
<h2 data-l10n-id="settings-hostname"
|
||||
class="tng-server-hostname-title" aria-level="3">
|
||||
HostnamE</h2>
|
||||
<input data-prop="hostnameNodeInput"
|
||||
disabled class="tng-server-hostname-input"
|
||||
type="text" />
|
||||
</li>
|
||||
<li class="tng-account-server-port settings-input-list">
|
||||
<h2 data-l10n-id="settings-port"
|
||||
class="tng-server-port-title" aria-level="3"></h2>
|
||||
<input data-prop="portNodeInput" disabled class="tng-server-port-input"
|
||||
type="text" />
|
||||
</li>
|
||||
</div>
|
||||
<!-- nothing is editable, nothing is saved; collapse. -->
|
||||
<button data-l10n-id="settings-save"
|
||||
data-event="click:onBack"
|
||||
class="tng-account-save settings-button recommend collapsed"
|
||||
></button>
|
||||
</section>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
define([
|
||||
'require',
|
||||
'l10n!',
|
||||
'cards',
|
||||
'./base_card',
|
||||
'template!./settings_account_servers.html'
|
||||
], function (require) {
|
||||
var mozL10n = require('l10n!'), cards = require('cards');
|
||||
return [
|
||||
require('./base_card')(require('template!./settings_account_servers.html')),
|
||||
{
|
||||
onArgs: function (args) {
|
||||
this.account = args.account;
|
||||
this.server = args.account.servers[args.index];
|
||||
this.headerLabel.textContent = this.account.name;
|
||||
mozL10n.setAttributes(this.serverLabel, 'settings-' + this.server.type + '-label');
|
||||
this.hostnameNodeInput.value = this.server.connInfo.hostname || this.server.connInfo.server;
|
||||
this.portNodeInput.value = this.server.connInfo.port || '';
|
||||
},
|
||||
onBack: function () {
|
||||
cards.removeCardAndSuccessors(this, 'animate', 1);
|
||||
},
|
||||
die: function () {
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<!-- Secret developer debug page; for those in the know. This page can only be
|
||||
reached by tapping 5 times on some whitespace on the main settings page.
|
||||
|
||||
NOTE NOTE NOTE NOTE NOTE
|
||||
|
||||
This page is intentionally unlocalized. There's a few reasons for this:
|
||||
- The intended users are Firefox OS developers for which English is
|
||||
currently a pre-requisite.
|
||||
- It allows us to change these settings without being particularly
|
||||
concerned about string freeze.
|
||||
- We don't have to worry about the localized strings not capturing the
|
||||
risk of enabling this logging.
|
||||
|
||||
This raises the question: Should this be shipped in non-developer builds if
|
||||
we think it's not worth localizing? Our current rationale is that it is
|
||||
useful. We (the developers) have had bug reporters enable the logging and
|
||||
worked with us to fix bugs using the functionality. Especially given our
|
||||
limited ability to provide updated versions of the app or have users
|
||||
side-load versions of our certified app.
|
||||
-->
|
||||
<!-- This section element is needed by building blocks, would be nice if
|
||||
bb used a class instead of a section element target -->
|
||||
<section class="skin-organic settings-account-common bbshim" role="region">
|
||||
<header class="tng-account-header">
|
||||
<a data-event="click:onClose"
|
||||
href="#" class="tng-back-btn back-btn header-start-btn">
|
||||
<span class="icon icon-back"></span>
|
||||
</a>
|
||||
<h1 class="header-label">Debug Magic</h1>
|
||||
</header>
|
||||
<section class="scrollregion-below-header skin-organic" role="region">
|
||||
<button data-event="click:resetApp"
|
||||
href="#" class="tng-dbg-reset">Reset App</button>
|
||||
<header><h2>Logging</h2></header>
|
||||
<ul class="tng-list">
|
||||
<li class="select-item">
|
||||
<label class="list-text">Logging Mode</label>
|
||||
<span class="button icon icon-dialog">
|
||||
<select data-prop="loggingSelect"
|
||||
data-event="change:onChangeLogging"
|
||||
class="tng-dbg-logging">
|
||||
<option value="">
|
||||
No logging.
|
||||
</option>
|
||||
<option value="safe">
|
||||
Circular: Safe data: no passwords or message contents logged.
|
||||
</option>
|
||||
<option value="dangerous">
|
||||
Circular: Dangerous data: passwords and message contents logged.
|
||||
</option>
|
||||
<option value="realtime-dangerous">
|
||||
Realtime + Circular: Dangerous data: passwords and message contents logged.
|
||||
</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<button data-event="click:dumpLog"
|
||||
href="#"
|
||||
class="tng-dbg-dump-storage"
|
||||
>Dump circular log buffer to sdcard</button>
|
||||
<header><h2>More Settings!</h2></header>
|
||||
<button data-event="click:fastSync"
|
||||
href="#"
|
||||
class="tng-dbg-fastsync">Enable faster sync options</button>
|
||||
|
||||
<button data-event="click:resetStartupCache"
|
||||
href="#">Reset startup cache</button>
|
||||
</section>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
var _secretDebug;
|
||||
define([
|
||||
'require',
|
||||
'api',
|
||||
'cards',
|
||||
'html_cache',
|
||||
'./base_card',
|
||||
'template!./settings_debug.html'
|
||||
], function (require) {
|
||||
var MailAPI = require('api'), cards = require('cards'), htmlCache = require('html_cache');
|
||||
if (!_secretDebug) {
|
||||
_secretDebug = {};
|
||||
}
|
||||
return [
|
||||
require('./base_card')(require('template!./settings_debug.html')),
|
||||
{
|
||||
createdCallback: function () {
|
||||
this.loggingSelect.value = MailAPI.config.debugLogging || '';
|
||||
},
|
||||
onClose: function () {
|
||||
cards.removeCardAndSuccessors(this, 'animate', 1);
|
||||
},
|
||||
resetApp: function () {
|
||||
window.location.reload();
|
||||
},
|
||||
dumpLog: function (target) {
|
||||
MailAPI.debugSupport('dumpLog', target);
|
||||
},
|
||||
onChangeLogging: function () {
|
||||
var value = this.loggingSelect.value || false;
|
||||
MailAPI.debugSupport('setLogging', value);
|
||||
},
|
||||
fastSync: function () {
|
||||
_secretDebug.fastSync = [
|
||||
20000,
|
||||
60000
|
||||
];
|
||||
},
|
||||
resetStartupCache: function () {
|
||||
htmlCache.reset();
|
||||
console.log('htmlCache.reset done');
|
||||
},
|
||||
die: function () {
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<!-- Main settings menu, root of all (mail) settings -->
|
||||
<!-- need this section element to keep building blocks happy. Too bad it
|
||||
expects a section element -->
|
||||
<section class="skin-organic bbshim" role="region">
|
||||
<header class="tng-main-header">
|
||||
<menu data-event="click:onClose" type="toolbar" class="tng-close-btn">
|
||||
<button data-l10n-id="settings-done"></button>
|
||||
</menu>
|
||||
<h1 class="tng-main-header-label"
|
||||
data-l10n-id="settings-main-header"></h1>
|
||||
</header>
|
||||
<section class="scrollregion-below-header skin-organic" role="region">
|
||||
<header class="collapsed"></header>
|
||||
<header class="tng-main-accounts-label">
|
||||
<h2 data-l10n-id="settings-account-section"></h2>
|
||||
</header>
|
||||
<ul data-prop="accountsContainer" class="tng-accounts-container"
|
||||
data-l10n-id="settings-account-listbox" role="listbox">
|
||||
</ul>
|
||||
<button data-event="click:onClickAddAccount"
|
||||
href="#" data-l10n-id="settings-account-add"
|
||||
class="tng-account-add"></button>
|
||||
<a data-event="click:onClickSecretButton"
|
||||
class="tng-email-lib-version list-text">v1</a>
|
||||
</section>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
define([
|
||||
'require',
|
||||
'tmpl!./tng/account_item.html',
|
||||
'api',
|
||||
'cards',
|
||||
'./base_card',
|
||||
'template!./settings_main.html'
|
||||
], function (require) {
|
||||
var tngAccountItemNode = require('tmpl!./tng/account_item.html'), MailAPI = require('api'), cards = require('cards');
|
||||
return [
|
||||
require('./base_card')(require('template!./settings_main.html')),
|
||||
{
|
||||
createdCallback: function () {
|
||||
this.acctsSlice = MailAPI.viewAccounts(false);
|
||||
this.acctsSlice.onsplice = this.onAccountsSplice.bind(this);
|
||||
this._secretButtonClickCount = 0;
|
||||
this._secretButtonTimer = null;
|
||||
},
|
||||
extraClasses: [
|
||||
'anim-fade',
|
||||
'anim-overlay'
|
||||
],
|
||||
onClose: function () {
|
||||
cards.removeCardAndSuccessors(this, 'animate', 1, 1);
|
||||
},
|
||||
onAccountsSplice: function (index, howMany, addedItems, requested, moreExpected) {
|
||||
var accountsContainer = this.accountsContainer;
|
||||
var account;
|
||||
if (howMany) {
|
||||
for (var i = index + howMany - 1; i >= index; i--) {
|
||||
account = this.acctsSlice.items[i];
|
||||
accountsContainer.removeChild(account.element);
|
||||
}
|
||||
}
|
||||
var insertBuddy = index >= accountsContainer.childElementCount ? null : accountsContainer.children[index], self = this;
|
||||
addedItems.forEach(function (account) {
|
||||
var accountNode = account.element = tngAccountItemNode.cloneNode(true);
|
||||
accountNode.account = account;
|
||||
self.updateAccountDom(account, true);
|
||||
accountsContainer.insertBefore(accountNode, insertBuddy);
|
||||
});
|
||||
},
|
||||
updateAccountDom: function (account, firstTime) {
|
||||
var accountNode = account.element;
|
||||
if (firstTime) {
|
||||
var accountLabel = accountNode.querySelector('.tng-account-item-label');
|
||||
accountLabel.textContent = account.name;
|
||||
accountNode.setAttribute('aria-label', account.name);
|
||||
accountNode.addEventListener('click', this.onClickEnterAccount.bind(this, account), false);
|
||||
}
|
||||
},
|
||||
onClickAddAccount: function () {
|
||||
cards.pushCard('setup_account_info', 'animate', { allowBack: true }, 'right');
|
||||
},
|
||||
onClickEnterAccount: function (account) {
|
||||
cards.pushCard('settings_account', 'animate', { account: account }, 'right');
|
||||
},
|
||||
onClickSecretButton: function () {
|
||||
if (this._secretButtonTimer === null) {
|
||||
this._secretButtonTimer = window.setTimeout(function () {
|
||||
this._secretButtonTimer = null;
|
||||
this._secretButtonClickCount = 0;
|
||||
}.bind(this), 2000);
|
||||
}
|
||||
if (++this._secretButtonClickCount >= 5) {
|
||||
window.clearTimeout(this._secretButtonTimer);
|
||||
this._secretButtonTimer = null;
|
||||
this._secretButtonClickCount = 0;
|
||||
cards.pushCard('settings_debug', 'animate', {}, 'right');
|
||||
}
|
||||
},
|
||||
die: function () {
|
||||
this.acctsSlice.die();
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<!-- need this section element to keep building blocks happy. Too bad it
|
||||
expects a section element -->
|
||||
<section class="skin-organic bbshim" role="region">
|
||||
<header class="tng-signature-header">
|
||||
<a data-event="click:onBack"
|
||||
href="#"
|
||||
class="signature-back-btn tng-back-btn back-btn header-start-btn"
|
||||
role="button" data-l10n-id="back-button">
|
||||
<span class="icon icon-back"></span>
|
||||
</a>
|
||||
<menu data-event="click:onClickDone"
|
||||
class="tng-close-btn tng-signature-done" type="toolbar">
|
||||
<button data-l10n-id="settings-done"></button>
|
||||
</menu>
|
||||
<h1 class="tng-signature-header-label header-label" data-l10n-id="settings-account-signature-label"></h1>
|
||||
</header>
|
||||
<section class="scrollregion-below-header skin-organic" role="region">
|
||||
<div data-prop="signatureNode"
|
||||
dir="auto"
|
||||
role="textbox"
|
||||
aria-multiline="true"
|
||||
data-l10n-id="settings-account-signature-textbox"
|
||||
class="tng-signature-text" contenteditable="true"></div>
|
||||
</section>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
define([
|
||||
'require',
|
||||
'tmpl!./sig/save_signature.html',
|
||||
'cards',
|
||||
'./base_card',
|
||||
'template!./settings_signature.html',
|
||||
'./editor_mixins'
|
||||
], function (require) {
|
||||
var backFormNode = require('tmpl!./sig/save_signature.html'), cards = require('cards'), trailingRegExp = /\s+$/;
|
||||
return [
|
||||
require('./base_card')(require('template!./settings_signature.html')),
|
||||
require('./editor_mixins'),
|
||||
{
|
||||
onArgs: function (args) {
|
||||
this.account = args.account;
|
||||
this.identity = this.account.identities[0];
|
||||
this._bindEditor(this.signatureNode);
|
||||
this.populateEditor(this.identity.signature || '');
|
||||
},
|
||||
getTextFromEditor: function () {
|
||||
var text = this.fromEditor().replace(trailingRegExp, '');
|
||||
return text;
|
||||
},
|
||||
goBack: function () {
|
||||
cards.removeCardAndSuccessors(this, 'animate', 1);
|
||||
},
|
||||
onBack: function () {
|
||||
var signature = this.getTextFromEditor();
|
||||
if (signature === this.identity.signature) {
|
||||
this.goBack();
|
||||
return;
|
||||
}
|
||||
var menu = backFormNode.cloneNode(true);
|
||||
this._savePromptMenu = menu;
|
||||
cards.setStatusColor(menu);
|
||||
document.body.appendChild(menu);
|
||||
var formSubmit = function (evt) {
|
||||
cards.setStatusColor();
|
||||
document.body.removeChild(menu);
|
||||
this._savePromptMenu = null;
|
||||
switch (evt.explicitOriginalTarget.id) {
|
||||
case 'sig-save':
|
||||
this.identity.modifyIdentity({ signature: signature });
|
||||
this.goBack();
|
||||
break;
|
||||
case 'sig-discard':
|
||||
this.goBack();
|
||||
break;
|
||||
case 'sig-cancel':
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}.bind(this);
|
||||
menu.addEventListener('submit', formSubmit);
|
||||
},
|
||||
onClickDone: function () {
|
||||
var signature = this.getTextFromEditor();
|
||||
if (signature !== this.identity.signature) {
|
||||
this.identity.modifyIdentity({ signature: signature });
|
||||
}
|
||||
this.onBack();
|
||||
},
|
||||
onCardVisible: function () {
|
||||
var selection = window.getSelection();
|
||||
if (selection.rangeCount > 0) {
|
||||
selection.removeAllRanges();
|
||||
}
|
||||
var node = this.signatureNode.lastChild;
|
||||
if (node) {
|
||||
var range = document.createRange();
|
||||
range.setStartAfter(node);
|
||||
range.setEndAfter(node);
|
||||
selection.addRange(range);
|
||||
}
|
||||
this.signatureNode.focus();
|
||||
},
|
||||
die: function () {
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
define([
|
||||
'require',
|
||||
'./setup_l10n_map',
|
||||
'l10n!'
|
||||
], function (require) {
|
||||
var SETUP_ERROR_L10N_ID_MAP = require('./setup_l10n_map'), mozL10n = require('l10n!');
|
||||
return {
|
||||
showError: function (errName, errDetails) {
|
||||
this.errorRegionNode.classList.remove('collapsed');
|
||||
var errorStr = SETUP_ERROR_L10N_ID_MAP.hasOwnProperty(errName) ? SETUP_ERROR_L10N_ID_MAP[errName] : SETUP_ERROR_L10N_ID_MAP.unknown;
|
||||
mozL10n.setAttributes(this.errorMessageNode, errorStr, errDetails);
|
||||
this.scrollBelowNode.scrollTop = 0;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<!-- need this section element to keep building blocks happy. Too bad it
|
||||
expects a section element -->
|
||||
<section class="skin-organic form-card bbshim" role="region">
|
||||
<header class="sup-account-header">
|
||||
<a data-prop="backButton"
|
||||
data-event="click:onBack"
|
||||
href="#" class="sup-back-btn collapsed"
|
||||
role="button" data-l10n-id="back-button">
|
||||
<span class="icon icon-back"></span>
|
||||
</a>
|
||||
<h1 class="sup-account-header-label"
|
||||
data-l10n-id="setup-account-header3"></h1>
|
||||
</header>
|
||||
<div data-prop="scrollBelowNode" class="scrollregion-below-header">
|
||||
<div data-prop="errorRegionNode" class="sup-error-region collapsed">
|
||||
<div data-prop="errorMessageNode" class="sup-error-message"></div>
|
||||
</div>
|
||||
<form data-prop="formNode" class="sup-form sup-account-form">
|
||||
<p>
|
||||
<!-- x-inputmode is a temporary v1 B2G stop-gap -->
|
||||
<input data-prop="nameNode"
|
||||
data-event="input:onInfoInput"
|
||||
class="sup-info-name" type="text"
|
||||
data-l10n-id="setup-info-name"
|
||||
x-inputmode="verbatim"
|
||||
inputmode="verbatim"
|
||||
dir="auto"
|
||||
required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<p>
|
||||
<input data-prop="emailNode"
|
||||
data-event="input:onInfoInput"
|
||||
class="sup-info-email" type="email"
|
||||
data-l10n-id="setup-info-email"
|
||||
dir="auto"
|
||||
required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<p>
|
||||
<button data-prop="nextButton"
|
||||
data-event="click:onNext"
|
||||
class="sup-info-next-btn recommend" disabled>
|
||||
<span data-l10n-id="setup-info-next"></span>
|
||||
</button>
|
||||
<button data-prop="manualConfig"
|
||||
data-event="click:onClickManualConfig"
|
||||
class="sup-manual-config-btn" disabled
|
||||
data-l10n-id="setup-manual-config2">
|
||||
ManuaL SetuP
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'evt',
|
||||
'l10n!',
|
||||
'cards',
|
||||
'html_cache',
|
||||
'form_navigation',
|
||||
'./base_card',
|
||||
'template!./setup_account_info.html',
|
||||
'./setup_account_error_mixin'
|
||||
], function (require, exports, module) {
|
||||
var evt = require('evt'), mozL10n = require('l10n!'), cards = require('cards'), htmlCache = require('html_cache'), FormNavigation = require('form_navigation');
|
||||
return [
|
||||
require('./base_card')(require('template!./setup_account_info.html')),
|
||||
require('./setup_account_error_mixin'),
|
||||
{
|
||||
createdCallback: function () {
|
||||
htmlCache.cloneAndSave(module.id, this);
|
||||
this.formNavigation = new FormNavigation({
|
||||
formElem: this.formNode,
|
||||
onLast: this.onNext.bind(this)
|
||||
});
|
||||
this.needsFocus = true;
|
||||
},
|
||||
onArgs: function (args) {
|
||||
if (args.allowBack) {
|
||||
this.backButton.classList.remove('collapsed');
|
||||
}
|
||||
if (args.launchedFromActivity) {
|
||||
this.errorRegionNode.classList.remove('collapsed');
|
||||
mozL10n.setAttributes(this.errorMessageNode, 'setup-empty-account-message');
|
||||
}
|
||||
},
|
||||
onCardVisible: function () {
|
||||
if (this.needsFocus) {
|
||||
this.nameNode.focus();
|
||||
this.needsFocus = false;
|
||||
}
|
||||
},
|
||||
onBack: function (event) {
|
||||
evt.emit('setupAccountCanceled', this);
|
||||
},
|
||||
onNext: function (event) {
|
||||
event.preventDefault();
|
||||
htmlCache.reset();
|
||||
cards.pushCard('setup_progress', 'animate', {
|
||||
displayName: this.nameNode.value,
|
||||
emailAddress: this.emailNode.value,
|
||||
callingCard: this
|
||||
}, 'right');
|
||||
},
|
||||
onInfoInput: function (event) {
|
||||
this.nextButton.disabled = this.manualConfig.disabled = !this.formNode.checkValidity();
|
||||
},
|
||||
onClickManualConfig: function (event) {
|
||||
event.preventDefault();
|
||||
cards.pushCard('setup_manual_config', 'animate', {
|
||||
displayName: this.nameNode.value,
|
||||
emailAddress: this.emailNode.value
|
||||
}, 'right');
|
||||
},
|
||||
die: function () {
|
||||
this.formNavigation = null;
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<!-- need this section element to keep building blocks happy. Too bad it
|
||||
expects a section element -->
|
||||
<section class="form-card card skin-organic bbshim" role="region">
|
||||
<header class="sup-account-header">
|
||||
<a href="#"
|
||||
data-event="click:onBack" class="sup-back-btn">
|
||||
<span class="icon icon-back"></span>
|
||||
</a>
|
||||
<h1 class="sup-account-header-label"
|
||||
data-l10n-id="setup-account-header3"></h1>
|
||||
</header>
|
||||
<div data-prop="scrollBelowNode" class="scrollregion-below-header">
|
||||
<div data-prop="errorRegionNode" class="sup-error-region collapsed">
|
||||
<div data-prop="errorMessageNode" class="sup-error-message"></div>
|
||||
</div>
|
||||
<form data-prop="formNode" class="sup-form sup-account-form">
|
||||
<p class="list-text-with-sub" role="heading" aria-level="2">
|
||||
<span data-l10n-id="setup-account-enter-password" class="list-text">
|
||||
EnteR PassworD FoR:</span>
|
||||
<span data-prop="emailNode"
|
||||
class="sup-email-display list-sub-text"></span>
|
||||
</p>
|
||||
<p>
|
||||
<input data-prop="passwordNode"
|
||||
data-event="input:onInfoInput"
|
||||
class="sup-info-password" type="password"
|
||||
data-l10n-id="setup-info-password"
|
||||
dir="auto"
|
||||
required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<button data-prop="nextButton"
|
||||
data-event="click:onNext"
|
||||
class="sup-info-next-btn recommend" disabled>
|
||||
<span data-l10n-id="setup-info-next"></span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
define([
|
||||
'require',
|
||||
'mix',
|
||||
'cards',
|
||||
'form_navigation',
|
||||
'./base_card',
|
||||
'template!./setup_account_password.html',
|
||||
'./setup_account_error_mixin'
|
||||
], function (require) {
|
||||
var mix = require('mix'), cards = require('cards'), FormNavigation = require('form_navigation');
|
||||
function bindFormNavigation(instance) {
|
||||
return new FormNavigation({
|
||||
formElem: instance.formNode,
|
||||
onLast: instance.onNext.bind(instance)
|
||||
});
|
||||
}
|
||||
return [
|
||||
require('./base_card')(require('template!./setup_account_password.html')),
|
||||
require('./setup_account_error_mixin'),
|
||||
{
|
||||
onArgs: function (args) {
|
||||
this.args = args;
|
||||
this.emailAddress = args.emailAddress;
|
||||
this.emailNode.textContent = this.emailAddress;
|
||||
this.needsFocus = true;
|
||||
bindFormNavigation(this);
|
||||
},
|
||||
onCardVisible: function () {
|
||||
if (this.needsFocus) {
|
||||
this.passwordNode.focus();
|
||||
this.needsFocus = false;
|
||||
}
|
||||
},
|
||||
onBack: function (event) {
|
||||
cards.removeCardAndSuccessors(this, 'animate', 1);
|
||||
},
|
||||
onNext: function (event) {
|
||||
event.preventDefault();
|
||||
this.args.password = this.passwordNode.value;
|
||||
cards.pushCard('setup_progress', 'animate', mix({ callingCard: this }, this.args), 'right');
|
||||
},
|
||||
onInfoInput: function (event) {
|
||||
this.nextButton.disabled = !this.formNode.checkValidity();
|
||||
},
|
||||
die: function () {
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<!-- need this section element to keep building blocks happy. Too bad it
|
||||
expects a section element -->
|
||||
<section class="skin-organic bbshim settings-account-common" role="region">
|
||||
<header class="sup-account-header">
|
||||
<menu type="toolbar">
|
||||
<button data-event="click:onNext" class="sup-info-next-btn">
|
||||
<span data-l10n-id="setup-info-next"></span>
|
||||
</button>
|
||||
</menu>
|
||||
<h1 class="sup-account-header-label"
|
||||
data-l10n-id="setup-account-header3"></h1>
|
||||
</header>
|
||||
<section class="scrollregion-below-header skin-organic" role="region">
|
||||
<ul class="tng-list">
|
||||
<li class="syncinterval-setting select-item">
|
||||
<label data-l10n-id="settings-account-check-mail" class="list-text">
|
||||
ChecK FoR NeW MessageS</label>
|
||||
<span class="button icon icon-dialog">
|
||||
<select class="tng-account-check-interval mail-select"
|
||||
data-l10n-id="settings-account-check-mail-select">
|
||||
<option value="0" data-l10n-id="settings-check-every-manual">
|
||||
ManuallY</option>
|
||||
<option value="300000" data-l10n-id="settings-check-every-5min">
|
||||
5 MinuteS</option>
|
||||
<option value="600000" data-l10n-id="settings-check-every-10min">
|
||||
10 MinuteS</option>
|
||||
<option value="900000" data-l10n-id="settings-check-every-15min">
|
||||
15 MinuteS</option>
|
||||
<option value="1800000" data-l10n-id="settings-check-every-30min">
|
||||
30 MinuteS</option>
|
||||
<option value="3600000" data-l10n-id="settings-check-every-60min">
|
||||
60 MinuteS</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<label class="tng-notify-mail-label pack-switch list-pack-switch">
|
||||
<input class="tng-notify-mail" type="checkbox" />
|
||||
<span data-l10n-id="settings-account-display-notifications"
|
||||
class="list-text list-pack-switch-text">
|
||||
DisplaY NotificationS FoR NeW MessageS</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="tng-sound-onsend-label pack-switch list-pack-switch">
|
||||
<input class="tng-sound-onsend" type="checkbox" />
|
||||
<span data-l10n-id="settings-account-play-sound-onsend"
|
||||
class="list-text list-pack-switch-text">
|
||||
PlaY SounD AfteR MessagE SenT</span>
|
||||
</label>
|
||||
</li>
|
||||
<li class="settings-signature settings-input-list">
|
||||
<label class="pack-switch tng-default-label list-pack-switch">
|
||||
<input class="tng-signature-input" type="checkbox">
|
||||
<span data-l10n-id="settings-account-signature-label"
|
||||
class="list-text list-pack-switch-text">
|
||||
SignaturE</span>
|
||||
</label>
|
||||
<button class="signature-button button icon">
|
||||
<div dir="auto" class="signature-button-content"></div>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
define([
|
||||
'require',
|
||||
'cards',
|
||||
'l10n!',
|
||||
'./base_card',
|
||||
'template!./setup_account_prefs.html',
|
||||
'./account_prefs_mixins'
|
||||
], function (require) {
|
||||
var cards = require('cards'), mozL10n = require('l10n!');
|
||||
return [
|
||||
require('./base_card')(require('template!./setup_account_prefs.html')),
|
||||
require('./account_prefs_mixins'),
|
||||
{
|
||||
onArgs: function (args) {
|
||||
this.account = args.account;
|
||||
this.identity = this.account.identities[0];
|
||||
this.identity.modifyIdentity({
|
||||
signatureEnabled: true,
|
||||
signature: mozL10n.get('settings-default-signature-2')
|
||||
});
|
||||
this._bindPrefs('tng-account-check-interval', 'tng-notify-mail', 'tng-sound-onsend', 'tng-signature-input', 'signature-button');
|
||||
},
|
||||
onNext: function (event) {
|
||||
cards.pushCard('setup_done', 'animate');
|
||||
},
|
||||
onCardVisible: function () {
|
||||
this.updateSignatureButton();
|
||||
},
|
||||
die: function () {
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
24
assets/webapps/email.gaiamobile.org/js/cards/setup_done.html
Normal file
24
assets/webapps/email.gaiamobile.org/js/cards/setup_done.html
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<!-- Account all setup, add another or read your mail? -->
|
||||
<!-- need this section element to keep building blocks happy. Too bad it
|
||||
expects a section element -->
|
||||
<section class="skin-organic bbshim" role="region">
|
||||
<header class="sup-account-header">
|
||||
<h1 class="sup-account-header-label"
|
||||
data-l10n-id="setup-completed-header"></h1>
|
||||
</header>
|
||||
<div class="sup-progress-region">
|
||||
<span class="sup-progress-label" data-l10n-id="setup-completed-label">
|
||||
AlL DonE!
|
||||
</span>
|
||||
</div>
|
||||
<button data-event="click:onAddAnother"
|
||||
class="sup-add-another-account-btn sup-form-btn"
|
||||
data-l10n-id="setup-add-another-account">
|
||||
AdD AnotheR AccounT
|
||||
</button>
|
||||
<button data-event="click:onShowMail"
|
||||
class="sup-show-mail-btn sup-form-btn recommend"
|
||||
data-l10n-id="setup-show-mail">
|
||||
ContinuE tO MaiL
|
||||
</button>
|
||||
</section>
|
||||
21
assets/webapps/email.gaiamobile.org/js/cards/setup_done.js
Normal file
21
assets/webapps/email.gaiamobile.org/js/cards/setup_done.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
define([
|
||||
'require',
|
||||
'evt',
|
||||
'./base_card',
|
||||
'template!./setup_done.html'
|
||||
], function (require) {
|
||||
var evt = require('evt');
|
||||
return [
|
||||
require('./base_card')(require('template!./setup_done.html')),
|
||||
{
|
||||
onAddAnother: function () {
|
||||
evt.emit('addAccount');
|
||||
},
|
||||
onShowMail: function () {
|
||||
evt.emit('showLatestAccount');
|
||||
},
|
||||
die: function () {
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<section class="error-card form-card card skin-organic bbshim" role="region">
|
||||
<header>
|
||||
<h1 class="sup-account-header-label header-label"></h1>
|
||||
</header>
|
||||
<div class="scrollregion-below-header">
|
||||
<h2 class="sup-enable-label sup-form-label"></h2>
|
||||
|
||||
<p data-prop="accountNode" class="sup-gmail-account"></p>
|
||||
|
||||
<div class="actions">
|
||||
<button data-event="click:onDismiss"
|
||||
class="sup-dismiss-btn recommend"></button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
define([
|
||||
'require',
|
||||
'cards',
|
||||
'./base_card',
|
||||
'template!./setup_fix_gmail.html'
|
||||
], function (require) {
|
||||
var cards = require('cards');
|
||||
return [
|
||||
require('./base_card')(require('template!./setup_fix_gmail.html')),
|
||||
{
|
||||
extraClasses: [
|
||||
'anim-fade',
|
||||
'anim-overlay'
|
||||
],
|
||||
onArgs: function (args) {
|
||||
this.account = args.account;
|
||||
this.restoreCard = args.restoreCard;
|
||||
this.accountNode.textContent = this.account.name;
|
||||
var translations = {
|
||||
'sup-account-header-label': 'setup-gmail-{ACCOUNT_TYPE}-header',
|
||||
'sup-enable-label': 'setup-gmail-{ACCOUNT_TYPE}-message',
|
||||
'sup-dismiss-btn': 'setup-gmail-{ACCOUNT_TYPE}-retry'
|
||||
};
|
||||
var accountType = this.account.type === 'imap+smtp' ? 'imap' : 'pop3';
|
||||
for (var className in translations) {
|
||||
var l10nId = translations[className].replace('{ACCOUNT_TYPE}', accountType);
|
||||
this.getElementsByClassName(className)[0].setAttribute('data-l10n-id', l10nId);
|
||||
}
|
||||
},
|
||||
die: function () {
|
||||
},
|
||||
onDismiss: function () {
|
||||
this.account.clearProblems();
|
||||
cards.removeCardAndSuccessors(this, 'animate', 1, this.restoreCard);
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<section class="error-card form-card card skin-organic bbshim" role="region">
|
||||
<header>
|
||||
<h1 class="sup-account-header-label header-label"
|
||||
data-l10n-id="setup-gmail-twofactor-header"></h1>
|
||||
</header>
|
||||
<div class="scrollregion-below-header">
|
||||
<h2 class="sup-bad-password-label sup-form-label"
|
||||
data-l10n-id="setup-gmail-twofactor-message"></h2>
|
||||
|
||||
<p data-prop="accountNode" class="sup-bad-password-account"></p>
|
||||
|
||||
<input data-prop="passwordNode" class="sup-info-password" type="password"
|
||||
data-l10n-id="setup-info-password" />
|
||||
|
||||
<div class="actions">
|
||||
<button data-event="click:onUsePassword"
|
||||
class="sup-use-password-btn recommend"
|
||||
data-l10n-id="setup-gmail-twofactor-fix"></button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
define([
|
||||
'require',
|
||||
'./base_card',
|
||||
'template!./setup_fix_gmail_twofactor.html',
|
||||
'./setup_fix_mixin'
|
||||
], function (require) {
|
||||
return [
|
||||
require('./base_card')(require('template!./setup_fix_gmail_twofactor.html')),
|
||||
require('./setup_fix_mixin')
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
define([
|
||||
'require',
|
||||
'l10n!',
|
||||
'cards'
|
||||
], function (require) {
|
||||
var mozL10n = require('l10n!'), cards = require('cards');
|
||||
return {
|
||||
extraClasses: [
|
||||
'anim-fade',
|
||||
'anim-overlay'
|
||||
],
|
||||
onArgs: function (args) {
|
||||
this.account = args.account;
|
||||
this.whichSide = args.whichSide;
|
||||
this.restoreCard = args.restoreCard;
|
||||
var type = this.account.type;
|
||||
if (type === 'imap+smtp' || type === 'pop3+smtp') {
|
||||
var l10nString = null;
|
||||
if (this.whichSide === 'incoming') {
|
||||
if (type === 'imap+smtp') {
|
||||
l10nString = 'settings-account-clarify-imap';
|
||||
} else {
|
||||
l10nString = 'settings-account-clarify-pop3';
|
||||
}
|
||||
} else {
|
||||
l10nString = 'settings-account-clarify-smtp';
|
||||
}
|
||||
mozL10n.setAttributes(this.accountNode, l10nString, { 'account-name': this.account.name });
|
||||
}
|
||||
},
|
||||
onUsePassword: function () {
|
||||
var password = this.passwordNode.value;
|
||||
if (password) {
|
||||
this.account.modifyAccount(this.whichSide === 'incoming' ? { password: password } : { outgoingPassword: password }, this.proceed.bind(this));
|
||||
} else {
|
||||
this.proceed();
|
||||
}
|
||||
},
|
||||
proceed: function () {
|
||||
this.account.clearProblems();
|
||||
cards.removeCardAndSuccessors(this, 'animate', 1, this.restoreCard);
|
||||
},
|
||||
die: function () {
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<!-- need this section element to keep building blocks happy. Too bad it
|
||||
expects a section element -->
|
||||
<section class="error-card form-card skin-organic bbshim" role="region">
|
||||
<header>
|
||||
<h1 class="sup-account-header-label header-label"
|
||||
data-l10n-id="setup-fix-oauth2-header"></h1>
|
||||
</header>
|
||||
<div class="scrollregion-below-header">
|
||||
<p class="sup-oauth-description"
|
||||
data-l10n-id="setup-fix-oauth2-description">
|
||||
EmaiL NeedS ReauthorizatioN FoR AccounT
|
||||
</p>
|
||||
|
||||
<p data-prop="oauth2Name" class="sup-oauth2-name"></p>
|
||||
|
||||
<div class="actions">
|
||||
<button data-event="click:onReauth"
|
||||
class="sup-reauth-btn recommend"
|
||||
data-l10n-id="setup-fix-oauth2-reauthorize"></button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
define([
|
||||
'require',
|
||||
'./oauth2/fetch',
|
||||
'cards',
|
||||
'./base_card',
|
||||
'template!./setup_fix_oauth2.html'
|
||||
], function (require) {
|
||||
var oauthFetch = require('./oauth2/fetch'), cards = require('cards');
|
||||
return [
|
||||
require('./base_card')(require('template!./setup_fix_oauth2.html')),
|
||||
{
|
||||
extraClasses: [
|
||||
'anim-fade',
|
||||
'anim-overlay'
|
||||
],
|
||||
onArgs: function (args) {
|
||||
this.account = args.account;
|
||||
this.restoreCard = args.restoreCard;
|
||||
this.oauth2Name.textContent = this.account.name;
|
||||
},
|
||||
die: function () {
|
||||
},
|
||||
onReauth: function (event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
var oauth2 = this.account._wireRep.credentials.oauth2;
|
||||
oauthFetch(oauth2, { login_hint: this.account.username }).then(function (response) {
|
||||
if (response.status === 'cancel') {
|
||||
this.delayedClose();
|
||||
} else if (response.status === 'success') {
|
||||
this.account.modifyAccount({ oauthTokens: response.tokens });
|
||||
this.account.clearProblems();
|
||||
this.delayedClose();
|
||||
} else {
|
||||
console.error('Unknown oauthFetch status: ' + response.status);
|
||||
this.delayedClose();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
delayedClose: function () {
|
||||
setTimeout(this.close.bind(this), 100);
|
||||
},
|
||||
close: function (event) {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
cards.removeCardAndSuccessors(this, 'animate', 1, this.restoreCard);
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<section class="error-card form-card card skin-organic bbshim" role="region">
|
||||
<header>
|
||||
<h1 class="sup-account-header-label header-label"
|
||||
data-l10n-id="setup-bad-password-header"></h1>
|
||||
</header>
|
||||
<div class="scrollregion-below-header sup-form">
|
||||
<form>
|
||||
<h2 class="sup-bad-password-label sup-form-label"
|
||||
data-l10n-id="setup-bad-password"></h2>
|
||||
|
||||
<p data-prop="accountNode" class="sup-bad-password-account"></p>
|
||||
|
||||
<p>
|
||||
<input data-prop="passwordNode"
|
||||
class="sup-info-password" type="password"
|
||||
dir="auto"
|
||||
data-l10n-id="setup-new-password" />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
|
||||
<div class="actions">
|
||||
<button data-event="click:onUsePassword"
|
||||
class="sup-use-password-btn recommend"
|
||||
data-l10n-id="setup-fix-password"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
define([
|
||||
'require',
|
||||
'./base_card',
|
||||
'template!./setup_fix_password.html',
|
||||
'./setup_fix_mixin'
|
||||
], function (require) {
|
||||
return [
|
||||
require('./base_card')(require('template!./setup_fix_password.html')),
|
||||
require('./setup_fix_mixin')
|
||||
];
|
||||
});
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
define({
|
||||
'offline': 'setup-error-offline',
|
||||
'bad-user-or-pass': 'setup-error-bad-user-or-pass2',
|
||||
'bad-address': 'setup-error-bad-address',
|
||||
'not-authorized': 'setup-error-not-authorized',
|
||||
'unknown': 'setup-error-unknown2',
|
||||
'needs-app-pass': 'setup-error-needs-app-pass',
|
||||
'imap-disabled': 'setup-error-imap-disabled',
|
||||
'pop3-disabled': 'setup-error-pop3-disabled',
|
||||
'bad-security': 'setup-error-bad-security',
|
||||
'unresponsive-server': 'setup-error-unresponsive-server',
|
||||
'pop-server-not-great': 'setup-error-pop-server-not-great',
|
||||
'server-problem': 'setup-error-server-problem',
|
||||
'no-config-info': 'setup-error-no-config-info',
|
||||
'server-maintenance': 'setup-error-server-maintenance',
|
||||
'user-account-exists': 'setup-error-account-already-exists'
|
||||
});
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
<!-- need this section element to keep building blocks happy. Too bad it
|
||||
expects a section element -->
|
||||
<section class="skin-organic form-card bbshim" role="region">
|
||||
<header class="sup-account-header">
|
||||
<a data-event="click:onBack" href="#" class="sup-back-btn" role="button"
|
||||
data-l10n-id="back-button">
|
||||
<span class="icon icon-back"></span>
|
||||
</a>
|
||||
<h1 class="sup-account-header-label header-label"
|
||||
data-l10n-id="setup-manual-config-header"></h1>
|
||||
</header>
|
||||
<div data-prop="scrollBelowNode" class="scrollregion-below-header">
|
||||
<div data-prop="errorRegionNode" class="sup-error-region collapsed">
|
||||
<div data-prop="errorMessageNode" class="sup-error-message"></div>
|
||||
</div>
|
||||
<header class="collapsed"></header>
|
||||
<form data-prop="formNode" class="sup-manual-form">
|
||||
<div class="sup-manual-common sup-form">
|
||||
<p>
|
||||
<!-- x-inputmode is a temporary v1 B2G stop-gap -->
|
||||
<input class="sup-info-name" type="text"
|
||||
data-l10n-id="setup-info-name"
|
||||
x-inputmode="verbatim"
|
||||
inputmode="verbatim"
|
||||
dir="auto"
|
||||
required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<p>
|
||||
<input class="sup-info-email" type="email"
|
||||
data-l10n-id="setup-info-email"
|
||||
dir="auto"
|
||||
required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<p class="sup-manual-password-wrapper">
|
||||
<input class="sup-info-password" type="password"
|
||||
data-l10n-id="setup-info-password"
|
||||
dir="auto"
|
||||
required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<header>
|
||||
<h2 data-l10n-id="setup-manual-account-type"></h2>
|
||||
</header>
|
||||
<div class="sup-form sup-form-item">
|
||||
<!-- These don't need L10N as they are protocol names. -->
|
||||
<li class="sup-manual-account-type-container">
|
||||
<span class="button icon icon-dialog">
|
||||
<select data-prop="accountTypeNode"
|
||||
data-event="change:onChangeAccountType"
|
||||
data-l10n-id="setup-manual-account-type-select"
|
||||
class="mail-select sup-manual-account-type">
|
||||
<option value="imap+smtp">IMAP+SMTP</option>
|
||||
<option value="pop3+smtp">POP3+SMTP</option>
|
||||
<option value="activesync">ActiveSync</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<section data-prop="compositeSection"
|
||||
class="sup-manual-composite" role="region">
|
||||
<header class="collapsed"></header>
|
||||
<header>
|
||||
<h2 data-prop="manualImapTitle" class="sup-manual-imap-title"
|
||||
data-l10n-id="setup-manual-imap-header"></h2>
|
||||
<h2 data-prop="manualPop3Title" class="sup-manual-pop3-title"
|
||||
data-l10n-id="setup-manual-pop3-header"></h2>
|
||||
</header>
|
||||
<div class="sup-form">
|
||||
<p>
|
||||
<input class="sup-manual-composite-hostname" type="text"
|
||||
data-l10n-id="setup-manual-hostname"
|
||||
x-inputmode="verbatim"
|
||||
inputmode="verbatim"
|
||||
dir="auto"
|
||||
data-maybe-required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<p>
|
||||
<input class="sup-manual-composite-username" type="text"
|
||||
data-l10n-id="setup-manual-username"
|
||||
x-inputmode="verbatim"
|
||||
inputmode="verbatim"
|
||||
dir="auto"
|
||||
data-maybe-required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<p>
|
||||
<input class="sup-manual-composite-password" type="password"
|
||||
data-l10n-id="setup-info-password"
|
||||
dir="auto"
|
||||
data-maybe-required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<li class="sup-socket-select-container">
|
||||
<span class="button icon icon-dialog">
|
||||
<select class="mail-select sup-manual-composite-socket sup-socket-select"
|
||||
data-l10n-id="setup-manual-socket-select">
|
||||
<option value="SSL" data-l10n-id="setup-manual-socket-ssl">
|
||||
SsL</option>
|
||||
<option value="STARTTLS" data-l10n-id="setup-manual-socket-starttls">
|
||||
StarttlS</option>
|
||||
<option value="PLAIN" data-l10n-id="setup-manual-socket-plain">
|
||||
PlaiN</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
<p>
|
||||
<input class="sup-manual-composite-port" type="text"
|
||||
data-l10n-id="setup-manual-port" value="993"
|
||||
dir="auto"
|
||||
data-maybe-required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<header>
|
||||
<h2 data-l10n-id="setup-manual-smtp-header"></h2>
|
||||
</header>
|
||||
<div class="sup-form">
|
||||
<p>
|
||||
<input class="sup-manual-smtp-hostname" type="text"
|
||||
data-l10n-id="setup-manual-hostname"
|
||||
x-inputmode="verbatim"
|
||||
inputmode="verbatim"
|
||||
dir="auto"
|
||||
data-maybe-required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<p>
|
||||
<input class="sup-manual-smtp-username" type="text"
|
||||
data-l10n-id="setup-manual-username"
|
||||
x-inputmode="verbatim"
|
||||
inputmode="verbatim"
|
||||
dir="auto"
|
||||
data-maybe-required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<p>
|
||||
<input class="sup-manual-smtp-password" type="password"
|
||||
data-l10n-id="setup-info-password"
|
||||
dir="auto"
|
||||
data-maybe-required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<li class="sup-socket-select-container">
|
||||
<span class="button icon icon-dialog">
|
||||
<select class="mail-select sup-manual-smtp-socket sup-socket-select"
|
||||
data-l10n-id="setup-manual-socket-select">
|
||||
<option value="SSL" data-l10n-id="setup-manual-socket-ssl">
|
||||
SsL</option>
|
||||
<option value="STARTTLS" data-l10n-id="setup-manual-socket-starttls">
|
||||
StarttlS</option>
|
||||
<option value="PLAIN" data-l10n-id="setup-manual-socket-plain">
|
||||
PlaiN</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
<p>
|
||||
<input class="sup-manual-smtp-port" type="text"
|
||||
data-l10n-id="setup-manual-port" value="465"
|
||||
dir="auto"
|
||||
data-maybe-required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-prop="activeSyncSection"
|
||||
class="sup-manual-activesync collapsed" role="region">
|
||||
<header class="collapsed"></header>
|
||||
<header>
|
||||
<h2 data-l10n-id="setup-manual-activesync-header">
|
||||
ActivesynC SettingS</h2>
|
||||
</header>
|
||||
<div class="sup-form">
|
||||
<p>
|
||||
<input class="sup-manual-activesync-hostname" type="text"
|
||||
data-l10n-id="setup-manual-hostname"
|
||||
x-inputmode="verbatim"
|
||||
inputmode="verbatim"
|
||||
dir="auto"
|
||||
data-maybe-required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
<p>
|
||||
<input class="sup-manual-activesync-username" type="text"
|
||||
data-l10n-id="setup-manual-activesync-username"
|
||||
dir="auto"
|
||||
data-maybe-required />
|
||||
<button type="reset"></button>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<div class="sup-form">
|
||||
<p>
|
||||
<button data-prop="nextButton"
|
||||
data-event="click:onNext"
|
||||
class="sup-manual-next-btn recommend" disabled>
|
||||
<span data-l10n-id="setup-info-next"></span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
define([
|
||||
'require',
|
||||
'cards',
|
||||
'confirm_dialog',
|
||||
'form_navigation',
|
||||
'tmpl!./tng/plain_socket_warning.html',
|
||||
'./base_card',
|
||||
'template!./setup_manual_config.html',
|
||||
'./setup_account_error_mixin'
|
||||
], function (require) {
|
||||
var cards = require('cards'), ConfirmDialog = require('confirm_dialog'), FormNavigation = require('form_navigation'), plainSocketWarningNode = require('tmpl!./tng/plain_socket_warning.html');
|
||||
return [
|
||||
require('./base_card')(require('template!./setup_manual_config.html')),
|
||||
require('./setup_account_error_mixin'),
|
||||
{
|
||||
onArgs: function (args) {
|
||||
this.formItems = {
|
||||
common: {
|
||||
displayName: this._fromClass('sup-info-name'),
|
||||
emailAddress: this._fromClass('sup-info-email'),
|
||||
password: this._fromClass('sup-info-password'),
|
||||
passwordWrapper: this._fromClass('sup-manual-password-wrapper')
|
||||
},
|
||||
composite: {
|
||||
hostname: this._fromClass('sup-manual-composite-hostname'),
|
||||
port: this._fromClass('sup-manual-composite-port'),
|
||||
socket: this._fromClass('sup-manual-composite-socket'),
|
||||
username: this._fromClass('sup-manual-composite-username'),
|
||||
password: this._fromClass('sup-manual-composite-password')
|
||||
},
|
||||
smtp: {
|
||||
hostname: this._fromClass('sup-manual-smtp-hostname'),
|
||||
port: this._fromClass('sup-manual-smtp-port'),
|
||||
socket: this._fromClass('sup-manual-smtp-socket'),
|
||||
username: this._fromClass('sup-manual-smtp-username'),
|
||||
password: this._fromClass('sup-manual-smtp-password')
|
||||
},
|
||||
activeSync: {
|
||||
hostname: this._fromClass('sup-manual-activesync-hostname'),
|
||||
username: this._fromClass('sup-manual-activesync-username')
|
||||
}
|
||||
};
|
||||
var password = password || '';
|
||||
var common = this.formItems.common;
|
||||
common.displayName.value = args.displayName;
|
||||
common.emailAddress.value = args.emailAddress;
|
||||
common.password.value = password;
|
||||
var composite = this.formItems.composite;
|
||||
composite.username.value = args.emailAddress;
|
||||
composite.password.value = password;
|
||||
var smtp = this.formItems.smtp;
|
||||
smtp.username.value = args.emailAddress;
|
||||
smtp.password.value = password;
|
||||
this.changeIfSame(common.emailAddress, [
|
||||
composite.username,
|
||||
smtp.username
|
||||
]);
|
||||
this.changeIfSame(composite.username, [smtp.username]);
|
||||
this.changeIfSame(composite.password, [
|
||||
smtp.password,
|
||||
common.password
|
||||
]);
|
||||
for (var type in this.formItems) {
|
||||
for (var field in this.formItems[type]) {
|
||||
if (this.formItems[type][field].tagName === 'INPUT') {
|
||||
this.formItems[type][field].addEventListener('input', this.onInfoInput.bind(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.requireFields('composite', true);
|
||||
this.requireFields('smtp', true);
|
||||
this.requireFields('activeSync', false);
|
||||
composite.socket.addEventListener('change', this.onChangeCompositeSocket.bind(this));
|
||||
smtp.socket.addEventListener('change', this.onChangeSmtpSocket.bind(this));
|
||||
this.onChangeAccountType({ target: this.accountTypeNode });
|
||||
this._formNavigation = new FormNavigation({
|
||||
formElem: this.formNode,
|
||||
onLast: this.onNext.bind(this)
|
||||
});
|
||||
},
|
||||
_fromClass: function (className) {
|
||||
return this.getElementsByClassName(className)[0];
|
||||
},
|
||||
onBack: function (event) {
|
||||
cards.removeCardAndSuccessors(this, 'animate', 1);
|
||||
},
|
||||
onNext: function (event) {
|
||||
event.preventDefault();
|
||||
var config = { type: this.accountTypeNode.value };
|
||||
if (config.type === 'imap+smtp' || config.type === 'pop3+smtp') {
|
||||
config.incoming = {
|
||||
hostname: this.formItems.composite.hostname.value,
|
||||
port: this.formItems.composite.port.value,
|
||||
socketType: this.formItems.composite.socket.value,
|
||||
username: this.formItems.composite.username.value,
|
||||
password: this.formItems.composite.password.value,
|
||||
authentication: 'password-cleartext'
|
||||
};
|
||||
config.outgoing = {
|
||||
hostname: this.formItems.smtp.hostname.value,
|
||||
port: this.formItems.smtp.port.value,
|
||||
socketType: this.formItems.smtp.socket.value,
|
||||
username: this.formItems.smtp.username.value,
|
||||
password: this.formItems.smtp.password.value,
|
||||
authentication: 'password-cleartext'
|
||||
};
|
||||
} else {
|
||||
config.incoming = {
|
||||
server: 'https://' + this.formItems.activeSync.hostname.value,
|
||||
username: this.formItems.activeSync.username.value
|
||||
};
|
||||
}
|
||||
this.pushSetupCard(config);
|
||||
},
|
||||
pushSetupCard: function (config) {
|
||||
var password;
|
||||
if (this.accountTypeNode.value === 'activesync') {
|
||||
password = this.formItems.common.password.value;
|
||||
} else {
|
||||
password = this.formItems.composite.password.value;
|
||||
}
|
||||
cards.pushCard('setup_progress', 'animate', {
|
||||
displayName: this.formItems.common.displayName.value,
|
||||
emailAddress: this.formItems.common.emailAddress.value,
|
||||
password: password,
|
||||
outgoingPassword: config.outgoing && config.outgoing.password,
|
||||
configInfo: config,
|
||||
callingCard: this
|
||||
}, 'right');
|
||||
},
|
||||
onInfoInput: function (ignoredEvent) {
|
||||
this.nextButton.disabled = !this.formNode.checkValidity();
|
||||
},
|
||||
changeIfSame: function (sourceField, destFields) {
|
||||
sourceField._previousValue = sourceField.value;
|
||||
sourceField.addEventListener('input', function (e) {
|
||||
for (var i = 0; i < destFields.length; i++) {
|
||||
var destField = destFields[i];
|
||||
if (destField.value === e.target._previousValue) {
|
||||
destField.value = destField._previousValue = e.target.value;
|
||||
}
|
||||
}
|
||||
sourceField._previousValue = e.target.value;
|
||||
this.onInfoInput();
|
||||
}.bind(this));
|
||||
},
|
||||
onChangeAccountType: function (event) {
|
||||
var isComposite = event.target.value === 'imap+smtp' || event.target.value === 'pop3+smtp';
|
||||
var isImap = event.target.value === 'imap+smtp';
|
||||
if (isComposite) {
|
||||
this.compositeSection.classList.remove('collapsed');
|
||||
this.activeSyncSection.classList.add('collapsed');
|
||||
this.manualImapTitle.classList.toggle('collapsed', !isImap);
|
||||
this.manualPop3Title.classList.toggle('collapsed', isImap);
|
||||
} else {
|
||||
this.compositeSection.classList.add('collapsed');
|
||||
this.activeSyncSection.classList.remove('collapsed');
|
||||
}
|
||||
this.formItems.common.passwordWrapper.classList.toggle('collapsed', isComposite);
|
||||
this.requireFields('composite', isComposite);
|
||||
this.requireFields('smtp', isComposite);
|
||||
this.requireFields('activeSync', !isComposite);
|
||||
this.onChangeCompositeSocket({ target: this.formItems.composite.socket });
|
||||
},
|
||||
onChangeCompositeSocket: function (event) {
|
||||
var isImap = this.accountTypeNode.value === 'imap+smtp';
|
||||
var SSL_VALUE = isImap ? '993' : '995';
|
||||
var STARTTLS_VALUE = isImap ? '143' : '110';
|
||||
var socketType = event.target.value;
|
||||
if (socketType === 'PLAIN') {
|
||||
this.showPlainSocketWarning();
|
||||
}
|
||||
var portField = this.formItems.composite.port;
|
||||
if (socketType === 'SSL') {
|
||||
portField.value = SSL_VALUE;
|
||||
} else if (socketType === 'STARTTLS' || socketType === 'PLAIN') {
|
||||
portField.value = STARTTLS_VALUE;
|
||||
}
|
||||
},
|
||||
onChangeSmtpSocket: function (event) {
|
||||
const SSL_VALUE = '465';
|
||||
const STARTTLS_VALUE = '587';
|
||||
const PLAIN_VALUE = '25';
|
||||
var socketType = event.target.value;
|
||||
var portField = this.formItems.smtp.port;
|
||||
if (socketType == 'PLAIN') {
|
||||
this.showPlainSocketWarning();
|
||||
}
|
||||
if (socketType === 'SSL' && (portField.value === STARTTLS_VALUE || portField.value === PLAIN_VALUE)) {
|
||||
portField.value = SSL_VALUE;
|
||||
} else if (socketType == 'STARTTLS' && (portField.value == SSL_VALUE || portField.value == PLAIN_VALUE)) {
|
||||
portField.value = STARTTLS_VALUE;
|
||||
} else if (socketType == 'PLAIN' && (portField.value == SSL_VALUE || portField.value == STARTTLS_VALUE)) {
|
||||
portField.value = PLAIN_VALUE;
|
||||
}
|
||||
},
|
||||
requireFields: function (type, required) {
|
||||
for (var field in this.formItems[type]) {
|
||||
var item = this.formItems[type][field];
|
||||
if (!item.hasAttribute('data-maybe-required')) {
|
||||
continue;
|
||||
}
|
||||
if (required) {
|
||||
item.setAttribute('required', '');
|
||||
} else {
|
||||
item.removeAttribute('required');
|
||||
}
|
||||
}
|
||||
},
|
||||
showPlainSocketWarning: function () {
|
||||
var dialog = plainSocketWarningNode.cloneNode(true);
|
||||
var learnMoreNode = dialog.querySelector('.tng-plain-socket-learn-more');
|
||||
learnMoreNode.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
new MozActivity({
|
||||
name: 'view',
|
||||
data: {
|
||||
type: 'url',
|
||||
url: learnMoreNode.dataset.href
|
||||
}
|
||||
});
|
||||
});
|
||||
ConfirmDialog.show(dialog, {
|
||||
id: 'tng-plain-socket-warning-ok',
|
||||
handler: function () {
|
||||
}
|
||||
});
|
||||
},
|
||||
die: function () {
|
||||
this._formNavigation = null;
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue