├── .gitignore ├── BASIC_CONFIGURATION_README ├── CHANGES ├── CREDITS ├── INSTALL ├── LICENSE ├── README.md ├── RELEASE_NOTES ├── ROADMAP ├── contrib ├── README.get_mx.py.txt └── get_mx.py ├── doc ├── html │ ├── BASIC_CONFIGURATION_README.html │ ├── INSTALL.html │ ├── automx-test.1.html │ ├── automx.conf.5.html │ ├── automx_ldap.5.html │ ├── automx_script.5.html │ └── automx_sql.5.html ├── man │ ├── man1 │ │ └── automx-test.1 │ └── man5 │ │ ├── automx.conf.5 │ │ ├── automx_ldap.5 │ │ ├── automx_script.5 │ │ └── automx_sql.5 └── txt │ ├── automx-test.1.txt │ ├── automx.conf.5.txt │ ├── automx_ldap.5.txt │ ├── automx_script.5.txt │ └── automx_sql.5.txt ├── setup.py └── src ├── automx-test ├── automx ├── __init__.py ├── config.py └── view.py ├── automx_wsgi.py ├── conf ├── apache.conf.example ├── automx.conf ├── automx.conf.example-complex └── nginx-automx.conf ├── doc ├── BASIC_CONFIGURATION_README.rst ├── INSTALL.rst ├── Makefile ├── Makefile.MacOSX ├── automx-test.1.rst ├── automx.conf.5.rst ├── automx_ldap.5.rst ├── automx_script.5.rst └── automx_sql.5.rst ├── foundation-scss ├── MIT-LICENSE.txt ├── config.rb ├── humans.txt ├── index.html ├── javascripts │ ├── foundation │ │ ├── foundation.alerts.js │ │ ├── foundation.clearing.js │ │ ├── foundation.cookie.js │ │ ├── foundation.dropdown.js │ │ ├── foundation.forms.js │ │ ├── foundation.interchange.js │ │ ├── foundation.joyride.js │ │ ├── foundation.js │ │ ├── foundation.magellan.js │ │ ├── foundation.orbit.js │ │ ├── foundation.placeholder.js │ │ ├── foundation.reveal.js │ │ ├── foundation.section.js │ │ ├── foundation.tooltips.js │ │ └── foundation.topbar.js │ └── vendor │ │ ├── custom.modernizr.js │ │ ├── jquery.js │ │ └── zepto.js ├── robots.txt └── sass │ ├── _normalize.scss │ ├── _settings.scss │ └── app.scss └── html ├── css └── app.css ├── img ├── automx-banner.png └── company-banner.png ├── index.html.de ├── index.html.en └── js ├── foundation.min.js ├── foundation ├── foundation.alerts.js ├── foundation.clearing.js ├── foundation.cookie.js ├── foundation.dropdown.js ├── foundation.forms.js ├── foundation.interchange.js ├── foundation.joyride.js ├── foundation.js ├── foundation.magellan.js ├── foundation.orbit.js ├── foundation.placeholder.js ├── foundation.reveal.js ├── foundation.section.js ├── foundation.tooltips.js └── foundation.topbar.js └── vendor ├── custom.modernizr.js ├── jquery.js └── zepto.js /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .pydevproject 3 | .settings/org.eclipse.core.resources.prefs 4 | *.*~ 5 | *.swp 6 | .DS_Store 7 | src/foundation-scss/.sass-cache/ 8 | .idea 9 | -------------------------------------------------------------------------------- /BASIC_CONFIGURATION_README: -------------------------------------------------------------------------------- 1 | Basic Configuration 2 | 3 | This document contains information for initial and/or basic configuration of 4 | automx. 5 | 6 | automx reads runtime behaviour and all settings controlling a domains account 7 | provisioning from a single configuration file. By default automx expects to 8 | find this file at /etc/automx.conf. 9 | 10 | Format 11 | 12 | The general format of the automx.conf file is as follows: 13 | 14 | • The basic element contained in an INI file is the property. Every property 15 | has a name and a value, delimited by an equals sign (“=”). The name appears 16 | to the left of the equals sign. 17 | • Properties are grouped into sections. The section name appears on a line by 18 | itself, in square brackets (“[” and “]”). All properties after the section 19 | declaration are associated with that section. There is no explicit “end of 20 | section” delimiter; sections end at the next section declaration, or the 21 | end of the file. Sections may not be nested. 22 | • Section and property names are case sensitive. 23 | • A line with a number sign (“#”) begins a comment. Anything following a 24 | number sign will be ignored by automx. 25 | 26 | Sections 27 | 28 | Sections create a namespace in which properties specific to a domain are 29 | defined. The section name identifies the domain. The three section names 30 | [automx], [DEFAULT] and [global] are reserved for special purposes within 31 | automx. 32 | 33 | [automx] 34 | 35 | Controlling automx Runtime Behaviour 36 | 37 | This section is mandatory - it lists all options controlling automx runtime 38 | behaviour. The properties provider and domains are also mandatory. Usage of 39 | memcache and all of its associated properties is highly recommended. 40 | 41 | [automx] 42 | 43 | The following example shows a typical [automx] section setup: 44 | 45 | [automx] 46 | provider = example.com 1 47 | domains = * 2 48 | logfile = /var/log/automx/automx.log 3 49 | debug = yes 4 50 | memcache = 127.0.0.1:11211 5 51 | memcache_ttl = 86400 52 | client_error_limit = 5 6 53 | rate_limit_exception_networks = 127.0.0.0/8, ::1/128 7 54 | 55 | 1 56 | The provider property configures automx to identify the webservice as 57 | example.com. 58 | 2 59 | The wildcard option * used in domains instructs automx to answer any 60 | configuration request regardless of the domain sent by the mail client. 61 | 3 62 | All log information should go to /var/log/automx/automx.log. 63 | 4 64 | Debugging is enabled and infos will be sent to logfile. 65 | 5 66 | Statistical data controlling errors caused by clients accessing database 67 | backends will be sent to the specified memcache service. 68 | 6 69 | In this example a client may not cause more than 5 errors before automx 70 | will refuse to answer further queries. 71 | 7 72 | Clients listed in rate_limit_exception_networks are excluded from rate 73 | limiting. 74 | 75 | [DEFAULT] 76 | 77 | Properties present in all other sections 78 | 79 | This section is optional. Settings in this section define properties which will 80 | be present in all other sections. It is useful to avoid redundancy. 81 | 82 | [DEFAULT] 83 | 84 | The following example shows a typical [DEFAULT] section setup: 85 | 86 | [DEFAULT] 87 | action = settings 1 88 | account_type = email 2 89 | account_name = Example Inc. 3 90 | account_name_short = Example 4 91 | 92 | 1 93 | 94 | The default action for automx is to provide account settings. 95 | 96 | Note 97 | 98 | The Microsoft schema forsees other actions that account provisioning. 99 | 100 | 2 101 | The account_type should be an email account. 102 | 3 103 | The account should show up as Example Inc. in the clients account list. 104 | 4 105 | The accounts short name should be Example. 106 | 107 | [global] 108 | 109 | A global backend 110 | 111 | Setting this section is mandatory, but it may remain empty. It provides a 112 | backend, which will be used whenever automx should serve a domain, but no 113 | section with domain-specific settings has been specified. 114 | 115 | Other sections may either explicitly or implicitly refer to the [global] 116 | section as a whole. An explicit reference specifies global as backend property. 117 | Implicit references simply announce the domain in automx' domains list and omit 118 | an explicit section definition for that domain. 119 | 120 | Note 121 | 122 | This is useful when many domains should use the same backend or when automx 123 | domain property configures it to run as wildcard service. 124 | 125 | [global] 126 | 127 | The following example configures automx to query a LDAP directory service. 128 | Refer to automx_ldap(5) for a detailed discussion of parameters and their 129 | meaning: 130 | 131 | [global] 132 | backend = ldap 133 | 134 | account_name = ${cn} (Example Inc.) 135 | display_name = ${givenName} ${sn} 136 | 137 | smtp = yes 138 | smtp_server = mail.example.com 139 | smtp_port = 587 140 | smtp_encryption = starttls 141 | smtp_auth = plaintext 142 | smtp_auth_identity = ${mail} 143 | smtp_expiration_date = 2012-12-31 144 | smtp_refresh_ttl = 0 145 | smtp_default = yes 146 | 147 | imap = yes 148 | imap_server = mail.example.com 149 | imap_port = 993 150 | imap_encryption = ssl 151 | imap_auth = plaintext 152 | imap_auth_identity = ${mail} 153 | imap_expiration_date = 2012-12-31 154 | imap_refresh_ttl = 0 155 | 156 | pop = no 157 | pop_server = mail.example.com 158 | pop_port = 995 159 | pop_encryption = ssl 160 | pop_auth = plaintext 161 | pop_auth_identity = ${mail} 162 | pop_expiration_date = 2012-12-31 163 | pop_refresh_ttl = 0 164 | 165 | host = ldap://ldap.example.com 166 | base = ou=people,dc=example,dc=com 167 | result_attrs = mail, cn, givenName, sn 168 | scope = sub 169 | filter = (&(objectClass=*)(uniqueIdentifier=%s)) 170 | 171 | bindmethod = sasl 172 | saslmech = EXTERNAL 173 | usetls = yes 174 | reqcert = demand 175 | cert = /etc/ssl/certs/mail.example.com.crt.pem 176 | key = /etc/ssl/private/mail.example.com.key.pem 177 | cacert = /etc/ssl/certs/ca-certificates.crt 178 | 179 | Authors 180 | 181 | Christian Rößner 182 | Wrote the program. 183 | Patrick Ben Koetter 184 | Wrote the documentation. 185 | 186 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | * Version 1.1.2 2 | --------------------------------- 3 | - Added support for cal/carddav 4 | - Added support for eMClient for OX 5 | 6 | * Version 1.1.1 7 | --------------------------------- 8 | - Added service_domain_required option for Outlook 9 | - Added service_domain_name option for Outlook 10 | 11 | * Version 1.1.0 12 | --------------------------------- 13 | - Include code changes from never-released 0.10.3 14 | - Make code Python2.7 up to Python3.5 compatible 15 | 16 | * Version 0.10.3 17 | --------------------------------- 18 | - Replace macros in *_server varaibles 19 | - Making service sections dynamic 20 | - Documentation improvements 21 | 22 | * Version 0.10.2 23 | --------------------------------- 24 | - Several minor bug fixes 25 | - Drop memcache dependencies 26 | 27 | * Version 0.10.0 28 | --------------------------------- 29 | - Several minor bug fixes 30 | 31 | * Version 0.10.0 32 | --------------------------------- 33 | - NEW: Implemented mobileconfig support 34 | - Minor bug fixes 35 | 36 | * Version 0.9.2 - ?? 37 | --------------------------------- 38 | - Bug fixes 39 | 40 | * Version 0.9.1 - unreleased 41 | --------------------------------- 42 | - Documentation updates 43 | 44 | * Version 0.9 - 2012/10/12 45 | --------------------------------- 46 | - NEW: Added ActiveSync MobileSync support 47 | 48 | * Version 0.9_beta2 - 2012/04/18 49 | --------------------------------- 50 | - NEW: Result modifiers for dynamic backends 51 | 52 | * Version 0.9_beta1 - 2012/04/04 53 | --------------------------------- 54 | - NEW: Denial-of-Service-Attack protection 55 | - NEW: Multiple service profiles 56 | - NEW: Centralized logging 57 | - Code cleanup 58 | - Improved exception handling 59 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | Repository, automx-test and Build Management 2 | Marc Schiffbauer 3 | Maintainer and programming 4 | Christian Roessner 5 | Website, Documentation and Idea 6 | Patrick Ben Koetter 7 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | INSTALL 2 | 3 | This document contains step by step instructions to install automx. 4 | 5 | automx Download 6 | 7 | If you haven't done so yet get automx from its website: 8 | 9 | https://github.com/sys4/automx/releases/latest 10 | 11 | Download the latest release, Unpack the tar archive and change into the newly 12 | created directory: 13 | 14 | $ tar xzf vVERSION.tar.gz 15 | $ cd automx-VERSION 16 | 17 | Software Requirements 18 | 19 | automx is a Python application. You must install a few extra modules to handle 20 | frontend and backend communication as well to deal with XML data. 21 | 22 | mod_wsgi 23 | Install mod_wsgi for the Apache web server and the python-wsgi module. 24 | ldap, python-sqlalchemy (optional) 25 | If you plan to use either LDAP or SQL as backends to retrieve configuration 26 | options from install ldap for LDAP and python-sqlalchemy for SQL. Further 27 | you also need to install the SQL backend driver that communicates with your 28 | database. For MySQL this might be mysqldb. 29 | dateutil, ipaddr, lxml 30 | Install the python-packages dateutil, ipaddr, lxml and memcache. Otherwise 31 | automx will not be able to handle the XML data it needs to deal with. 32 | memcached 33 | If you want to rate limit connections to automx, install a memcached server. 34 | M2Crypto (mobileconfig only) 35 | Mobileconfig profiles may be signed with your webservers cert and key. You 36 | need to install M2Crypto, which does the S/MIME-signing. 37 | 38 | Once you've satisfied the requirements, you can start to install automx. 39 | 40 | Installing automx 41 | 42 | automx is a wsgi script depending on some automx-specific libraries. It reads 43 | its configuration from a single configuration file. The program, the libraries 44 | and the configuration file need to be installed at different locations. 45 | 46 | Installing the program 47 | 48 | Create a directory for the automx program and copy it to that location: 49 | 50 | $ mkdir -p /usr/lib/automx 51 | $ cp automx-VERSION/src/automx_wsgi.py /usr/lib/automx/ 52 | 53 | Installing the test program 54 | 55 | Copy the automx-test program to a location that is in your $PATH: 56 | 57 | $ cp automx-VERSION/src/automx-test /usr/bin/automx-test 58 | 59 | Installing automx-specific libraries 60 | 61 | Python loads packages from various locations depending on your distribution and 62 | python version. To correctly determine the used paths please type the following 63 | commands: 64 | 65 | $ python 66 | >>> import sys 67 | >>> sys.path 68 | >>> (CTRL+D) 69 | 70 | You'll get a list of used paths. Please remember the first shown path entry 71 | (for example '/usr/lib/python2.7') - this is the best location for placing the 72 | automx-directory: 73 | 74 | $ cp -r automx-VERSION/src/automx /usr/lib/pythonVERSION 75 | 76 | Installing man Pages 77 | 78 | Try using the manpath command to find out where man pages are stored on your 79 | computer: 80 | 81 | $ manpath /usr/local/man:/usr/local/share/man:/usr/share/man 82 | 83 | Copy the man pages to that location: 84 | 85 | $ cp -a automx-VERSION/doc/man/ /usr/share/man 86 | 87 | Installing the configuration file 88 | 89 | Place the sample automx.conf file into /etc: 90 | 91 | $ cp automx-VERSION/src/conf/automx.conf /etc/ 92 | 93 | Follow automx.conf(5) Adopt this configuration file to your needs. You may find 94 | detailed information in the man page automx.conf(5). 95 | 96 | Tip 97 | 98 | Set debug=yes in the section automx while you setup, configure and test automx. 99 | It will help you detect problems more easily. This will log the request GET/ 100 | POST and the response to the error.log file(s). 101 | 102 | DNS Configuration 103 | 104 | Mail clients seeking mail account autoconfiguration will either request an IP 105 | address for autoconfig.example.com (Mozilla schema) or autodiscover.example.com 106 | (Microsoft schema). Provide settings in your DNS that directs them to the 107 | server running the automx service: 108 | 109 | autoconfig.example.com. IN A 192.168.2.1 110 | autodiscover.example.com. IN A 192.168.2.1 111 | 112 | Note 113 | 114 | If you install automx on an existing host, which has it's own domain-name, then 115 | it is also possible to use above entries as nicknames: 116 | 117 | somehost.example.com. IN A 192.168.2.1 118 | autoconfig IN CNAME somehost.example.com. 119 | autodiscover IN CNAME somehost.example.com. 120 | 121 | Web Server Configuration 122 | 123 | Finally configure the web server. It will accept configuration requests from 124 | mail clients, pass the information to automx and in turn will respond with 125 | account profiles once automx has figured out the details. 126 | 127 | First enable the wsgi module. Follow your OS documentation to find out how it 128 | needs to be done. (e.g. 'a2enmod wsgi' for Apache on Debian) 129 | 130 | automx is able to provision mail clients following the Mozilla autoconfig 131 | schema as well as mail clients following the Microsoft autodiscover schema. 132 | Both schemas have different requirements regarding hostname, port and level of 133 | security when a request is sent to the configuration server: 134 | 135 | Microsoft 136 | Mail clients following the Microsoft autodiscover schema require a https 137 | connection. The web server must identify itself as autodiscover.example.com 138 | on port 443 and it must use a valid server certificate that is trusted by 139 | the mail client requesting configuration. 140 | Mozilla 141 | Mail clients following the Mozilla autoconfig schema can use either a http 142 | or a https connection. The web server must identify itself as 143 | autoconfig.example.com on port 80 or 443. If it connects on 443 a valid 144 | server certificate that is trusted by the mail client requesting 145 | configuration has to be used. 146 | 147 | To provision Apple iOS devices or Mac OS X Mail, you need to place the file 148 | automx.html somewhere in your document root of your webserver. After that you 149 | can use your iOS device and open the Safari browser calling this website. After 150 | entering the form data, you will receive a mobileconfig file and the device 151 | switches to the settings assistent. On Mac OS X, you also can call this 152 | document and save it to disk. After opening it, the profile manager opens and 153 | the steps are similar to iOS. For signed profiles see the man page automx.conf 154 | (5). 155 | 156 | Here is a simple example that configures an autoconfig and an autodiscover 157 | service (both use the same automx script). You need to copy & paste this lines 158 | into your existing website configuration files (for Debian take a look in /etc/ 159 | apache2/sites-enabled/...): 160 | 161 | 162 | ServerName example.com 163 | ServerAlias autoconfig.example.com 164 | ServerAdmin webmaster@example.com 165 | 166 | WSGIScriptAliasMatch \ 167 | (?i)^/.+/(autodiscover|config-v1.1).xml \ 168 | /usr/lib/automx/automx_wsgi.py 169 | 170 | Order allow,deny 171 | Allow from all 172 | 173 | 174 | 175 | 176 | 177 | ServerName example.com:443 178 | ServerAlias autodiscover.example.com:443 179 | ServerAdmin webmaster@example.com 180 | 181 | WSGIScriptAliasMatch \ 182 | (?i)^/.+/(autodiscover|config-v1.1).xml \ 183 | /usr/lib/automx/automx_wsgi.py 184 | WSGIScriptAlias \ 185 | /mobileconfig \ 186 | /usr/lib/automx/automx_wsgi.py 187 | 188 | Order allow,deny 189 | Allow from all 190 | 191 | 192 | 193 | 194 | Note 195 | 196 | If you haven't done so, you also need to configure and enable SSL in your 197 | apache-configuration. At least that means enabling the default SSL-site, 198 | install (self signed) certificates and activating the ssl-support (e.g. 199 | 'a2enmod ssl' for Apache on debian). Don't forget to restart your web-server 200 | afterwards! You need also to ajust the paths to automx_wsgi.py in the example 201 | above. 202 | 203 | Note 204 | 205 | For Nginx see the example configuration file nginx-automx.conf. You can place 206 | this file into /etc/nginx/conf.d (this depends on your distribution) and adopt 207 | it to your needs. 208 | 209 | Note 210 | 211 | ISPs 212 | 213 | In an advanced environment with thousands of domains, you can redirect mail 214 | clients via DNS entries to your ISP automx provisioning server for Microsoft 215 | clients and a web server instance with a wild card ServerName to serve the 216 | Mozilla schema. 217 | 218 | Add this to your DNS-configuation: 219 | 220 | *.example.com. A 192.168.2.1 221 | 222 | and this to your virtualhost-definition in your webserver-configuration: 223 | 224 | ServerAlias *.example.com 225 | 226 | automx comes with a little utility that helps testing proper operation. The 227 | next section explains how to use it. 228 | 229 | Testing And Debugging automx 230 | 231 | The automx-test utility sends configuration requests for Microsoft and Mozilla 232 | clients to the web server: 233 | 234 | $ automx-test user@example.com 235 | 236 | The domainpart in the address determines the list of hostnames that will be 237 | queried. In this example autoconfig.example.com and autodiscover.example.com 238 | will be contacted. 239 | 240 | You should see the web server header. The script will say Success or Failed. 241 | 242 | If things go wrong, the error.log is your friend. It will indicate 243 | configuration issues, if python modules are missing, if your database can not 244 | be queried or anything else that might go wrong. If you also enabled debug in / 245 | etc/automx.conf, you will find further information in your automx.log file. 246 | Please turn on debug, if you want to send us a bug report. PLEASE NOTICE! 247 | Mobileconfig will display a users password in cleartext! So please remove that 248 | from bug reports first! 249 | 250 | Note 251 | 252 | If you split error logs by port, e.g. port 80 and 443, you need to check both. 253 | Autoconfig requests will mostly show up in the port 80 error.log, whereas 254 | autodiscover will only show up in your 443 error.log. 255 | 256 | Authors 257 | 258 | Christian Roessner 259 | Wrote the program. 260 | Patrick Ben Koetter 261 | Wrote the documentation. 262 | Christian Sudec 263 | 04-22-2013: Updated the documentation to support automx 0.9.2 264 | 265 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project has been deprecated in favor of automx2. 2 | You can find automx2 on GitHub: https://github.com/rseichter/automx2 3 | -------------------------------------------------------------------------------- /RELEASE_NOTES: -------------------------------------------------------------------------------- 1 | Version 1.1.0 to 1.1.1: 2 | 3 | Added some Outlook options 4 | 5 | Version 0.10 to 1.1.0: 6 | 7 | Many smaller bugs have been fixed. M2Crypto was removed in favor of an 8 | external call to OpenSSL. 9 | 10 | Major changes are that this release is the first release that works for 11 | Python 2.7 up to Python 3.5. Please see also the installation instructions. 12 | For Python 3.x you should replace python-ldap with pyldap, which is a drop-in 13 | replacement that is compatible with Python 2 and Python 3. 14 | 15 | The master branch had a interim version of 0.10.3, which was primarily a bug 16 | fix release. As automx itself was mostly feature complete, it would had 17 | become the version 1.0.0. Because porting it to Py2/3, the version directly 18 | jumps to 1.1.0 19 | 20 | Version 0.9.x to 0.10: 21 | 22 | Automx does support mobileconfig profiles for Apple iOS devices as well as 23 | Mac OS X Mail. The profiles are optionally signed. 24 | 25 | The backend "file" was expanded and does support the new option mobileconfig, 26 | which may point to a mobileconfig file (it is the equivalent to the 27 | autoconfig/autodiscover option of that specific backend). 28 | 29 | Version 0.8 to 0.9: 30 | 31 | Failure Counter (Denial-of-Service-Attack protection) 32 | A failure counter tracks bad (email address) requests. The counter serves to 33 | protect automx from Denial-of-Service-Attacks. 34 | 35 | An attacker might try to iterate a list of email addresses against automx to 36 | see, if the server returns valid results or 500 temp errors. This feature is 37 | expected to be especially useful for automx setups using database driven 38 | backends. automx will stop querying the database once the border limit 39 | has been reached. 40 | 41 | The protection is built using memcached, as it allows a central instance to 42 | collect for multiple automx processes. 43 | 44 | Multi Service Profiles 45 | Multi service profile support allows to serve more than one setting for the 46 | same service within a profile. 47 | 48 | For this we added several new backends and a new option that allows automx to 49 | follow any further backends. These backends may either substitute the former 50 | settings or add new information. 51 | 52 | 53 | Improved Logging 54 | As we made some changes to the code, we realized that it important to change 55 | logging behavior. So we now log all automx messages to an extra file and also 56 | the debugging output does give us much more information as the version 0.8. 57 | 58 | 59 | Code Cleanups 60 | Many changes have been done inside the code itself. Code cleanups and a better 61 | exception handling for situations, where no data could be retrieved from 62 | databases. Also the main file now is much cleaner, as we introduced some 63 | constant variables for error codes. 64 | 65 | # vim: set tw=80: 66 | -------------------------------------------------------------------------------- /ROADMAP: -------------------------------------------------------------------------------- 1 | Version 1.1.0 to 1.2.0: 2 | 3 | * We may implement a web services backend. 4 | 5 | * We may implement further iOS options for CalDAV and CardDAV and similar. 6 | 7 | * Maybe we will include mutt support. 8 | 9 | * Proxy for Autodiscover. 10 | 11 | No further plans at this time of writing. 12 | -------------------------------------------------------------------------------- /contrib/README.get_mx.py.txt: -------------------------------------------------------------------------------- 1 | Thanks to: .webflow GmbH 2 | 3 | On our mailserver it´s necessary that automx dynamically detects the MX-Record from the requested domain to return the perfect configuration. 4 | I´ve build a small automx-script for this problem. 5 | 6 | The script takes the first argument and detects the primary MX-Record. 7 | The second argument is used as fallback if the MX detection gets an timeout or other exception. 8 | 9 | You need the dnspython module for this script to run: 10 | 11 | pip install dnspython 12 | -------------------------------------------------------------------------------- /contrib/get_mx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import dns.resolver 4 | import re 5 | import sys 6 | 7 | domain = str(sys.argv[1]) 8 | 9 | try: 10 | mx_objects = dns.resolver.query(domain,'MX') 11 | except: 12 | print str(sys.argv[2]) 13 | sys.exit(0) 14 | 15 | mx_server = {} 16 | 17 | for server in mx_objects: 18 | mx_server[server.preference] = server.exchange 19 | 20 | primary_mx_server = re.sub('\.$', '', mx_server[sorted(mx_server)[0]].to_text()) 21 | 22 | print primary_mx_server 23 | 24 | -------------------------------------------------------------------------------- /doc/html/automx-test.1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | automx-test 8 | 9 | 10 | 326 | 327 | 328 |
329 |

automx-test

330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 |
Date:02/08/2013
Subtitle:automx command line client
Manual Section:1
Manual Group:automx
Copyright:This document has been placed in the public domain.
346 |
347 |

Synopsis

348 |

automx-test localpart@domainpart

349 |
350 |
351 |

Description

352 |

The automx-test command line client requests autoconfiguration data in Mozilla and Microsoft schema requests and outputs the server response to STDOUT.

353 |
354 |
355 |

Authors

356 |
357 |
Christian Roessner <cr@sys4.de>
358 |
Wrote the program.
359 |
Patrick Ben Koetter <p@sys4.de>
360 |
Wrote the documentation.
361 |
362 |
363 | 367 |
368 | 369 | 370 | -------------------------------------------------------------------------------- /doc/html/automx_script.5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | automx_script 8 | 9 | 10 | 326 | 327 | 328 |
329 |

automx_script

330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 |
Date:02/08/2013
Subtitle:automx script backend configuration parameters
Manual Section:5
Manual Group:automx
Copyright:This document has been placed in the public domain.
346 |
347 |

Description

348 |

The automx_sript(5) man page specifies all parameters that control access from within automx to a script backend.

349 |
350 |
351 |

Parameters

352 |
353 |
result_attrs (default: none)
354 |

Specifies a list of one or more variables. The output of the external script will assign values to these variables. Results will be assigned in the same order as variables have been listed:

355 |
356 | result_attrs = server, email, auth, socket
357 | 
358 |
359 |
360 |
361 |

Important

362 |

automx_script(5) does not check if the number of values returned matches the ones expected. Too many return values will be discarded. If the script returns too few automx_script(5) will use the corresponding result attribute name.

363 |
364 |
365 |
script (default: none)
366 |

Specifies the absolute path to the script which should be run by the backend automx_script(5) backend:

367 |
368 | script = /usr/local/bin/example_com.sh "%s"
369 | 
370 |
371 |
separator (default: whitespace)
372 |

Specifies the character that separates values returned by the external script:

373 |
374 | separator = ,
375 | 
376 |
377 |
378 |
379 |
380 |

Authors

381 |
382 |
Christian Roessner <cr@sys4.de>
383 |
Wrote the program.
384 |
Michael Menge
385 |
Wrote and contributed this backend.
386 |
Patrick Ben Koetter <p@sys4.de>
387 |
Wrote the documentation.
388 |
389 |
390 | 394 |
395 | 396 | 397 | -------------------------------------------------------------------------------- /doc/man/man1/automx-test.1: -------------------------------------------------------------------------------- 1 | .\" Man page generated from reStructuredText. 2 | . 3 | .TH AUTOMX-TEST 1 "02/08/2013" "" "automx" 4 | .SH NAME 5 | automx-test \- automx command line client 6 | . 7 | .nr rst2man-indent-level 0 8 | . 9 | .de1 rstReportMargin 10 | \\$1 \\n[an-margin] 11 | level \\n[rst2man-indent-level] 12 | level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] 13 | - 14 | \\n[rst2man-indent0] 15 | \\n[rst2man-indent1] 16 | \\n[rst2man-indent2] 17 | .. 18 | .de1 INDENT 19 | .\" .rstReportMargin pre: 20 | . RS \\$1 21 | . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] 22 | . nr rst2man-indent-level +1 23 | .\" .rstReportMargin post: 24 | .. 25 | .de UNINDENT 26 | . RE 27 | .\" indent \\n[an-margin] 28 | .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] 29 | .nr rst2man-indent-level -1 30 | .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] 31 | .in \\n[rst2man-indent\\n[rst2man-indent-level]]u 32 | .. 33 | .SH SYNOPSIS 34 | .sp 35 | automx\-test \fIlocalpart@domainpart\fP 36 | .SH DESCRIPTION 37 | .sp 38 | The \fBautomx\-test\fP command line client requests autoconfiguration data in Mozilla and Microsoft schema requests and outputs the server response to \fBSTDOUT\fP. 39 | .SH AUTHORS 40 | .INDENT 0.0 41 | .TP 42 | .B Christian Roessner <\fI\%cr@sys4.de\fP> 43 | Wrote the program. 44 | .TP 45 | .B Patrick Ben Koetter <\fI\%p@sys4.de\fP> 46 | Wrote the documentation. 47 | .UNINDENT 48 | .SH SEE ALSO 49 | .sp 50 | \fI\%automx(8)\fP, \fI\%automx.conf(5)\fP, \fI\%automx_ldap(5)\fP, \fI\%automx_script(5)\fP, \fI\%automx_sql(5)\fP, \fI\%automx\-test(1)\fP 51 | .SH COPYRIGHT 52 | This document has been placed in the public domain. 53 | .\" Generated by docutils manpage writer. 54 | . 55 | -------------------------------------------------------------------------------- /doc/man/man5/automx_ldap.5: -------------------------------------------------------------------------------- 1 | .\" Man page generated from reStructuredText. 2 | . 3 | .TH AUTOMX_LDAP 5 "02/08/2013" "" "automx" 4 | .SH NAME 5 | automx_ldap \- automx LDAP backend configuration parameters 6 | . 7 | .nr rst2man-indent-level 0 8 | . 9 | .de1 rstReportMargin 10 | \\$1 \\n[an-margin] 11 | level \\n[rst2man-indent-level] 12 | level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] 13 | - 14 | \\n[rst2man-indent0] 15 | \\n[rst2man-indent1] 16 | \\n[rst2man-indent2] 17 | .. 18 | .de1 INDENT 19 | .\" .rstReportMargin pre: 20 | . RS \\$1 21 | . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] 22 | . nr rst2man-indent-level +1 23 | .\" .rstReportMargin post: 24 | .. 25 | .de UNINDENT 26 | . RE 27 | .\" indent \\n[an-margin] 28 | .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] 29 | .nr rst2man-indent-level -1 30 | .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] 31 | .in \\n[rst2man-indent\\n[rst2man-indent-level]]u 32 | .. 33 | .SH DESCRIPTION 34 | .sp 35 | The automx_ldap(5) man page specifies all parameters that control access from 36 | within automx to a LDAP backend. 37 | .SH PARAMETERS 38 | .INDENT 0.0 39 | .TP 40 | .B authzid (no default) 41 | Specifies the SASL proxy authorization identity. 42 | .TP 43 | .B base (default: none) 44 | Specifies the default base DN to use when performing ldap operations. The base must be specified as a Distinguished Name in LDAP format. 45 | .TP 46 | .B binddn (default: none) 47 | Specifies the default bind DN to use when performing ldap operations. The bind DN must be specified as a Distinguished Name in LDAP format. 48 | .TP 49 | .B bindmethod (default: simple) 50 | Specifies how authentication should take place. Valid options are either simple for a simple bind or sasl for a bind that requires SASL authentication. 51 | .TP 52 | .B bindpw (default: none) 53 | Specifies the password used when binddn identifies itself with the LDAP server. 54 | .TP 55 | .B cacert (default: none) 56 | Specifies the path to a file that contains all certificates of Certification Authorities automx should trust. 57 | .TP 58 | .B cert (default: none) 59 | Specifies the path to a file that contains automx\(aqs certificate. 60 | .TP 61 | .B cipher (default: TLSv1) 62 | See ciphers(1) for a list of valid options. 63 | .TP 64 | .B filter (default: (objectClass=*)) 65 | Specifies the search filter to select appropriate LDAP objects. The filter should conform to the string representation for search filters as defined in RFC 4515. 66 | .sp 67 | \fBNOTE:\fP 68 | .INDENT 7.0 69 | .INDENT 3.5 70 | See the section “Macros and Variables” in automx.conf(5) for a list of available query macros. 71 | .UNINDENT 72 | .UNINDENT 73 | .TP 74 | .B host (default: \fI\%ldap://127.0.0.1/\fP) 75 | Specifies one or more LDAP servers separated by commas as shown in the following example: 76 | .INDENT 7.0 77 | .INDENT 3.5 78 | .sp 79 | .nf 80 | .ft C 81 | host = ldap://127.0.0.1, ldap://192.168.2.1 82 | .ft P 83 | .fi 84 | .UNINDENT 85 | .UNINDENT 86 | .sp 87 | \fBIMPORTANT:\fP 88 | .INDENT 7.0 89 | .INDENT 3.5 90 | Subsequent servers to the first serve only for fallback purposes, i.e. a server to the right will only be queried if the server left to it cannot be reached. If a server can be reached no further attempts will be made regardless if the query returned a result or not. 91 | .UNINDENT 92 | .UNINDENT 93 | .TP 94 | .B key (default: none) 95 | Specifies the path to a file that contains automx\(aqs private key, which matches automx certificate given with cert. 96 | .TP 97 | .B reqcert (default: never) 98 | Specifies what checks to perform on server certificates in a TLS session, if any. The can be specified as one of the following keywords: 99 | .INDENT 7.0 100 | .TP 101 | .B never 102 | The client will not request or check any server certificate. This is the default setting. 103 | .TP 104 | .B allow 105 | The server certificate is requested. If no certificate is provided, the session proceeds normally. If a bad certificate is provided, it will be ignored and the session proceeds normally. 106 | .TP 107 | .B try 108 | The server certificate is requested. If no certificate is provided, the session proceeds normally. If a bad certificate is provided, the session is immediately terminated. 109 | .TP 110 | .B demand 111 | These keywords are equivalent. The server certificate is requested. If no certificate is provided, or a bad certificate is provided, the session is immediately terminated. 112 | .UNINDENT 113 | .TP 114 | .B result_attrs (default: none) 115 | If automx finds one or more entries, the attributes specified by result_attrs are returned. If * is listed, all user attributes are returned. 116 | .TP 117 | .B saslmech (default: none) 118 | Specifies the SASL mechanism to be used for authentication. 119 | .INDENT 7.0 120 | .TP 121 | .B cram\-md5 122 | The SASL cram\-md5 mechanism (see: RFC 2195) will be used to authenticate LDAP bind requests. 123 | .TP 124 | .B digest\-md5 125 | The SASL digest\-md5 mechanism (see: RFC 2831) will be used to authenticate LDAP bind requests. 126 | .TP 127 | .B external 128 | The SASL external mechanism (see: RFC 4422) will be used to authenticate LDAP bind requests. 129 | .TP 130 | .B gssapi 131 | The SASL gssapi mechanism (see: RFC 4752) will be used to authenticate LDAP bind requests. 132 | .TP 133 | .B none 134 | No SASL mechanism will be use to authenticate LDAP bind requests. 135 | .UNINDENT 136 | .TP 137 | .B scope (default: sub) 138 | Specify the scope of the search to be one of base (or exact), one (or onelevel), sub (or substree), to specify a base object, one\-level, or subtree search. 139 | .TP 140 | .B usetls (default: false) 141 | Specifies if automx should use TLS when it connects to the LDAP host. 142 | .UNINDENT 143 | .SH AUTHORS 144 | .INDENT 0.0 145 | .TP 146 | .B Christian Roessner <\fI\%cr@sys4.de\fP> 147 | Wrote the program. 148 | .TP 149 | .B Patrick Ben Koetter <\fI\%p@sys4.de\fP> 150 | Wrote the documentation. 151 | .UNINDENT 152 | .SH SEE ALSO 153 | .sp 154 | \fI\%automx(8)\fP, \fI\%automx.conf(5)\fP, \fI\%automx_ldap(5)\fP, \fI\%automx_script(5)\fP, \fI\%automx_sql(5)\fP, \fI\%automx\-test(1)\fP 155 | .SH COPYRIGHT 156 | This document has been placed in the public domain. 157 | .\" Generated by docutils manpage writer. 158 | . 159 | -------------------------------------------------------------------------------- /doc/man/man5/automx_script.5: -------------------------------------------------------------------------------- 1 | .\" Man page generated from reStructuredText. 2 | . 3 | .TH AUTOMX_SCRIPT 5 "02/08/2013" "" "automx" 4 | .SH NAME 5 | automx_script \- automx script backend configuration parameters 6 | . 7 | .nr rst2man-indent-level 0 8 | . 9 | .de1 rstReportMargin 10 | \\$1 \\n[an-margin] 11 | level \\n[rst2man-indent-level] 12 | level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] 13 | - 14 | \\n[rst2man-indent0] 15 | \\n[rst2man-indent1] 16 | \\n[rst2man-indent2] 17 | .. 18 | .de1 INDENT 19 | .\" .rstReportMargin pre: 20 | . RS \\$1 21 | . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] 22 | . nr rst2man-indent-level +1 23 | .\" .rstReportMargin post: 24 | .. 25 | .de UNINDENT 26 | . RE 27 | .\" indent \\n[an-margin] 28 | .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] 29 | .nr rst2man-indent-level -1 30 | .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] 31 | .in \\n[rst2man-indent\\n[rst2man-indent-level]]u 32 | .. 33 | .SH DESCRIPTION 34 | .sp 35 | The automx_sript(5) man page specifies all parameters that control access from within automx to a script backend. 36 | .SH PARAMETERS 37 | .INDENT 0.0 38 | .TP 39 | .B result_attrs (default: none) 40 | Specifies a list of one or more variables. The output of the external script will assign values to these variables. Results will be assigned in the same order as variables have been listed: 41 | .INDENT 7.0 42 | .INDENT 3.5 43 | .sp 44 | .nf 45 | .ft C 46 | result_attrs = server, email, auth, socket 47 | .ft P 48 | .fi 49 | .UNINDENT 50 | .UNINDENT 51 | .UNINDENT 52 | .sp 53 | \fBIMPORTANT:\fP 54 | .INDENT 0.0 55 | .INDENT 3.5 56 | automx_script(5) does not check if the number of values returned matches the ones expected. Too many return values will be discarded. If the script returns too few automx_script(5) will use the corresponding result attribute name. 57 | .UNINDENT 58 | .UNINDENT 59 | .INDENT 0.0 60 | .TP 61 | .B script (default: none) 62 | Specifies the absolute path to the script which should be run by the backend automx_script(5) backend: 63 | .INDENT 7.0 64 | .INDENT 3.5 65 | .sp 66 | .nf 67 | .ft C 68 | script = /usr/local/bin/example_com.sh "%s" 69 | .ft P 70 | .fi 71 | .UNINDENT 72 | .UNINDENT 73 | .TP 74 | .B separator (default: whitespace) 75 | Specifies the character that separates values returned by the external script: 76 | .INDENT 7.0 77 | .INDENT 3.5 78 | .sp 79 | .nf 80 | .ft C 81 | separator = , 82 | .ft P 83 | .fi 84 | .UNINDENT 85 | .UNINDENT 86 | .UNINDENT 87 | .SH AUTHORS 88 | .INDENT 0.0 89 | .TP 90 | .B Christian Roessner <\fI\%cr@sys4.de\fP> 91 | Wrote the program. 92 | .TP 93 | .B Michael Menge 94 | Wrote and contributed this backend. 95 | .TP 96 | .B Patrick Ben Koetter <\fI\%p@sys4.de\fP> 97 | Wrote the documentation. 98 | .UNINDENT 99 | .SH SEE ALSO 100 | .sp 101 | \fI\%automx(8)\fP, \fI\%automx.conf(5)\fP, \fI\%automx_ldap(5)\fP, \fI\%automx_script(5)\fP, \fI\%automx_sql(5)\fP, \fI\%automx\-test(1)\fP 102 | .SH COPYRIGHT 103 | This document has been placed in the public domain. 104 | .\" Generated by docutils manpage writer. 105 | . 106 | -------------------------------------------------------------------------------- /doc/man/man5/automx_sql.5: -------------------------------------------------------------------------------- 1 | .\" Man page generated from reStructuredText. 2 | . 3 | .TH AUTOMX_SQL 5 "02/08/2013" "" "automx" 4 | .SH NAME 5 | automx_sql \- automx SQL backend configuration parameters 6 | . 7 | .nr rst2man-indent-level 0 8 | . 9 | .de1 rstReportMargin 10 | \\$1 \\n[an-margin] 11 | level \\n[rst2man-indent-level] 12 | level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] 13 | - 14 | \\n[rst2man-indent0] 15 | \\n[rst2man-indent1] 16 | \\n[rst2man-indent2] 17 | .. 18 | .de1 INDENT 19 | .\" .rstReportMargin pre: 20 | . RS \\$1 21 | . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] 22 | . nr rst2man-indent-level +1 23 | .\" .rstReportMargin post: 24 | .. 25 | .de UNINDENT 26 | . RE 27 | .\" indent \\n[an-margin] 28 | .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] 29 | .nr rst2man-indent-level -1 30 | .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] 31 | .in \\n[rst2man-indent\\n[rst2man-indent-level]]u 32 | .. 33 | .SH DESCRIPTION 34 | .sp 35 | The automx_sql(5) man page specifies all parameters that control access from within automx to a SQL backend. 36 | .SH PARAMETERS 37 | .INDENT 0.0 38 | .TP 39 | .B host (default: none) 40 | Specifies one or more SQL servers separated by commas. Each server specification must provide database driver, username and password to access a database on a host as shown in the following example: 41 | .INDENT 7.0 42 | .INDENT 3.5 43 | .sp 44 | .nf 45 | .ft C 46 | host = driver://username:password@hostname/database 47 | .ft P 48 | .fi 49 | .UNINDENT 50 | .UNINDENT 51 | .sp 52 | \fBIMPORTANT:\fP 53 | .INDENT 7.0 54 | .INDENT 3.5 55 | Subsequent servers to the first serve only for fallback purposes, i.e. a server to the right will only be queried if the server left to it cannot be reached. If a server can be reached no further attempts will be made regardless if the query returned a result or not. 56 | .UNINDENT 57 | .UNINDENT 58 | .TP 59 | .B query (default: none) 60 | Specifies the query that should be sent to the database specified with the host parameter: 61 | .INDENT 7.0 62 | .INDENT 3.5 63 | query = SELECT displayname, mailaddr FROM mail WHERE mailaddr=\(aq%s\(aq; 64 | .UNINDENT 65 | .UNINDENT 66 | .sp 67 | \fBNOTE:\fP 68 | .INDENT 7.0 69 | .INDENT 3.5 70 | See the section called “Macros and Variables” in automx.conf(5) for a list of available query macros. 71 | .UNINDENT 72 | .UNINDENT 73 | .TP 74 | .B result_attrs (default: none) 75 | Specifies the attributes whose values should be used in an automx account setup: 76 | .INDENT 7.0 77 | .INDENT 3.5 78 | result_attrs = displayname, mailaddr 79 | .UNINDENT 80 | .UNINDENT 81 | .UNINDENT 82 | .SH AUTHORS 83 | .INDENT 0.0 84 | .TP 85 | .B Christian Roessner <\fI\%cr@sys4.de\fP> 86 | Wrote the program. 87 | .TP 88 | .B Patrick Ben Koetter <\fI\%p@sys4.de\fP> 89 | Wrote the documentation. 90 | .UNINDENT 91 | .SH SEE ALSO 92 | .sp 93 | \fI\%automx(8)\fP, \fI\%automx.conf(5)\fP, \fI\%automx_ldap(5)\fP, \fI\%automx_script(5)\fP, \fI\%automx_sql(5)\fP, \fI\%automx\-test(1)\fP 94 | .SH COPYRIGHT 95 | This document has been placed in the public domain. 96 | .\" Generated by docutils manpage writer. 97 | . 98 | -------------------------------------------------------------------------------- /doc/txt/automx-test.1.txt: -------------------------------------------------------------------------------- 1 | automx-test 2 | 3 | Date: 02/08/2013 4 | Subtitle: automx command line client 5 | Manual Section: 1 6 | Manual Group: automx 7 | Copyright: This document has been placed in the public domain. 8 | 9 | Synopsis 10 | 11 | automx-test localpart@domainpart 12 | 13 | Description 14 | 15 | The automx-test command line client requests autoconfiguration data in Mozilla 16 | and Microsoft schema requests and outputs the server response to STDOUT. 17 | 18 | Authors 19 | 20 | Christian Roessner 21 | Wrote the program. 22 | Patrick Ben Koetter 23 | Wrote the documentation. 24 | 25 | See also 26 | 27 | automx(8), automx.conf(5), automx_ldap(5), automx_script(5), automx_sql(5), 28 | automx-test(1) 29 | 30 | -------------------------------------------------------------------------------- /doc/txt/automx_ldap.5.txt: -------------------------------------------------------------------------------- 1 | automx_ldap 2 | 3 | Date: 02/08/2013 4 | Subtitle: automx LDAP backend configuration parameters 5 | Manual Section: 5 6 | Manual Group: automx 7 | Copyright: This document has been placed in the public domain. 8 | 9 | Description 10 | 11 | The automx_ldap(5) man page specifies all parameters that control access from 12 | within automx to a LDAP backend. 13 | 14 | Parameters 15 | 16 | authzid (no default) 17 | Specifies the SASL proxy authorization identity. 18 | base (default: none) 19 | Specifies the default base DN to use when performing ldap operations. The 20 | base must be specified as a Distinguished Name in LDAP format. 21 | binddn (default: none) 22 | Specifies the default bind DN to use when performing ldap operations. The 23 | bind DN must be specified as a Distinguished Name in LDAP format. 24 | bindmethod (default: simple) 25 | Specifies how authentication should take place. Valid options are either 26 | simple for a simple bind or sasl for a bind that requires SASL 27 | authentication. 28 | bindpw (default: none) 29 | Specifies the password used when binddn identifies itself with the LDAP 30 | server. 31 | cacert (default: none) 32 | Specifies the path to a file that contains all certificates of 33 | Certification Authorities automx should trust. 34 | cert (default: none) 35 | Specifies the path to a file that contains automx's certificate. 36 | cipher (default: TLSv1) 37 | See ciphers(1) for a list of valid options. 38 | filter (default: (objectClass=*)) 39 | 40 | Specifies the search filter to select appropriate LDAP objects. The filter 41 | should conform to the string representation for search filters as defined 42 | in RFC 4515. 43 | 44 | Note 45 | 46 | See the section “Macros and Variables” in automx.conf(5) for a list of 47 | available query macros. 48 | 49 | host (default: ldap://127.0.0.1/) 50 | 51 | Specifies one or more LDAP servers separated by commas as shown in the 52 | following example: 53 | 54 | host = ldap://127.0.0.1, ldap://192.168.2.1 55 | 56 | Important 57 | 58 | Subsequent servers to the first serve only for fallback purposes, i.e. a 59 | server to the right will only be queried if the server left to it cannot be 60 | reached. If a server can be reached no further attempts will be made 61 | regardless if the query returned a result or not. 62 | 63 | key (default: none) 64 | Specifies the path to a file that contains automx's private key, which 65 | matches automx certificate given with cert. 66 | reqcert (default: never) 67 | 68 | Specifies what checks to perform on server certificates in a TLS session, 69 | if any. The can be specified as one of the following keywords: 70 | 71 | never 72 | The client will not request or check any server certificate. This is 73 | the default setting. 74 | allow 75 | The server certificate is requested. If no certificate is provided, the 76 | session proceeds normally. If a bad certificate is provided, it will be 77 | ignored and the session proceeds normally. 78 | try 79 | The server certificate is requested. If no certificate is provided, the 80 | session proceeds normally. If a bad certificate is provided, the 81 | session is immediately terminated. 82 | demand 83 | These keywords are equivalent. The server certificate is requested. If 84 | no certificate is provided, or a bad certificate is provided, the 85 | session is immediately terminated. 86 | 87 | result_attrs (default: none) 88 | If automx finds one or more entries, the attributes specified by 89 | result_attrs are returned. If * is listed, all user attributes are 90 | returned. 91 | saslmech (default: none) 92 | 93 | Specifies the SASL mechanism to be used for authentication. 94 | 95 | cram-md5 96 | The SASL cram-md5 mechanism (see: RFC 2195) will be used to 97 | authenticate LDAP bind requests. 98 | digest-md5 99 | The SASL digest-md5 mechanism (see: RFC 2831) will be used to 100 | authenticate LDAP bind requests. 101 | external 102 | The SASL external mechanism (see: RFC 4422) will be used to 103 | authenticate LDAP bind requests. 104 | gssapi 105 | The SASL gssapi mechanism (see: RFC 4752) will be used to authenticate 106 | LDAP bind requests. 107 | none 108 | No SASL mechanism will be use to authenticate LDAP bind requests. 109 | 110 | scope (default: sub) 111 | Specify the scope of the search to be one of base (or exact), one (or 112 | onelevel), sub (or substree), to specify a base object, one-level, or 113 | subtree search. 114 | usetls (default: false) 115 | Specifies if automx should use TLS when it connects to the LDAP host. 116 | 117 | Authors 118 | 119 | Christian Roessner 120 | Wrote the program. 121 | Patrick Ben Koetter 122 | Wrote the documentation. 123 | 124 | See also 125 | 126 | automx(8), automx.conf(5), automx_ldap(5), automx_script(5), automx_sql(5), 127 | automx-test(1) 128 | 129 | -------------------------------------------------------------------------------- /doc/txt/automx_script.5.txt: -------------------------------------------------------------------------------- 1 | automx_script 2 | 3 | Date: 02/08/2013 4 | Subtitle: automx script backend configuration parameters 5 | Manual Section: 5 6 | Manual Group: automx 7 | Copyright: This document has been placed in the public domain. 8 | 9 | Description 10 | 11 | The automx_sript(5) man page specifies all parameters that control access from 12 | within automx to a script backend. 13 | 14 | Parameters 15 | 16 | result_attrs (default: none) 17 | 18 | Specifies a list of one or more variables. The output of the external 19 | script will assign values to these variables. Results will be assigned in 20 | the same order as variables have been listed: 21 | 22 | result_attrs = server, email, auth, socket 23 | 24 | Important 25 | 26 | automx_script(5) does not check if the number of values returned matches the 27 | ones expected. Too many return values will be discarded. If the script returns 28 | too few automx_script(5) will use the corresponding result attribute name. 29 | 30 | script (default: none) 31 | 32 | Specifies the absolute path to the script which should be run by the 33 | backend automx_script(5) backend: 34 | 35 | script = /usr/local/bin/example_com.sh "%s" 36 | 37 | separator (default: whitespace) 38 | 39 | Specifies the character that separates values returned by the external 40 | script: 41 | 42 | separator = , 43 | 44 | Authors 45 | 46 | Christian Roessner 47 | Wrote the program. 48 | Michael Menge 49 | Wrote and contributed this backend. 50 | Patrick Ben Koetter 51 | Wrote the documentation. 52 | 53 | See also 54 | 55 | automx(8), automx.conf(5), automx_ldap(5), automx_script(5), automx_sql(5), 56 | automx-test(1) 57 | 58 | -------------------------------------------------------------------------------- /doc/txt/automx_sql.5.txt: -------------------------------------------------------------------------------- 1 | automx_sql 2 | 3 | Date: 02/08/2013 4 | Subtitle: automx SQL backend configuration parameters 5 | Manual Section: 5 6 | Manual Group: automx 7 | Copyright: This document has been placed in the public domain. 8 | 9 | Description 10 | 11 | The automx_sql(5) man page specifies all parameters that control access from 12 | within automx to a SQL backend. 13 | 14 | Parameters 15 | 16 | host (default: none) 17 | 18 | Specifies one or more SQL servers separated by commas. Each server 19 | specification must provide database driver, username and password to access 20 | a database on a host as shown in the following example: 21 | 22 | host = driver://username:password@hostname/database 23 | 24 | Important 25 | 26 | Subsequent servers to the first serve only for fallback purposes, i.e. a 27 | server to the right will only be queried if the server left to it cannot be 28 | reached. If a server can be reached no further attempts will be made 29 | regardless if the query returned a result or not. 30 | 31 | query (default: none) 32 | 33 | Specifies the query that should be sent to the database specified with the 34 | host parameter: 35 | 36 | query = SELECT displayname, mailaddr FROM mail WHERE mailaddr='%s'; 37 | 38 | Note 39 | 40 | See the section called “Macros and Variables” in automx.conf(5) for a list 41 | of available query macros. 42 | 43 | result_attrs (default: none) 44 | 45 | Specifies the attributes whose values should be used in an automx account 46 | setup: 47 | 48 | result_attrs = displayname, mailaddr 49 | 50 | Authors 51 | 52 | Christian Roessner 53 | Wrote the program. 54 | Patrick Ben Koetter 55 | Wrote the documentation. 56 | 57 | See also 58 | 59 | automx(8), automx.conf(5), automx_ldap(5), automx_script(5), automx_sql(5), 60 | automx-test(1) 61 | 62 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | 5 | setup(name='automx', 6 | description='Provides account configuration data to mailclients', 7 | url='https://github.com/sys4/automx', 8 | author_email='c@roessner.co', 9 | maintainer='Christian Roessner', 10 | keywords=['wsgi', 'autoconfig', 'autodiscover', 'mobileconfig'], 11 | license='GNU GPLv3', 12 | version='1.1.1', 13 | py_modules=['automx_wsgi'], 14 | packages=['automx'], 15 | package_dir={'': 'src'}, 16 | data_files=[('/etc', ['src/conf/automx.conf'])], 17 | scripts=['src/automx-test'], 18 | requires=['future', 'lxml', 'ipaddress'], 19 | classifiers=[ 20 | 'Development Status :: 4 - Beta', 21 | 'Environment :: No Input/Output (Daemon)', 22 | 'Intended Audience :: Developers', 23 | 'Intended Audience :: System Administrators', 24 | 'Intended Audience :: Telecommunications Industry', 25 | 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 26 | 'Operating System :: OS Independent', 27 | 'Programming Language :: Python :: 2.7', 28 | 'Programming Language :: Python :: 3', 29 | 'Programming Language :: Python :: 3.3', 30 | 'Programming Language :: Python :: 3.4', 31 | 'Programming Language :: Python :: 3.5', 32 | 'Topic :: Communications :: Email', 33 | 'Topic :: Utilities', 34 | ] 35 | ) 36 | -------------------------------------------------------------------------------- /src/automx-test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # automx - auto configuration service 4 | # Copyright (c) 2011-2013 [*] sys4 AG 5 | # 6 | # Authors: Christian Roessner, Patrick Ben Koetter, Marc Schiffbauer 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | # 22 | VERSION="1.1.1" 23 | 24 | trap clean_exit EXIT 25 | function clean_exit() { 26 | [[ -f "$OLREQUEST" ]] && rm "$OLREQUEST" 27 | [[ -f "$MBREQUEST" ]] && rm "$MBREQUEST" 28 | [[ -f "$MBCRESPONSE" ]] && rm "$MBCRESPONSE" 29 | } 30 | 31 | # We need a mail address 32 | if [[ $1 ]]; then 33 | PROFILE="$1" 34 | else 35 | echo "Provide the mail address for which configuration settings should be retrieved." 36 | read -ep "Mail address: " PROFILE 37 | fi 38 | 39 | DOMAIN="${PROFILE#*@}" 40 | PROGRAM_NAME=$(basename $0) 41 | OLREQUEST="$(mktemp /tmp/${PROGRAM_NAME}.XXXXXX)" 42 | MBREQUEST="$(mktemp /tmp/${PROGRAM_NAME}.XXXXXX)" 43 | MBCRESPONSE="$(mktemp /tmp/${PROGRAM_NAME}.XXXXXX)" 44 | 45 | # Test Mozilla schema 46 | MOZFOUND=0 47 | AUTOCONF="autoconfig.$DOMAIN" 48 | WELL_KNOWN="$DOMAIN/.well-known/autoconfig" 49 | if [[ ! $(dig +short $AUTOCONF) ]]; then 50 | echo 51 | echo "Autodiscovery domain for Mozilla Thunderbird not found ($AUTOCONF)" 52 | echo 53 | else 54 | CON="http://$AUTOCONF/mail/config-v1.1.xml?emailaddress=$PROFILE" 55 | echo 56 | echo "Testing Autoconfig ..." 57 | echo "Connecting to $CON ..." 58 | echo 59 | wget -S -O - -q --no-check-certificate $CON && MOZFOUND=1 60 | fi 61 | if [ $MOZFOUND -ne 1 ]; then 62 | # some error happened; try fallback URL 63 | echo "Trying fallback URL ..." 64 | CON="http://$WELL_KNOWN/mail/config-v1.1.xml?emailaddress=$PROFILE" 65 | echo "Connecting to $CON ..." 66 | echo 67 | wget -S -O - -q --no-check-certificate $CON && MOZFOUND=1 68 | fi 69 | if [ $MOZFOUND -eq 0 ]; then 70 | # no supported autoconfig information 71 | echo "No autoconfig endpoint found." 72 | echo 73 | fi 74 | 75 | 76 | # Test Microsoft schema 77 | AUTODISC="autodiscover.$DOMAIN" 78 | if [[ -z $(dig +short $AUTODISC) ]]; then 79 | # default domain does not exist, try to discover non-default 80 | AUTODISC="$(dig +short -t srv _autodiscover._tcp.$DOMAIN)" 81 | AUTODISC="${AUTODISC//* /}" 82 | AUTODISC="${AUTODISC%.*}" 83 | fi 84 | if [[ $AUTODISC ]]; then 85 | # Test Microsoft Outlook schema 86 | CON="https://$AUTODISC/autodiscover/autodiscover.xml" 87 | cat <<-REQ >$OLREQUEST 88 | 89 | 90 | 91 | http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a 92 | $PROFILE 93 | 94 | 95 | REQ 96 | 97 | echo 98 | echo "Testing Autodiscover (Microsoft Outlook(tm)) ..." 99 | echo "Connecting to $CON ..." 100 | echo 101 | wget -S -O - -q --post-file=$OLREQUEST --no-check-certificate $CON 102 | rm $OLREQUEST 103 | 104 | # Test Microsoft Mobile schema 105 | cat <<-REQ >$MBREQUEST 106 | 107 | 108 | 109 | http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006 110 | $PROFILE 111 | 112 | 113 | REQ 114 | 115 | echo 116 | echo "Testing Autodiscover (mobilesync) ..." 117 | echo "Connecting to $CON ..." 118 | echo 119 | wget -S -O - -q --post-file=$MBREQUEST --no-check-certificate $CON 120 | rm $MBREQUEST 121 | else 122 | echo 123 | echo "Autodiscovery domain for Microsoft Outlook(tm) not found (autodiscover.$DOMAIN)" 124 | echo 125 | fi 126 | 127 | # Test mobileconfig schema 128 | AUTODISC="autodiscover.$DOMAIN" 129 | if [[ -z $(dig +short $AUTODISC) ]]; then 130 | # default domain does not exist, try to discover non-default 131 | AUTODISC="$(dig +short -t srv _autodiscover._tcp.$DOMAIN)" 132 | AUTODISC="${AUTODISC//* /}" 133 | AUTODISC="${AUTODISC%.*}" 134 | fi 135 | if [[ $AUTODISC ]]; then 136 | # Test Apple mobileconfig schema 137 | CON="https://$AUTODISC/mobileconfig" 138 | 139 | echo -n "_mobileconfig=true&cn=&emailaddress=$PROFILE&password=" > $MBREQUEST 140 | 141 | echo 142 | echo "Testing mobileconfig..." 143 | echo "Connecting to $CON ..." 144 | echo 145 | wget -S -O - -q --post-file=$MBREQUEST --no-check-certificate $CON | hexdump -C 146 | else 147 | echo 148 | echo "Domain for iOS(tm) not found ($DOMAIN)" 149 | echo 150 | fi 151 | # EOF 152 | -------------------------------------------------------------------------------- /src/automx/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | automx - auto configuration service 3 | Copyright (c) 2011-2013 [*] sys4 AG 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | """ 18 | 19 | __version__ = '0.10.2' 20 | __author__ = "Christian Roessner, Patrick Ben Koetter" 21 | __copyright__ = "Copyright (c) 2011-2013 [*] sys4 AG" 22 | 23 | __all__ = ['config', 'view'] 24 | -------------------------------------------------------------------------------- /src/conf/apache.conf.example: -------------------------------------------------------------------------------- 1 | # Example for autoconfig 2 | 3 | 4 | ServerName autoconfig.example.com 5 | ServerAdmin webmaster@example.com 6 | DocumentRoot /usr/lib/automx 7 | 8 | # Redirect permanent / https://autoconfig.example.com 9 | 10 | 11 | WSGIScriptAlias /mail/config-v1.1.xml /usr/lib/automx/automx_wsgi.py 12 | 13 | Order allow,deny 14 | Allow from all 15 | 16 | 17 | 18 | 19 | 20 | # SSL example for autodiscover 21 | 22 | 23 | ServerName autodiscover.example.com 24 | ServerAlias autoconfig.example.com 25 | ServerAdmin webmaster@example.com 26 | DocumentRoot /usr/lib/automx 27 | 28 | 29 | WSGIScriptAlias /Autodiscover/Autodiscover.xml /usr/lib/automx/automx_wsgi.py 30 | WSGIScriptAlias /autodiscover/autodiscover.xml /usr/lib/automx/automx_wsgi.py 31 | WSGIScriptAlias /mobileconfig /usr/lib/automx/automx_wsgi.py 32 | 33 | Order allow,deny 34 | Allow from all 35 | 36 | 37 | 38 | # more ssl options ... 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/conf/automx.conf: -------------------------------------------------------------------------------- 1 | # file: /etc/automx.conf 2 | 3 | [automx] 4 | provider = example.com 5 | domains = example.com 6 | 7 | # debug = yes 8 | # logfile = /var/log/automx/automx.log 9 | 10 | # Protect against DoS 11 | memcache = 127.0.0.1:11211 12 | memcache_ttl = 600 13 | client_error_limit = 20 14 | rate_limit_exception_networks = 127.0.0.0/8, ::1/128 15 | 16 | # The DEFAULT section is always merged into each other section. Each section 17 | # can overwrite settings done here. 18 | [DEFAULT] 19 | account_type = email 20 | account_name = Example Incorporated 21 | account_name_short = Example Inc. 22 | 23 | 24 | # If a domain is listed in the automx section, it may have its own section. If 25 | # none is found here, the global section is used. 26 | [global] 27 | backend = static 28 | action = settings 29 | 30 | 31 | # If you want to sign mobileconfig profiles, enable these options. Make sure 32 | # that your webserver has proper privileges to read the key. The cert file 33 | # must contain the server certificate and all intermediate certificates. You 34 | # can simply concatenate these certificates. 35 | #sign_mobileconfig = yes 36 | #sign_cert = /path/to/cert 37 | #sign_key = /path/to/key 38 | 39 | smtp = yes 40 | smtp_server = mail.example.com 41 | smtp_port = 587 42 | smtp_encryption = starttls 43 | smtp_auth = plaintext 44 | smtp_refresh_ttl = 6 45 | smtp_default = yes 46 | 47 | imap = yes 48 | imap_server = mail.example.com 49 | imap_port = 143 50 | imap_encryption = starttls 51 | imap_auth = plaintext 52 | imap_refresh_ttl = 6 53 | 54 | pop = yes 55 | pop_server = mail.example.com 56 | pop_port = 110 57 | pop_encryption = none 58 | pop_auth = plaintext 59 | pop_refresh_ttl = 6 60 | 61 | 62 | [example.com] 63 | backend = global 64 | # example.com uses settings from the global section 65 | 66 | -------------------------------------------------------------------------------- /src/conf/automx.conf.example-complex: -------------------------------------------------------------------------------- 1 | [automx] 2 | provider = example.com 3 | ; debug = yes 4 | 5 | # We care about any domain that we get a query for 6 | domains = * 7 | 8 | 9 | [DEFAULT] 10 | action = settings 11 | 12 | account_type = email 13 | account_name = Foobar - ${cn} 14 | account_name_short = Whatever you like 15 | 16 | display_name = ${givenName} ${sn} 17 | 18 | smtp = yes 19 | smtp_server = mail.example.com 20 | smtp_port = 587 21 | smtp_encryption = starttls 22 | smtp_auth = plaintext 23 | smtp_auth_identity = ${deliverToAddress} 24 | smtp_default = yes 25 | 26 | imap = yes 27 | imap_server = mail.example.com 28 | imap_port = 143 29 | imap_encryption = starttls 30 | imap_auth = plaintext 31 | imap_auth_identity = ${deliverToAddress} 32 | 33 | pop = yes 34 | pop_server = mail.example.com 35 | pop_port = 110 36 | pop_encryption = starttls 37 | pop_auth = plaintext 38 | pop_auth_identity = ${deliverToAddress} 39 | 40 | host = ldap://primary.example.com, ldap://fallback1.example.com 41 | base = ou=mail,ou=it,dc=example,dc=com 42 | result_attrs = deliverToAddress, cn, givenName, sn 43 | scope = sub 44 | filter = (&(objectClass=*)(recipientAddress=%s)) 45 | 46 | bindmethod = sasl 47 | saslmech = EXTERNAL 48 | usetls = yes 49 | reqcert = demand 50 | cert = /etc/ssl/certs/yourservercert.pem 51 | key = /etc/ssl/private/yourserverkey.pem 52 | cacert = /etc/ssl/certs/yourca.pem 53 | 54 | 55 | [global] 56 | backend = ldap 57 | 58 | 59 | [lululala.net] 60 | backend = file 61 | autodiscover = /var/www/automx/templates/autoconfig.xml 62 | 63 | # More options are merged from global! And see, we do not provide a static 64 | # XML file for the autoconfig service (in this example). 65 | 66 | 67 | [foobar.org] 68 | backend = filter 69 | section_filter = filter.foobar.org 70 | 71 | # You may nearly specify any command here. Even ssh remote command invocation 72 | # was tested by the author. It works, as long as you get an emailaddress back. 73 | filter.foobar.org = /bin/echo "%s" 74 | 75 | 76 | [filter.foobar.org] 77 | backend = sql 78 | action = settings 79 | 80 | # Just a sqlite3 example here! 81 | host = sqlite:///var/www/yoursqlite3.db 82 | query = SELECT displayname, mailaddr FROM mail WHERE mailaddr='%s'; 83 | result_attrs = displayname, mailaddr 84 | 85 | display_name = ${displayname} 86 | 87 | smtp = yes 88 | smtp_server = mail.somewhere.org 89 | smtp_port = 587 90 | smtp_encryption = starttls 91 | smtp_auth = plaintext 92 | smtp_auth_identity = ${mailaddr} 93 | smtp_default = yes 94 | 95 | imap = yes 96 | imap_server = mail.somewhere.org 97 | imap_port = 143 98 | imap_encryption = starttls 99 | imap_auth = plaintext 100 | imap_auth_identity = ${mailaddr} 101 | 102 | pop = no 103 | -------------------------------------------------------------------------------- /src/conf/nginx-automx.conf: -------------------------------------------------------------------------------- 1 | # /etc/nginx/conf.d/nginx-automx.conf 2 | 3 | # You need to ajust the server IP, certificate, server name and the 4 | # socket for uWSGI 5 | 6 | map $http_accept_language $lang { 7 | default en; 8 | ~de de; 9 | ~en en; 10 | } 11 | 12 | ## 13 | ## HTTP: autoconfig.* 14 | ## 15 | 16 | server { 17 | # Your server IP 18 | listen 11.22.33.44:80; 19 | 20 | server_name autoconfig.*; 21 | 22 | # automx - autoconfig 23 | location /mail/config-v1.1.xml { 24 | include uwsgi_params; 25 | uwsgi_pass 127.0.0.1:9100; 26 | } 27 | 28 | access_log /var/log/nginx/access_log main; 29 | error_log /var/log/nginx/error_log info; 30 | } 31 | 32 | ## 33 | ## HTTPS: www.example.com 34 | ## 35 | 36 | server { 37 | # Your server IP 38 | listen 11.22.33.44:443; 39 | 40 | server_name www.example.com; 41 | 42 | # Path to your automx static files 43 | root /var/www/localhost/htdocs; 44 | 45 | index index.html.$lang index.html; 46 | 47 | ssl on; 48 | ssl_certificate /etc/ssl/certs/www.example.com.crt; 49 | ssl_certificate_key /etc/ssl/private/www.example.com.key; 50 | ssl_session_timeout 5m; 51 | ssl_session_cache shared:SSL:5m; 52 | 53 | ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; 54 | ssl_prefer_server_ciphers on; 55 | ssl_ciphers RC4:HIGH:!aNULL:!MD5:!kEDH; 56 | 57 | location / { 58 | more_set_headers 'Content-Type: text/html'; 59 | try_files $uri $uri/ index.html.$lang index.html; 60 | } 61 | 62 | # automx - autodiscover 63 | location /autodiscover/autodiscover.xml { 64 | include uwsgi_params; 65 | uwsgi_pass 127.0.0.1:9100; 66 | } 67 | # automx - mobileconfig 68 | location /mobileconfig { 69 | include uwsgi_params; 70 | uwsgi_pass 127.0.0.1:9100; 71 | } 72 | 73 | access_log /var/log/nginx/access_log main; 74 | error_log /var/log/nginx/error_log info; 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/doc/BASIC_CONFIGURATION_README.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | Basic Configuration 3 | ===================== 4 | 5 | This document contains information for initial and/or basic configuration of automx. 6 | 7 | automx reads runtime behaviour and all settings controlling a domains account provisioning from a single configuration file. By default automx expects to find this file at /etc/automx.conf. 8 | 9 | Format 10 | ====== 11 | 12 | The general format of the automx.conf file is as follows: 13 | 14 | - The basic element contained in an INI file is the property. Every property has a name and a value, delimited by an equals sign (“=”). The name appears to the left of the equals sign. 15 | - Properties are grouped into sections. The section name appears on a line by itself, in square brackets (“[” and “]”). All properties after the section declaration are associated with that section. There is no explicit “end of section” delimiter; sections end at the next section declaration, or the end of the file. Sections may not be nested. 16 | - Section and property names are case sensitive. 17 | - A line with a number sign (“#”) begins a comment. Anything following a number sign will be ignored by automx. 18 | 19 | 20 | Sections 21 | ======== 22 | 23 | Sections create a namespace in which properties specific to a domain are defined. The section name identifies the domain. The three section names [automx], [DEFAULT] and [global] are reserved for special purposes within automx. 24 | 25 | [automx] 26 | '''''''' 27 | 28 | Controlling automx Runtime Behaviour 29 | 30 | This section is mandatory - it lists all options controlling automx runtime behaviour. The properties provider and domains are also mandatory. Usage of memcache and all of its associated properties is highly recommended. 31 | 32 | [automx] 33 | 34 | The following example shows a typical [automx] section setup:: 35 | 36 | [automx] 37 | provider = example.com 1 38 | domains = * 2 39 | logfile = /var/log/automx/automx.log 3 40 | debug = yes 4 41 | memcache = 127.0.0.1:11211 5 42 | memcache_ttl = 86400 43 | client_error_limit = 5 6 44 | rate_limit_exception_networks = 127.0.0.0/8, ::1/128 7 45 | 46 | 1 47 | The provider property configures automx to identify the webservice as example.com. 48 | 49 | 2 50 | The wildcard option * used in domains instructs automx to answer any configuration request regardless of the domain sent by the mail client. 51 | 52 | 3 53 | All log information should go to /var/log/automx/automx.log. 54 | 55 | 4 56 | Debugging is enabled and infos will be sent to logfile. 57 | 58 | 5 59 | Statistical data controlling errors caused by clients accessing database backends will be sent to the specified memcache service. 60 | 61 | 6 62 | In this example a client may not cause more than 5 errors before automx will refuse to answer further queries. 63 | 64 | 7 65 | Clients listed in rate_limit_exception_networks are excluded from rate limiting. 66 | 67 | 68 | [DEFAULT] 69 | ''''''''' 70 | 71 | Properties present in all other sections 72 | 73 | This section is optional. Settings in this section define properties which will be present in all other sections. It is useful to avoid redundancy. 74 | 75 | [DEFAULT] 76 | 77 | The following example shows a typical [DEFAULT] section setup:: 78 | 79 | [DEFAULT] 80 | action = settings 1 81 | account_type = email 2 82 | account_name = Example Inc. 3 83 | account_name_short = Example 4 84 | 85 | 1 86 | The default action for automx is to provide account settings. 87 | 88 | .. NOTE:: 89 | 90 | The Microsoft schema forsees other actions that account provisioning. 91 | 92 | 2 93 | The account_type should be an email account. 94 | 95 | 3 96 | The account should show up as Example Inc. in the clients account list. 97 | 98 | 4 99 | The accounts short name should be Example. 100 | 101 | 102 | [global] 103 | '''''''' 104 | 105 | A global backend 106 | 107 | Setting this section is mandatory, but it may remain empty. It provides a backend, which will be used whenever automx should serve a domain, but no section with domain-specific settings has been specified. 108 | 109 | Other sections may either explicitly or implicitly refer to the [global] section as a whole. An explicit reference specifies global as backend property. Implicit references simply announce the domain in automx' domains list and omit an explicit section definition for that domain. 110 | 111 | .. NOTE:: 112 | 113 | This is useful when many domains should use the same backend or when automx domain property configures it to run as wildcard service. 114 | 115 | [global] 116 | 117 | The following example configures automx to query a LDAP directory service. Refer to automx_ldap(5) for a detailed discussion of parameters and their meaning:: 118 | 119 | [global] 120 | backend = ldap 121 | 122 | account_name = ${cn} (Example Inc.) 123 | display_name = ${givenName} ${sn} 124 | 125 | smtp = yes 126 | smtp_server = mail.example.com 127 | smtp_port = 587 128 | smtp_encryption = starttls 129 | smtp_auth = plaintext 130 | smtp_auth_identity = ${mail} 131 | smtp_expiration_date = 2012-12-31 132 | smtp_refresh_ttl = 0 133 | smtp_default = yes 134 | 135 | imap = yes 136 | imap_server = mail.example.com 137 | imap_port = 993 138 | imap_encryption = ssl 139 | imap_auth = plaintext 140 | imap_auth_identity = ${mail} 141 | imap_expiration_date = 2012-12-31 142 | imap_refresh_ttl = 0 143 | 144 | pop = no 145 | pop_server = mail.example.com 146 | pop_port = 995 147 | pop_encryption = ssl 148 | pop_auth = plaintext 149 | pop_auth_identity = ${mail} 150 | pop_expiration_date = 2012-12-31 151 | pop_refresh_ttl = 0 152 | 153 | host = ldap://ldap.example.com 154 | base = ou=people,dc=example,dc=com 155 | result_attrs = mail, cn, givenName, sn 156 | scope = sub 157 | filter = (&(objectClass=*)(uniqueIdentifier=%s)) 158 | 159 | bindmethod = sasl 160 | saslmech = EXTERNAL 161 | usetls = yes 162 | reqcert = demand 163 | cert = /etc/ssl/certs/mail.example.com.crt.pem 164 | key = /etc/ssl/private/mail.example.com.key.pem 165 | cacert = /etc/ssl/certs/ca-certificates.crt 166 | 167 | 168 | Authors 169 | ''''''' 170 | 171 | Christian Rößner 172 | Wrote the program. 173 | 174 | Patrick Ben Koetter 175 | Wrote the documentation. 176 | -------------------------------------------------------------------------------- /src/doc/Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/sh 2 | MANFILES = automx.conf.5.rst automx_ldap.5.rst automx_sql.5.rst automx_script.5.rst automx-test.1.rst 3 | DOCFILES = BASIC_CONFIGURATION_README.rst INSTALL.rst 4 | SOURCES = $(MANFILES) $(DOCFILES) 5 | MANS= $(patsubst %.rst,%,$(MANFILES)) 6 | HTMLS= $(patsubst %.rst,%.html,$(SOURCES)) 7 | PDFS = $(patsubst %.rst,%.pdf,$(SOURCES)) 8 | TXTS = $(patsubst %.rst,%.txt,$(SOURCES)) 9 | 10 | all: html txt man pdf 11 | 12 | man: $(MANS) 13 | 14 | pdf: $(PDFS) 15 | 16 | txt: $(TXTS) 17 | 18 | html: $(HTMLS) 19 | 20 | %.html: %.rst 21 | rst2html $< $@ 22 | 23 | %.txt: %.html 24 | w3m -cols 80 -dump $< > $@ 25 | 26 | %: %.rst 27 | rst2man $< $@ 28 | 29 | %.pdf: %.rst 30 | rst2pdf $< -o $@ 31 | 32 | install: 33 | cp -p *.5 ../../doc/man/man5/ 34 | cp -p *.1 ../../doc/man/man1/ 35 | cp -p *.html ../../doc/html/ 36 | cp -p automx*.txt ../../doc/txt/ 37 | cp -p INSTALL.txt ../../INSTALL 38 | cp -p BASIC_CONFIGURATION_README.txt ../../BASIC_CONFIGURATION_README 39 | 40 | .PHONY: clean 41 | 42 | clean: 43 | rm -f *.html *.txt *.pdf *.5 *.1 BASIC_CONFIGURATION_README INSTALL 44 | -------------------------------------------------------------------------------- /src/doc/Makefile.MacOSX: -------------------------------------------------------------------------------- 1 | SHELL = /bin/sh 2 | MANFILES = automx.conf.5.rst automx_ldap.5.rst automx_sql.5.rst automx_script.5.rst automx-test.1.rst 3 | DOCFILES = BASIC_CONFIGURATION_README.rst INSTALL.rst 4 | SOURCES = $(MANFILES) $(DOCFILES) 5 | MANS= $(patsubst %.rst,%,$(MANFILES)) 6 | HTMLS= $(patsubst %.rst,%.html,$(SOURCES)) 7 | PDFS = $(patsubst %.rst,%.pdf,$(SOURCES)) 8 | TXTS = $(patsubst %.rst,%.txt,$(SOURCES)) 9 | 10 | all: html txt man pdf 11 | 12 | man: $(MANS) 13 | 14 | pdf: $(PDFS) 15 | 16 | txt: $(TXTS) 17 | 18 | html: $(HTMLS) 19 | 20 | %.html: %.rst 21 | rst2html-2.7.py $< $@ 22 | 23 | %.txt: %.html 24 | w3m -cols 80 -dump $< > $@ 25 | 26 | %: %.rst 27 | rst2man-2.7.py $< $@ 28 | 29 | %.pdf: %.rst 30 | rst2pdf $< -o $@ 31 | 32 | install: 33 | cp -p *.5 ../../doc/man/man5/ 34 | cp -p *.1 ../../doc/man/man1/ 35 | cp -p *.html ../../doc/html/ 36 | cp -p automx*.txt ../../doc/txt/ 37 | cp -p INSTALL.txt ../../INSTALL 38 | cp -p BASIC_CONFIGURATION_README.txt ../../BASIC_CONFIGURATION_README 39 | 40 | .PHONY: clean 41 | 42 | clean: 43 | rm -f *.html *.txt *.pdf *.5 *.1 BASIC_CONFIGURATION_README INSTALL 44 | -------------------------------------------------------------------------------- /src/doc/automx-test.1.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | automx-test 3 | ============= 4 | 5 | :Date: 02/08/2013 6 | :Subtitle: automx command line client 7 | :Manual Section: 1 8 | :Manual Group: automx 9 | :Copyright: This document has been placed in the public domain. 10 | 11 | Synopsis 12 | '''''''' 13 | 14 | automx-test *localpart@domainpart* 15 | 16 | Description 17 | ''''''''''' 18 | 19 | The ``automx-test`` command line client requests autoconfiguration data in Mozilla and Microsoft schema requests and outputs the server response to ``STDOUT``. 20 | 21 | Authors 22 | ''''''' 23 | 24 | Christian Roessner 25 | Wrote the program. 26 | 27 | Patrick Ben Koetter 28 | Wrote the documentation. 29 | 30 | See also 31 | '''''''' 32 | 33 | `automx(8)`_, `automx.conf(5)`_, `automx_ldap(5)`_, `automx_script(5)`_, `automx_sql(5)`_, `automx-test(1)`_ 34 | 35 | .. _automx(8): automx.8.html 36 | .. _automx.conf(5): automx.conf.5.html 37 | .. _automx_ldap(5): automx_ldap.5.html 38 | .. _automx_sql(5): automx_sql.5.html 39 | .. _automx_script(5): automx_script.5.html 40 | .. _automx-test(1): automx-test.1.html 41 | -------------------------------------------------------------------------------- /src/doc/automx_ldap.5.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | automx_ldap 3 | ============= 4 | 5 | :Date: 02/08/2013 6 | :Subtitle: automx LDAP backend configuration parameters 7 | :Manual Section: 5 8 | :Manual Group: automx 9 | :Copyright: This document has been placed in the public domain. 10 | 11 | 12 | Description 13 | ''''''''''' 14 | 15 | The automx_ldap(5) man page specifies all parameters that control access from 16 | within automx to a LDAP backend. 17 | 18 | Parameters 19 | '''''''''' 20 | 21 | authzid (no default) 22 | Specifies the SASL proxy authorization identity. 23 | 24 | base (default: none) 25 | Specifies the default base DN to use when performing ldap operations. The base must be specified as a Distinguished Name in LDAP format. 26 | 27 | binddn (default: none) 28 | Specifies the default bind DN to use when performing ldap operations. The bind DN must be specified as a Distinguished Name in LDAP format. 29 | 30 | bindmethod (default: simple) 31 | Specifies how authentication should take place. Valid options are either simple for a simple bind or sasl for a bind that requires SASL authentication. 32 | 33 | bindpw (default: none) 34 | Specifies the password used when binddn identifies itself with the LDAP server. 35 | 36 | cacert (default: none) 37 | Specifies the path to a file that contains all certificates of Certification Authorities automx should trust. 38 | 39 | cert (default: none) 40 | Specifies the path to a file that contains automx's certificate. 41 | 42 | cipher (default: TLSv1) 43 | See ciphers(1) for a list of valid options. 44 | 45 | filter (default: (objectClass=*)) 46 | Specifies the search filter to select appropriate LDAP objects. The filter should conform to the string representation for search filters as defined in RFC 4515. 47 | 48 | .. NOTE:: 49 | 50 | See the section “Macros and Variables” in automx.conf(5) for a list of available query macros. 51 | 52 | host (default: ldap://127.0.0.1/) 53 | Specifies one or more LDAP servers separated by commas as shown in the following example:: 54 | 55 | host = ldap://127.0.0.1, ldap://192.168.2.1 56 | 57 | .. IMPORTANT:: 58 | 59 | Subsequent servers to the first serve only for fallback purposes, i.e. a server to the right will only be queried if the server left to it cannot be reached. If a server can be reached no further attempts will be made regardless if the query returned a result or not. 60 | 61 | key (default: none) 62 | Specifies the path to a file that contains automx's private key, which matches automx certificate given with cert. 63 | 64 | reqcert (default: never) 65 | Specifies what checks to perform on server certificates in a TLS session, if any. The can be specified as one of the following keywords: 66 | 67 | never 68 | The client will not request or check any server certificate. This is the default setting. 69 | 70 | allow 71 | The server certificate is requested. If no certificate is provided, the session proceeds normally. If a bad certificate is provided, it will be ignored and the session proceeds normally. 72 | 73 | try 74 | The server certificate is requested. If no certificate is provided, the session proceeds normally. If a bad certificate is provided, the session is immediately terminated. 75 | 76 | demand 77 | These keywords are equivalent. The server certificate is requested. If no certificate is provided, or a bad certificate is provided, the session is immediately terminated. 78 | 79 | result_attrs (default: none) 80 | If automx finds one or more entries, the attributes specified by result_attrs are returned. If * is listed, all user attributes are returned. 81 | 82 | saslmech (default: none) 83 | Specifies the SASL mechanism to be used for authentication. 84 | 85 | cram-md5 86 | The SASL cram-md5 mechanism (see: RFC 2195) will be used to authenticate LDAP bind requests. 87 | 88 | digest-md5 89 | The SASL digest-md5 mechanism (see: RFC 2831) will be used to authenticate LDAP bind requests. 90 | 91 | external 92 | The SASL external mechanism (see: RFC 4422) will be used to authenticate LDAP bind requests. 93 | 94 | gssapi 95 | The SASL gssapi mechanism (see: RFC 4752) will be used to authenticate LDAP bind requests. 96 | 97 | none 98 | No SASL mechanism will be use to authenticate LDAP bind requests. 99 | 100 | scope (default: sub) 101 | Specify the scope of the search to be one of base (or exact), one (or onelevel), sub (or substree), to specify a base object, one-level, or subtree search. 102 | 103 | usetls (default: false) 104 | Specifies if automx should use TLS when it connects to the LDAP host. 105 | 106 | Authors 107 | ''''''' 108 | 109 | Christian Roessner 110 | Wrote the program. 111 | 112 | Patrick Ben Koetter 113 | Wrote the documentation. 114 | 115 | See also 116 | '''''''' 117 | 118 | `automx(8)`_, `automx.conf(5)`_, `automx_ldap(5)`_, `automx_script(5)`_, `automx_sql(5)`_, `automx-test(1)`_ 119 | 120 | .. _automx(8): automx.8.html 121 | .. _automx.conf(5): automx.conf.5.html 122 | .. _automx_ldap(5): automx_ldap.5.html 123 | .. _automx_sql(5): automx_sql.5.html 124 | .. _automx_script(5): automx_script.5.html 125 | .. _automx-test(1): automx-test.1.html 126 | -------------------------------------------------------------------------------- /src/doc/automx_script.5.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | automx_script 3 | =============== 4 | 5 | :Date: 02/08/2013 6 | :Subtitle: automx script backend configuration parameters 7 | :Manual Section: 5 8 | :Manual Group: automx 9 | :Copyright: This document has been placed in the public domain. 10 | 11 | 12 | Description 13 | ''''''''''' 14 | 15 | The automx_sript(5) man page specifies all parameters that control access from within automx to a script backend. 16 | 17 | Parameters 18 | '''''''''' 19 | 20 | result_attrs (default: none) 21 | Specifies a list of one or more variables. The output of the external script will assign values to these variables. Results will be assigned in the same order as variables have been listed:: 22 | 23 | result_attrs = server, email, auth, socket 24 | 25 | .. IMPORTANT:: 26 | 27 | automx_script(5) does not check if the number of values returned matches the ones expected. Too many return values will be discarded. If the script returns too few automx_script(5) will use the corresponding result attribute name. 28 | 29 | script (default: none) 30 | Specifies the absolute path to the script which should be run by the backend automx_script(5) backend:: 31 | 32 | script = /usr/local/bin/example_com.sh "%s" 33 | 34 | separator (default: whitespace) 35 | Specifies the character that separates values returned by the external script:: 36 | 37 | separator = , 38 | 39 | Authors 40 | ''''''' 41 | 42 | Christian Roessner 43 | Wrote the program. 44 | 45 | Michael Menge 46 | Wrote and contributed this backend. 47 | 48 | Patrick Ben Koetter 49 | Wrote the documentation. 50 | 51 | See also 52 | '''''''' 53 | 54 | `automx(8)`_, `automx.conf(5)`_, `automx_ldap(5)`_, `automx_script(5)`_, `automx_sql(5)`_, `automx-test(1)`_ 55 | 56 | .. _automx(8): automx.8.html 57 | .. _automx.conf(5): automx.conf.5.html 58 | .. _automx_ldap(5): automx_ldap.5.html 59 | .. _automx_sql(5): automx_sql.5.html 60 | .. _automx_script(5): automx_script.5.html 61 | .. _automx-test(1): automx-test.1.html 62 | -------------------------------------------------------------------------------- /src/doc/automx_sql.5.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | automx_sql 3 | ============= 4 | 5 | :Date: 02/08/2013 6 | :Subtitle: automx SQL backend configuration parameters 7 | :Manual Section: 5 8 | :Manual Group: automx 9 | :Copyright: This document has been placed in the public domain. 10 | 11 | 12 | Description 13 | ''''''''''' 14 | 15 | The automx_sql(5) man page specifies all parameters that control access from within automx to a SQL backend. 16 | 17 | Parameters 18 | '''''''''' 19 | 20 | host (default: none) 21 | Specifies one or more SQL servers separated by commas. Each server specification must provide database driver, username and password to access a database on a host as shown in the following example:: 22 | 23 | host = driver://username:password@hostname/database 24 | 25 | .. IMPORTANT:: 26 | 27 | Subsequent servers to the first serve only for fallback purposes, i.e. a server to the right will only be queried if the server left to it cannot be reached. If a server can be reached no further attempts will be made regardless if the query returned a result or not. 28 | 29 | query (default: none) 30 | Specifies the query that should be sent to the database specified with the host parameter: 31 | 32 | query = SELECT displayname, mailaddr FROM mail WHERE mailaddr='%s'; 33 | 34 | .. NOTE:: 35 | 36 | See the section called “Macros and Variables” in automx.conf(5) for a list of available query macros. 37 | 38 | result_attrs (default: none) 39 | Specifies the attributes whose values should be used in an automx account setup: 40 | 41 | result_attrs = displayname, mailaddr 42 | 43 | Authors 44 | ''''''' 45 | 46 | Christian Roessner 47 | Wrote the program. 48 | 49 | Patrick Ben Koetter 50 | Wrote the documentation. 51 | 52 | See also 53 | '''''''' 54 | 55 | `automx(8)`_, `automx.conf(5)`_, `automx_ldap(5)`_, `automx_script(5)`_, `automx_sql(5)`_, `automx-test(1)`_ 56 | 57 | .. _automx(8): automx.8.html 58 | .. _automx.conf(5): automx.conf.5.html 59 | .. _automx_ldap(5): automx_ldap.5.html 60 | .. _automx_sql(5): automx_sql.5.html 61 | .. _automx_script(5): automx_script.5.html 62 | .. _automx-test(1): automx-test.1.html 63 | -------------------------------------------------------------------------------- /src/foundation-scss/MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 ZURB, http://www.zurb.com/ 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/foundation-scss/config.rb: -------------------------------------------------------------------------------- 1 | require 'zurb-foundation' 2 | # Require any additional compass plugins here. 3 | 4 | 5 | # Set this to the root of your project when deployed: 6 | http_path = "/" 7 | css_dir = "stylesheets" 8 | css_path = "../html/css" 9 | sass_dir = "sass" 10 | images_dir = "images" 11 | images_path = "../html/img" 12 | javascripts_dir = "javascripts" 13 | javascripts_path = "../html/js" 14 | 15 | # You can select your preferred output style here (can be overridden via the command line): 16 | # output_style = :expanded or :nested or :compact or :compressed 17 | 18 | # To enable relative paths to assets via compass helper functions. Uncomment: 19 | # relative_assets = true 20 | 21 | # To disable debugging comments that display the original location of your selectors. Uncomment: 22 | # line_comments = false 23 | 24 | 25 | # If you prefer the indented syntax, you might want to regenerate this 26 | # project again passing --syntax sass, or you can uncomment this: 27 | # preferred_syntax = :sass 28 | # and then run: 29 | # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass 30 | -------------------------------------------------------------------------------- /src/foundation-scss/humans.txt: -------------------------------------------------------------------------------- 1 | /* Foundation was made by ZURB, an interaction design and design strategy firm in Campbell, CA */ 2 | /* zurb.com */ 3 | /* humanstxt.org */ 4 | 5 | /* SITE */ 6 | Standards: HTML5, CSS3 7 | Components: jQuery, Orbit, Reveal 8 | Software: Coda, Textmate, Git -------------------------------------------------------------------------------- /src/foundation-scss/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Foundation 4 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 |

Welcome to Foundation

24 | 25 |

This is version 4.2.3.

26 |
27 |
28 |
29 | 30 |
31 |
32 |

The Grid

33 | 34 | 35 |
36 |
37 |
38 |

This is a twelve column section in a row. Each of these 39 | includes a div.panel element so you can see where the 40 | columns are - it's not required at all for the grid.

41 |
42 |
43 |
44 |
45 |
46 |
47 |

Six columns

48 |
49 |
50 |
51 |
52 |

Six columns

53 |
54 |
55 |
56 |
57 |
58 |
59 |

Four columns

60 |
61 |
62 |
63 |
64 |

Four columns

65 |
66 |
67 |
68 |
69 |

Four columns

70 |
71 |
72 |
73 | 74 |

Buttons

75 | 76 |
77 |
78 |

Small Button

79 | 80 |

Medium Button

81 | 82 |

Large Button

83 |
84 |
85 |

Small Alert Button 86 |

87 | 88 |

Medium Success Button 89 |

90 | 91 |

Large Secondary 92 | Button

93 |
94 |
95 |
96 | 97 |
98 |

Getting Started

99 | 100 |

We're stoked you want to try Foundation! To get going, this file 101 | (index.html) includes some basic styles you can modify, play around 102 | with, or totally destroy to get going.

103 | 104 |

Other Resources

105 | 106 |

Once you've exhausted the fun in this document, you should check 107 | out:

108 |
    109 |
  • Foundation 110 | Documentation
    Everything you need to know about using 111 | the framework. 112 |
  • 113 |
  • Foundation on 114 | Github
    Latest code, issue reports, feature requests and 115 | more. 116 |
  • 117 |
  • 118 | @foundationzurb
    Ping 119 | us on Twitter if you have questions. If you build something with 120 | this we'd love to see it (and send you a totally boss sticker). 121 |
  • 122 |
123 |
124 |
125 | 126 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /src/foundation-scss/javascripts/foundation/foundation.alerts.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ; 4 | (function ($, window, document, undefined) { 5 | 'use strict'; 6 | 7 | Foundation.libs.alerts = { 8 | name: 'alerts', 9 | 10 | version: '4.2.2', 11 | 12 | settings: { 13 | speed: 300, // fade out speed 14 | callback: function () { 15 | } 16 | }, 17 | 18 | init: function (scope, method, options) { 19 | this.scope = scope || this.scope; 20 | 21 | if (typeof method === 'object') { 22 | $.extend(true, this.settings, method); 23 | } 24 | 25 | if (typeof method !== 'string') { 26 | if (!this.settings.init) { 27 | this.events(); 28 | } 29 | 30 | return this.settings.init; 31 | } else { 32 | return this[method].call(this, options); 33 | } 34 | }, 35 | 36 | events: function () { 37 | var self = this; 38 | 39 | $(this.scope).on('click.fndtn.alerts', '[data-alert] a.close', function (e) { 40 | e.preventDefault(); 41 | $(this).closest("[data-alert]").fadeOut(self.speed, function () { 42 | $(this).remove(); 43 | self.settings.callback(); 44 | }); 45 | }); 46 | 47 | this.settings.init = true; 48 | }, 49 | 50 | off: function () { 51 | $(this.scope).off('.fndtn.alerts'); 52 | }, 53 | 54 | reflow: function () { 55 | } 56 | }; 57 | }(Foundation.zj, this, this.document)); -------------------------------------------------------------------------------- /src/foundation-scss/javascripts/foundation/foundation.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin v1.3 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2011, Klaus Hartl 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.opensource.org/licenses/GPL-2.0 9 | * 10 | * Modified to work with Zepto.js by ZURB 11 | */ 12 | (function ($, document, undefined) { 13 | 14 | var pluses = /\+/g; 15 | 16 | function raw(s) { 17 | return s; 18 | } 19 | 20 | function decoded(s) { 21 | return decodeURIComponent(s.replace(pluses, ' ')); 22 | } 23 | 24 | var config = $.cookie = function (key, value, options) { 25 | 26 | // write 27 | if (value !== undefined) { 28 | options = $.extend({}, config.defaults, options); 29 | 30 | if (value === null) { 31 | options.expires = -1; 32 | } 33 | 34 | if (typeof options.expires === 'number') { 35 | var days = options.expires, t = options.expires = new Date(); 36 | t.setDate(t.getDate() + days); 37 | } 38 | 39 | value = config.json ? JSON.stringify(value) : String(value); 40 | 41 | return (document.cookie = [ 42 | encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value), 43 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 44 | options.path ? '; path=' + options.path : '', 45 | options.domain ? '; domain=' + options.domain : '', 46 | options.secure ? '; secure' : '' 47 | ].join('')); 48 | } 49 | 50 | // read 51 | var decode = config.raw ? raw : decoded; 52 | var cookies = document.cookie.split('; '); 53 | for (var i = 0, l = cookies.length; i < l; i++) { 54 | var parts = cookies[i].split('='); 55 | if (decode(parts.shift()) === key) { 56 | var cookie = decode(parts.join('=')); 57 | return config.json ? JSON.parse(cookie) : cookie; 58 | } 59 | } 60 | 61 | return null; 62 | }; 63 | 64 | config.defaults = {}; 65 | 66 | $.removeCookie = function (key, options) { 67 | if ($.cookie(key) !== null) { 68 | $.cookie(key, null, options); 69 | return true; 70 | } 71 | return false; 72 | }; 73 | 74 | })(Foundation.zj, document); -------------------------------------------------------------------------------- /src/foundation-scss/javascripts/foundation/foundation.dropdown.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ; 4 | (function ($, window, document, undefined) { 5 | 'use strict'; 6 | 7 | Foundation.libs.dropdown = { 8 | name: 'dropdown', 9 | 10 | version: '4.2.0', 11 | 12 | settings: { 13 | activeClass: 'open', 14 | is_hover: false, 15 | opened: function () { 16 | }, 17 | closed: function () { 18 | } 19 | }, 20 | 21 | init: function (scope, method, options) { 22 | this.scope = scope || this.scope; 23 | Foundation.inherit(this, 'throttle scrollLeft data_options'); 24 | 25 | if (typeof method === 'object') { 26 | $.extend(true, this.settings, method); 27 | } 28 | 29 | if (typeof method !== 'string') { 30 | 31 | if (!this.settings.init) { 32 | this.events(); 33 | } 34 | 35 | return this.settings.init; 36 | } else { 37 | return this[method].call(this, options); 38 | } 39 | }, 40 | 41 | events: function () { 42 | var self = this; 43 | 44 | $(this.scope) 45 | .on('click.fndtn.dropdown', '[data-dropdown]', function (e) { 46 | var settings = $.extend({}, self.settings, self.data_options($(this))); 47 | e.preventDefault(); 48 | 49 | if (!settings.is_hover) self.toggle($(this)); 50 | }) 51 | .on('mouseenter', '[data-dropdown]', function (e) { 52 | var settings = $.extend({}, self.settings, self.data_options($(this))); 53 | if (settings.is_hover) self.toggle($(this)); 54 | }) 55 | .on('mouseleave', '[data-dropdown-content]', function (e) { 56 | var target = $('[data-dropdown="' + $(this).attr('id') + '"]'), 57 | settings = $.extend({}, self.settings, self.data_options(target)); 58 | if (settings.is_hover) self.close.call(self, $(this)); 59 | }) 60 | .on('opened.fndtn.dropdown', '[data-dropdown-content]', this.settings.opened) 61 | .on('closed.fndtn.dropdown', '[data-dropdown-content]', this.settings.closed); 62 | 63 | $('body').on('click.fndtn.dropdown', function (e) { 64 | var parent = $(e.target).closest('[data-dropdown-content]'); 65 | 66 | if ($(e.target).data('dropdown')) { 67 | return; 68 | } 69 | if (parent.length > 0 && ($(e.target).is('[data-dropdown-content]') || $.contains(parent.first()[0], e.target))) { 70 | e.stopPropagation(); 71 | return; 72 | } 73 | 74 | self.close.call(self, $('[data-dropdown-content]')); 75 | }); 76 | 77 | $(window).on('resize.fndtn.dropdown', self.throttle(function () { 78 | self.resize.call(self); 79 | }, 50)).trigger('resize'); 80 | 81 | this.settings.init = true; 82 | }, 83 | 84 | close: function (dropdown) { 85 | var self = this; 86 | dropdown.each(function () { 87 | if ($(this).hasClass(self.settings.activeClass)) { 88 | $(this) 89 | .css(Foundation.rtl ? 'right' : 'left', '-99999px') 90 | .removeClass(self.settings.activeClass); 91 | $(this).trigger('closed'); 92 | } 93 | }); 94 | }, 95 | 96 | open: function (dropdown, target) { 97 | this 98 | .css(dropdown 99 | .addClass(this.settings.activeClass), target); 100 | dropdown.trigger('opened'); 101 | }, 102 | 103 | toggle: function (target) { 104 | var dropdown = $('#' + target.data('dropdown')); 105 | 106 | this.close.call(this, $('[data-dropdown-content]').not(dropdown)); 107 | 108 | if (dropdown.hasClass(this.settings.activeClass)) { 109 | this.close.call(this, dropdown); 110 | } else { 111 | this.close.call(this, $('[data-dropdown-content]')); 112 | this.open.call(this, dropdown, target); 113 | } 114 | }, 115 | 116 | resize: function () { 117 | var dropdown = $('[data-dropdown-content].open'), 118 | target = $("[data-dropdown='" + dropdown.attr('id') + "']"); 119 | 120 | if (dropdown.length && target.length) { 121 | this.css(dropdown, target); 122 | } 123 | }, 124 | 125 | css: function (dropdown, target) { 126 | var offset_parent = dropdown.offsetParent(); 127 | // temporary workaround until 4.2 128 | if (offset_parent.length > 0 && /body/i.test(dropdown.offsetParent()[0].nodeName)) { 129 | var position = target.offset(); 130 | position.top -= dropdown.offsetParent().offset().top; 131 | position.left -= dropdown.offsetParent().offset().left; 132 | } else { 133 | var position = target.position(); 134 | } 135 | 136 | if (this.small()) { 137 | dropdown.css({ 138 | position: 'absolute', 139 | width: '95%', 140 | left: '2.5%', 141 | 'max-width': 'none', 142 | top: position.top + this.outerHeight(target) 143 | }); 144 | } else { 145 | if (!Foundation.rtl && $(window).width() > this.outerWidth(dropdown) + target.offset().left) { 146 | var left = position.left; 147 | if (dropdown.hasClass('right')) { 148 | dropdown.removeClass('right'); 149 | } 150 | } else { 151 | if (!dropdown.hasClass('right')) { 152 | dropdown.addClass('right'); 153 | } 154 | var left = position.left - (this.outerWidth(dropdown) - this.outerWidth(target)); 155 | } 156 | 157 | dropdown.attr('style', '').css({ 158 | position: 'absolute', 159 | top: position.top + this.outerHeight(target), 160 | left: left 161 | }); 162 | } 163 | 164 | return dropdown; 165 | }, 166 | 167 | small: function () { 168 | return $(window).width() < 768 || $('html').hasClass('lt-ie9'); 169 | }, 170 | 171 | off: function () { 172 | $(this.scope).off('.fndtn.dropdown'); 173 | $('html, body').off('.fndtn.dropdown'); 174 | $(window).off('.fndtn.dropdown'); 175 | $('[data-dropdown-content]').off('.fndtn.dropdown'); 176 | this.settings.init = false; 177 | }, 178 | 179 | reflow: function () { 180 | } 181 | }; 182 | }(Foundation.zj, this, this.document)); 183 | -------------------------------------------------------------------------------- /src/foundation-scss/javascripts/foundation/foundation.interchange.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ; 4 | (function ($, window, document, undefined) { 5 | 'use strict'; 6 | 7 | Foundation.libs.interchange = { 8 | name: 'interchange', 9 | 10 | version: '4.2.2', 11 | 12 | cache: {}, 13 | 14 | settings: { 15 | load_attr: 'interchange', 16 | 17 | named_queries: { 18 | 'default': 'only screen and (min-width: 1px)', 19 | small: 'only screen and (min-width: 768px)', 20 | medium: 'only screen and (min-width: 1280px)', 21 | large: 'only screen and (min-width: 1440px)', 22 | landscape: 'only screen and (orientation: landscape)', 23 | portrait: 'only screen and (orientation: portrait)', 24 | retina: 'only screen and (-webkit-min-device-pixel-ratio: 2),' + 25 | 'only screen and (min--moz-device-pixel-ratio: 2),' + 26 | 'only screen and (-o-min-device-pixel-ratio: 2/1),' + 27 | 'only screen and (min-device-pixel-ratio: 2),' + 28 | 'only screen and (min-resolution: 192dpi),' + 29 | 'only screen and (min-resolution: 2dppx)' 30 | }, 31 | 32 | directives: { 33 | replace: function (el, path) { 34 | if (/IMG/.test(el[0].nodeName)) { 35 | var path_parts = path.split('/'), 36 | path_file = path_parts[path_parts.length - 1], 37 | orig_path = el[0].src; 38 | 39 | if (new RegExp(path_file, 'i').test(el[0].src)) return; 40 | 41 | el[0].src = path; 42 | 43 | return el.trigger('replace', [el[0].src, orig_path]); 44 | } 45 | } 46 | } 47 | }, 48 | 49 | init: function (scope, method, options) { 50 | Foundation.inherit(this, 'throttle'); 51 | 52 | if (typeof method === 'object') { 53 | $.extend(true, this.settings, method); 54 | } 55 | 56 | this.events(); 57 | this.images(); 58 | 59 | if (typeof method !== 'string') { 60 | return this.settings.init; 61 | } else { 62 | return this[method].call(this, options); 63 | } 64 | }, 65 | 66 | events: function () { 67 | var self = this; 68 | 69 | $(window).on('resize.fndtn.interchange', self.throttle(function () { 70 | self.resize.call(self); 71 | }, 50)); 72 | }, 73 | 74 | resize: function () { 75 | var cache = this.cache; 76 | 77 | for (var uuid in cache) { 78 | if (cache.hasOwnProperty(uuid)) { 79 | var passed = this.results(uuid, cache[uuid]); 80 | 81 | if (passed) { 82 | this.settings.directives[passed 83 | .scenario[1]](passed.el, passed.scenario[0]); 84 | } 85 | } 86 | } 87 | 88 | }, 89 | 90 | results: function (uuid, scenarios) { 91 | var count = scenarios.length, 92 | results_arr = []; 93 | 94 | if (count > 0) { 95 | var el = $('[data-uuid="' + uuid + '"]'); 96 | 97 | for (var i = count - 1; i >= 0; i--) { 98 | var rule = scenarios[i][2]; 99 | if (this.settings.named_queries.hasOwnProperty(rule)) { 100 | var mq = matchMedia(this.settings.named_queries[rule]); 101 | } else { 102 | var mq = matchMedia(scenarios[i][2]); 103 | } 104 | if (mq.matches) { 105 | return {el: el, scenario: scenarios[i]}; 106 | } 107 | } 108 | } 109 | 110 | return false; 111 | }, 112 | 113 | images: function (force_update) { 114 | if (typeof this.cached_images === 'undefined' || force_update) { 115 | return this.update_images(); 116 | } 117 | 118 | return this.cached_images; 119 | }, 120 | 121 | update_images: function () { 122 | var images = document.getElementsByTagName('img'), 123 | count = images.length, 124 | data_attr = 'data-' + this.settings.load_attr; 125 | 126 | this.cached_images = []; 127 | 128 | for (var i = count - 1; i >= 0; i--) { 129 | this.loaded($(images[i]), (i === 0), function (image, last) { 130 | if (image) { 131 | var str = image.getAttribute(data_attr) || ''; 132 | 133 | if (str.length > 0) { 134 | this.cached_images.push(image); 135 | } 136 | } 137 | 138 | if (last) this.enhance(); 139 | 140 | }.bind(this)); 141 | } 142 | 143 | return 'deferred'; 144 | }, 145 | 146 | // based on jquery.imageready.js 147 | // @weblinc, @jsantell, (c) 2012 148 | 149 | loaded: function (image, last, callback) { 150 | function loaded() { 151 | callback(image[0], last); 152 | } 153 | 154 | function bindLoad() { 155 | this.one('load', loaded); 156 | 157 | if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { 158 | var src = this.attr('src'), 159 | param = src.match(/\?/) ? '&' : '?'; 160 | 161 | param += 'random=' + (new Date()).getTime(); 162 | this.attr('src', src + param); 163 | } 164 | } 165 | 166 | if (!image.attr('src')) { 167 | loaded(); 168 | return; 169 | } 170 | 171 | if (image[0].complete || image[0].readyState === 4) { 172 | loaded(); 173 | } else { 174 | bindLoad.call(image); 175 | } 176 | }, 177 | 178 | enhance: function () { 179 | var count = this.images().length; 180 | 181 | for (var i = count - 1; i >= 0; i--) { 182 | this._object($(this.images()[i])); 183 | } 184 | 185 | return $(window).trigger('resize'); 186 | }, 187 | 188 | parse_params: function (path, directive, mq) { 189 | return [this.trim(path), this.convert_directive(directive), this.trim(mq)]; 190 | }, 191 | 192 | convert_directive: function (directive) { 193 | var trimmed = this.trim(directive); 194 | 195 | if (trimmed.length > 0) { 196 | return trimmed; 197 | } 198 | 199 | return 'replace'; 200 | }, 201 | 202 | _object: function (el) { 203 | var raw_arr = this.parse_data_attr(el), 204 | scenarios = [], count = raw_arr.length; 205 | 206 | if (count > 0) { 207 | for (var i = count - 1; i >= 0; i--) { 208 | var split = raw_arr[i].split(/\((.*?)(\))$/); 209 | 210 | if (split.length > 1) { 211 | var cached_split = split[0].split(','), 212 | params = this.parse_params(cached_split[0], 213 | cached_split[1], split[1]); 214 | 215 | scenarios.push(params); 216 | } 217 | } 218 | } 219 | 220 | return this.store(el, scenarios); 221 | }, 222 | 223 | uuid: function (separator) { 224 | var delim = separator || "-"; 225 | 226 | function S4() { 227 | return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 228 | } 229 | 230 | return (S4() + S4() + delim + S4() + delim + S4() 231 | + delim + S4() + delim + S4() + S4() + S4()); 232 | }, 233 | 234 | store: function (el, scenarios) { 235 | var uuid = this.uuid(), 236 | current_uuid = el.data('uuid'); 237 | 238 | if (current_uuid) return this.cache[current_uuid]; 239 | 240 | el.attr('data-uuid', uuid); 241 | 242 | return this.cache[uuid] = scenarios; 243 | }, 244 | 245 | trim: function (str) { 246 | if (typeof str === 'string') { 247 | return $.trim(str); 248 | } 249 | 250 | return str; 251 | }, 252 | 253 | parse_data_attr: function (el) { 254 | var raw = el.data(this.settings.load_attr).split(/\[(.*?)\]/), 255 | count = raw.length, output = []; 256 | 257 | for (var i = count - 1; i >= 0; i--) { 258 | if (raw[i].replace(/[\W\d]+/, '').length > 4) { 259 | output.push(raw[i]); 260 | } 261 | } 262 | 263 | return output; 264 | }, 265 | 266 | reflow: function () { 267 | this.images(true); 268 | } 269 | 270 | }; 271 | 272 | }(Foundation.zj, this, this.document)); -------------------------------------------------------------------------------- /src/foundation-scss/javascripts/foundation/foundation.magellan.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ; 4 | (function ($, window, document, undefined) { 5 | 'use strict'; 6 | 7 | Foundation.libs.magellan = { 8 | name: 'magellan', 9 | 10 | version: '4.2.2', 11 | 12 | settings: { 13 | activeClass: 'active' 14 | }, 15 | 16 | init: function (scope, method, options) { 17 | this.scope = scope || this.scope; 18 | Foundation.inherit(this, 'data_options'); 19 | 20 | if (typeof method === 'object') { 21 | $.extend(true, this.settings, method); 22 | } 23 | 24 | if (typeof method !== 'string') { 25 | if (!this.settings.init) { 26 | this.fixed_magellan = $("[data-magellan-expedition]"); 27 | this.set_threshold(); 28 | this.last_destination = $('[data-magellan-destination]').last(); 29 | this.events(); 30 | } 31 | 32 | return this.settings.init; 33 | } else { 34 | return this[method].call(this, options); 35 | } 36 | }, 37 | 38 | events: function () { 39 | var self = this; 40 | $(this.scope).on('arrival.fndtn.magellan', '[data-magellan-arrival]', function (e) { 41 | var $destination = $(this), 42 | $expedition = $destination.closest('[data-magellan-expedition]'), 43 | activeClass = $expedition.attr('data-magellan-active-class') 44 | || self.settings.activeClass; 45 | 46 | $destination 47 | .closest('[data-magellan-expedition]') 48 | .find('[data-magellan-arrival]') 49 | .not($destination) 50 | .removeClass(activeClass); 51 | $destination.addClass(activeClass); 52 | }); 53 | 54 | this.fixed_magellan 55 | .on('update-position.fndtn.magellan', function () { 56 | var $el = $(this); 57 | // $el.data("magellan-fixed-position",""); 58 | // $el.data("magellan-top-offset", ""); 59 | }) 60 | .trigger('update-position'); 61 | 62 | $(window) 63 | .on('resize.fndtn.magellan', function () { 64 | this.fixed_magellan.trigger('update-position'); 65 | }.bind(this)) 66 | 67 | .on('scroll.fndtn.magellan', function () { 68 | var windowScrollTop = $(window).scrollTop(); 69 | self.fixed_magellan.each(function () { 70 | var $expedition = $(this); 71 | if (typeof $expedition.data('magellan-top-offset') === 'undefined') { 72 | $expedition.data('magellan-top-offset', $expedition.offset().top); 73 | } 74 | if (typeof $expedition.data('magellan-fixed-position') === 'undefined') { 75 | $expedition.data('magellan-fixed-position', false) 76 | } 77 | var fixed_position = (windowScrollTop + self.settings.threshold) > $expedition.data("magellan-top-offset"); 78 | var attr = $expedition.attr('data-magellan-top-offset'); 79 | 80 | if ($expedition.data("magellan-fixed-position") != fixed_position) { 81 | $expedition.data("magellan-fixed-position", fixed_position); 82 | if (fixed_position) { 83 | $expedition.addClass('fixed'); 84 | $expedition.css({position: "fixed", top: 0}); 85 | } else { 86 | $expedition.removeClass('fixed'); 87 | $expedition.css({position: "", top: ""}); 88 | } 89 | if (fixed_position && typeof attr != 'undefined' && attr != false) { 90 | $expedition.css({ 91 | position: "fixed", 92 | top: attr + "px" 93 | }); 94 | } 95 | } 96 | }); 97 | }); 98 | 99 | 100 | if (this.last_destination.length > 0) { 101 | $(window).on('scroll.fndtn.magellan', function (e) { 102 | var windowScrollTop = $(window).scrollTop(), 103 | scrolltopPlusHeight = windowScrollTop + $(window).height(), 104 | lastDestinationTop = Math.ceil(self.last_destination.offset().top); 105 | 106 | $('[data-magellan-destination]').each(function () { 107 | var $destination = $(this), 108 | destination_name = $destination.attr('data-magellan-destination'), 109 | topOffset = $destination.offset().top - windowScrollTop; 110 | 111 | if (topOffset <= self.settings.threshold) { 112 | $("[data-magellan-arrival='" + destination_name + "']").trigger('arrival'); 113 | } 114 | // In large screens we may hit the bottom of the page and dont reach the top of the last magellan-destination, so lets force it 115 | if (scrolltopPlusHeight >= $(self.scope).height() && lastDestinationTop > windowScrollTop && lastDestinationTop < scrolltopPlusHeight) { 116 | $('[data-magellan-arrival]').last().trigger('arrival'); 117 | } 118 | }); 119 | }); 120 | } 121 | 122 | this.settings.init = true; 123 | }, 124 | 125 | set_threshold: function () { 126 | if (!this.settings.threshold) { 127 | this.settings.threshold = (this.fixed_magellan.length > 0) ? 128 | this.outerHeight(this.fixed_magellan, true) : 0; 129 | } 130 | }, 131 | 132 | off: function () { 133 | $(this.scope).off('.fndtn.magellan'); 134 | }, 135 | 136 | reflow: function () { 137 | } 138 | }; 139 | }(Foundation.zj, this, this.document)); -------------------------------------------------------------------------------- /src/foundation-scss/javascripts/foundation/foundation.placeholder.js: -------------------------------------------------------------------------------- 1 | /*! http://mths.be/placeholder v2.0.7 by @mathias 2 | Modified to work with Zepto.js by ZURB 3 | */ 4 | ; 5 | (function (window, document, $) { 6 | 7 | var isInputSupported = 'placeholder' in document.createElement('input'), 8 | isTextareaSupported = 'placeholder' in document.createElement('textarea'), 9 | prototype = $.fn, 10 | valHooks = $.valHooks, 11 | hooks, 12 | placeholder; 13 | 14 | if (isInputSupported && isTextareaSupported) { 15 | 16 | placeholder = prototype.placeholder = function () { 17 | return this; 18 | }; 19 | 20 | placeholder.input = placeholder.textarea = true; 21 | 22 | } else { 23 | 24 | placeholder = prototype.placeholder = function () { 25 | var $this = this; 26 | $this 27 | .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]') 28 | .not('.placeholder') 29 | .bind({ 30 | 'focus.placeholder': clearPlaceholder, 31 | 'blur.placeholder': setPlaceholder 32 | }) 33 | .data('placeholder-enabled', true) 34 | .trigger('blur.placeholder'); 35 | return $this; 36 | }; 37 | 38 | placeholder.input = isInputSupported; 39 | placeholder.textarea = isTextareaSupported; 40 | 41 | hooks = { 42 | 'get': function (element) { 43 | var $element = $(element); 44 | return $element.data('placeholder-enabled') && $element.hasClass('placeholder') ? '' : element.value; 45 | }, 46 | 'set': function (element, value) { 47 | var $element = $(element); 48 | if (!$element.data('placeholder-enabled')) { 49 | return element.value = value; 50 | } 51 | if (value == '') { 52 | element.value = value; 53 | // Issue #56: Setting the placeholder causes problems if the element continues to have focus. 54 | if (element != document.activeElement) { 55 | // We can't use `triggerHandler` here because of dummy text/password inputs :( 56 | setPlaceholder.call(element); 57 | } 58 | } else if ($element.hasClass('placeholder')) { 59 | clearPlaceholder.call(element, true, value) || (element.value = value); 60 | } else { 61 | element.value = value; 62 | } 63 | // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363 64 | return $element; 65 | } 66 | }; 67 | 68 | isInputSupported || (valHooks.input = hooks); 69 | isTextareaSupported || (valHooks.textarea = hooks); 70 | 71 | $(function () { 72 | // Look for forms 73 | $(document).delegate('form', 'submit.placeholder', function () { 74 | // Clear the placeholder values so they don't get submitted 75 | var $inputs = $('.placeholder', this).each(clearPlaceholder); 76 | setTimeout(function () { 77 | $inputs.each(setPlaceholder); 78 | }, 10); 79 | }); 80 | }); 81 | 82 | // Clear placeholder values upon page reload 83 | $(window).bind('beforeunload.placeholder', function () { 84 | $('.placeholder').each(function () { 85 | this.value = ''; 86 | }); 87 | }); 88 | 89 | } 90 | 91 | function args(elem) { 92 | // Return an object of element attributes 93 | var newAttrs = {}, 94 | rinlinejQuery = /^jQuery\d+$/; 95 | $.each(elem.attributes, function (i, attr) { 96 | if (attr.specified && !rinlinejQuery.test(attr.name)) { 97 | newAttrs[attr.name] = attr.value; 98 | } 99 | }); 100 | return newAttrs; 101 | } 102 | 103 | function clearPlaceholder(event, value) { 104 | var input = this, 105 | $input = $(input); 106 | if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) { 107 | if ($input.data('placeholder-password')) { 108 | $input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id')); 109 | // If `clearPlaceholder` was called from `$.valHooks.input.set` 110 | if (event === true) { 111 | return $input[0].value = value; 112 | } 113 | $input.focus(); 114 | } else { 115 | input.value = ''; 116 | $input.removeClass('placeholder'); 117 | input == document.activeElement && input.select(); 118 | } 119 | } 120 | } 121 | 122 | function setPlaceholder() { 123 | var $replacement, 124 | input = this, 125 | $input = $(input), 126 | $origInput = $input, 127 | id = this.id; 128 | if (input.value == '') { 129 | if (input.type == 'password') { 130 | if (!$input.data('placeholder-textinput')) { 131 | try { 132 | $replacement = $input.clone().attr({'type': 'text'}); 133 | } catch (e) { 134 | $replacement = $('').attr($.extend(args(this), {'type': 'text'})); 135 | } 136 | $replacement 137 | .removeAttr('name') 138 | .data({ 139 | 'placeholder-password': true, 140 | 'placeholder-id': id 141 | }) 142 | .bind('focus.placeholder', clearPlaceholder); 143 | $input 144 | .data({ 145 | 'placeholder-textinput': $replacement, 146 | 'placeholder-id': id 147 | }) 148 | .before($replacement); 149 | } 150 | $input = $input.removeAttr('id').hide().prev().attr('id', id).show(); 151 | // Note: `$input[0] != input` now! 152 | } 153 | $input.addClass('placeholder'); 154 | $input[0].value = $input.attr('placeholder'); 155 | } else { 156 | $input.removeClass('placeholder'); 157 | } 158 | } 159 | 160 | }(this, document, Foundation.zj)); 161 | 162 | (function ($, window, document, undefined) { 163 | 'use strict'; 164 | 165 | Foundation.libs.placeholder = { 166 | name: 'placeholder', 167 | 168 | version: '4.2.2', 169 | 170 | init: function (scope, method, options) { 171 | this.scope = scope || this.scope; 172 | 173 | if (typeof method !== 'string') { 174 | window.onload = function () { 175 | $('input, textarea').placeholder(); 176 | } 177 | } 178 | } 179 | }; 180 | }(Foundation.zj, this, this.document)); -------------------------------------------------------------------------------- /src/foundation-scss/javascripts/foundation/foundation.tooltips.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ; 4 | (function ($, window, document, undefined) { 5 | 'use strict'; 6 | 7 | Foundation.libs.tooltips = { 8 | name: 'tooltips', 9 | 10 | version: '4.2.2', 11 | 12 | settings: { 13 | selector: '.has-tip', 14 | additionalInheritableClasses: [], 15 | tooltipClass: '.tooltip', 16 | appendTo: 'body', 17 | 'disable-for-touch': false, 18 | tipTemplate: function (selector, content) { 19 | return '' + content + ''; 22 | } 23 | }, 24 | 25 | cache: {}, 26 | 27 | init: function (scope, method, options) { 28 | Foundation.inherit(this, 'data_options'); 29 | var self = this; 30 | 31 | if (typeof method === 'object') { 32 | $.extend(true, this.settings, method); 33 | } else if (typeof options !== 'undefined') { 34 | $.extend(true, this.settings, options); 35 | } 36 | 37 | if (typeof method !== 'string') { 38 | if (Modernizr.touch) { 39 | $(this.scope) 40 | .on('click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip', 41 | '[data-tooltip]', function (e) { 42 | var settings = $.extend({}, self.settings, self.data_options($(this))); 43 | if (!settings['disable-for-touch']) { 44 | e.preventDefault(); 45 | $(settings.tooltipClass).hide(); 46 | self.showOrCreateTip($(this)); 47 | } 48 | }) 49 | .on('click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip', 50 | this.settings.tooltipClass, function (e) { 51 | e.preventDefault(); 52 | $(this).fadeOut(150); 53 | }); 54 | } else { 55 | $(this.scope) 56 | .on('mouseenter.fndtn.tooltip mouseleave.fndtn.tooltip', 57 | '[data-tooltip]', function (e) { 58 | var $this = $(this); 59 | 60 | if (/enter|over/i.test(e.type)) { 61 | self.showOrCreateTip($this); 62 | } else if (e.type === 'mouseout' || e.type === 'mouseleave') { 63 | self.hide($this); 64 | } 65 | }); 66 | } 67 | 68 | // $(this.scope).data('fndtn-tooltips', true); 69 | } else { 70 | return this[method].call(this, options); 71 | } 72 | 73 | }, 74 | 75 | showOrCreateTip: function ($target) { 76 | var $tip = this.getTip($target); 77 | 78 | if ($tip && $tip.length > 0) { 79 | return this.show($target); 80 | } 81 | 82 | return this.create($target); 83 | }, 84 | 85 | getTip: function ($target) { 86 | var selector = this.selector($target), 87 | tip = null; 88 | 89 | if (selector) { 90 | tip = $('span[data-selector="' + selector + '"]' + this.settings.tooltipClass); 91 | } 92 | 93 | return (typeof tip === 'object') ? tip : false; 94 | }, 95 | 96 | selector: function ($target) { 97 | var id = $target.attr('id'), 98 | dataSelector = $target.attr('data-tooltip') || $target.attr('data-selector'); 99 | 100 | if ((id && id.length < 1 || !id) && typeof dataSelector != 'string') { 101 | dataSelector = 'tooltip' + Math.random().toString(36).substring(7); 102 | $target.attr('data-selector', dataSelector); 103 | } 104 | 105 | return (id && id.length > 0) ? id : dataSelector; 106 | }, 107 | 108 | create: function ($target) { 109 | var $tip = $(this.settings.tipTemplate(this.selector($target), $('
').html($target.attr('title')).html())), 110 | classes = this.inheritable_classes($target); 111 | 112 | $tip.addClass(classes).appendTo(this.settings.appendTo); 113 | if (Modernizr.touch) { 114 | $tip.append('tap to close '); 115 | } 116 | $target.removeAttr('title').attr('title', ''); 117 | this.show($target); 118 | }, 119 | 120 | reposition: function (target, tip, classes) { 121 | var width, nub, nubHeight, nubWidth, column, objPos; 122 | 123 | tip.css('visibility', 'hidden').show(); 124 | 125 | width = target.data('width'); 126 | nub = tip.children('.nub'); 127 | nubHeight = this.outerHeight(nub); 128 | nubWidth = this.outerHeight(nub); 129 | 130 | objPos = function (obj, top, right, bottom, left, width) { 131 | return obj.css({ 132 | 'top': (top) ? top : 'auto', 133 | 'bottom': (bottom) ? bottom : 'auto', 134 | 'left': (left) ? left : 'auto', 135 | 'right': (right) ? right : 'auto', 136 | 'width': (width) ? width : 'auto' 137 | }).end(); 138 | }; 139 | 140 | objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', target.offset().left, width); 141 | 142 | if ($(window).width() < 767) { 143 | objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', 12.5, $(this.scope).width()); 144 | tip.addClass('tip-override'); 145 | objPos(nub, -nubHeight, 'auto', 'auto', target.offset().left); 146 | } else { 147 | var left = target.offset().left; 148 | if (Foundation.rtl) { 149 | left = target.offset().left + target.offset().width - this.outerWidth(tip); 150 | } 151 | objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', left, width); 152 | tip.removeClass('tip-override'); 153 | if (classes && classes.indexOf('tip-top') > -1) { 154 | objPos(tip, (target.offset().top - this.outerHeight(tip)), 'auto', 'auto', left, width) 155 | .removeClass('tip-override'); 156 | } else if (classes && classes.indexOf('tip-left') > -1) { 157 | objPos(tip, (target.offset().top + (this.outerHeight(target) / 2) - nubHeight * 2.5), 'auto', 'auto', (target.offset().left - this.outerWidth(tip) - nubHeight), width) 158 | .removeClass('tip-override'); 159 | } else if (classes && classes.indexOf('tip-right') > -1) { 160 | objPos(tip, (target.offset().top + (this.outerHeight(target) / 2) - nubHeight * 2.5), 'auto', 'auto', (target.offset().left + this.outerWidth(target) + nubHeight), width) 161 | .removeClass('tip-override'); 162 | } 163 | } 164 | 165 | tip.css('visibility', 'visible').hide(); 166 | }, 167 | 168 | inheritable_classes: function (target) { 169 | var inheritables = ['tip-top', 'tip-left', 'tip-bottom', 'tip-right', 'noradius'].concat(this.settings.additionalInheritableClasses), 170 | classes = target.attr('class'), 171 | filtered = classes ? $.map(classes.split(' '), function (el, i) { 172 | if ($.inArray(el, inheritables) !== -1) { 173 | return el; 174 | } 175 | }).join(' ') : ''; 176 | 177 | return $.trim(filtered); 178 | }, 179 | 180 | show: function ($target) { 181 | var $tip = this.getTip($target); 182 | 183 | this.reposition($target, $tip, $target.attr('class')); 184 | $tip.fadeIn(150); 185 | }, 186 | 187 | hide: function ($target) { 188 | var $tip = this.getTip($target); 189 | 190 | $tip.fadeOut(150); 191 | }, 192 | 193 | // deprecate reload 194 | reload: function () { 195 | var $self = $(this); 196 | 197 | return ($self.data('fndtn-tooltips')) ? $self.foundationTooltips('destroy').foundationTooltips('init') : $self.foundationTooltips('init'); 198 | }, 199 | 200 | off: function () { 201 | $(this.scope).off('.fndtn.tooltip'); 202 | $(this.settings.tooltipClass).each(function (i) { 203 | $('[data-tooltip]').get(i).attr('title', $(this).text()); 204 | }).remove(); 205 | }, 206 | 207 | reflow: function () { 208 | } 209 | }; 210 | }(Foundation.zj, this, this.document)); 211 | -------------------------------------------------------------------------------- /src/foundation-scss/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 3 | 4 | User-agent: * -------------------------------------------------------------------------------- /src/foundation-scss/sass/_normalize.scss: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.1.1 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /** 8 | * Correct `block` display not defined in IE 8/9. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | 26 | /** 27 | * Correct `inline-block` display not defined in IE 8/9. 28 | */ 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | } 35 | 36 | /** 37 | * Prevent modern browsers from displaying `audio` without controls. 38 | * Remove excess height in iOS 5 devices. 39 | */ 40 | 41 | audio:not([controls]) { 42 | display: none; 43 | height: 0; 44 | } 45 | 46 | /** 47 | * Address styling not present in IE 8/9. 48 | */ 49 | 50 | [hidden] { 51 | display: none; 52 | } 53 | 54 | /* ========================================================================== 55 | Base 56 | ========================================================================== */ 57 | 58 | /** 59 | * 1. Prevent system color scheme's background color being used in Firefox, IE, 60 | * and Opera. 61 | * 2. Prevent system color scheme's text color being used in Firefox, IE, and 62 | * Opera. 63 | * 3. Set default font family to sans-serif. 64 | * 4. Prevent iOS text size adjust after orientation change, without disabling 65 | * user zoom. 66 | */ 67 | 68 | html { 69 | background: #fff; /* 1 */ 70 | color: #000; /* 2 */ 71 | font-family: sans-serif; /* 3 */ 72 | -ms-text-size-adjust: 100%; /* 4 */ 73 | -webkit-text-size-adjust: 100%; /* 4 */ 74 | } 75 | 76 | /** 77 | * Remove default margin. 78 | */ 79 | 80 | body { 81 | margin: 0; 82 | } 83 | 84 | /* ========================================================================== 85 | Links 86 | ========================================================================== */ 87 | 88 | /** 89 | * Address `outline` inconsistency between Chrome and other browsers. 90 | */ 91 | 92 | a:focus { 93 | outline: thin dotted; 94 | } 95 | 96 | /** 97 | * Improve readability when focused and also mouse hovered in all browsers. 98 | */ 99 | 100 | a:active, 101 | a:hover { 102 | outline: 0; 103 | } 104 | 105 | /* ========================================================================== 106 | Typography 107 | ========================================================================== */ 108 | 109 | /** 110 | * Address variable `h1` font-size and margin within `section` and `article` 111 | * contexts in Firefox 4+, Safari 5, and Chrome. 112 | */ 113 | 114 | h1 { 115 | font-size: 2em; 116 | margin: 0.67em 0; 117 | } 118 | 119 | /** 120 | * Address styling not present in IE 8/9, Safari 5, and Chrome. 121 | */ 122 | 123 | abbr[title] { 124 | border-bottom: 1px dotted; 125 | } 126 | 127 | /** 128 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 129 | */ 130 | 131 | b, 132 | strong { 133 | font-weight: bold; 134 | } 135 | 136 | /** 137 | * Address styling not present in Safari 5 and Chrome. 138 | */ 139 | 140 | dfn { 141 | font-style: italic; 142 | } 143 | 144 | /** 145 | * Address differences between Firefox and other browsers. 146 | */ 147 | 148 | hr { 149 | -moz-box-sizing: content-box; 150 | box-sizing: content-box; 151 | height: 0; 152 | } 153 | 154 | /** 155 | * Address styling not present in IE 8/9. 156 | */ 157 | 158 | mark { 159 | background: #ff0; 160 | color: #000; 161 | } 162 | 163 | /** 164 | * Correct font family set oddly in Safari 5 and Chrome. 165 | */ 166 | 167 | code, 168 | kbd, 169 | pre, 170 | samp { 171 | font-family: monospace, serif; 172 | font-size: 1em; 173 | } 174 | 175 | /** 176 | * Improve readability of pre-formatted text in all browsers. 177 | */ 178 | 179 | pre { 180 | white-space: pre-wrap; 181 | } 182 | 183 | /** 184 | * Set consistent quote types. 185 | */ 186 | 187 | q { 188 | quotes: "\201C" "\201D" "\2018" "\2019"; 189 | } 190 | 191 | /** 192 | * Address inconsistent and variable font size in all browsers. 193 | */ 194 | 195 | small { 196 | font-size: 80%; 197 | } 198 | 199 | /** 200 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 201 | */ 202 | 203 | sub, 204 | sup { 205 | font-size: 75%; 206 | line-height: 0; 207 | position: relative; 208 | vertical-align: baseline; 209 | } 210 | 211 | sup { 212 | top: -0.5em; 213 | } 214 | 215 | sub { 216 | bottom: -0.25em; 217 | } 218 | 219 | /* ========================================================================== 220 | Embedded content 221 | ========================================================================== */ 222 | 223 | /** 224 | * Remove border when inside `a` element in IE 8/9. 225 | */ 226 | 227 | img { 228 | border: 0; 229 | } 230 | 231 | /** 232 | * Correct overflow displayed oddly in IE 9. 233 | */ 234 | 235 | svg:not(:root) { 236 | overflow: hidden; 237 | } 238 | 239 | /* ========================================================================== 240 | Figures 241 | ========================================================================== */ 242 | 243 | /** 244 | * Address margin not present in IE 8/9 and Safari 5. 245 | */ 246 | 247 | figure { 248 | margin: 0; 249 | } 250 | 251 | /* ========================================================================== 252 | Forms 253 | ========================================================================== */ 254 | 255 | /** 256 | * Define consistent border, margin, and padding. 257 | */ 258 | 259 | fieldset { 260 | border: 1px solid #c0c0c0; 261 | margin: 0 2px; 262 | padding: 0.35em 0.625em 0.75em; 263 | } 264 | 265 | /** 266 | * 1. Correct `color` not being inherited in IE 8/9. 267 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 268 | */ 269 | 270 | legend { 271 | border: 0; /* 1 */ 272 | padding: 0; /* 2 */ 273 | } 274 | 275 | /** 276 | * 1. Correct font family not being inherited in all browsers. 277 | * 2. Correct font size not being inherited in all browsers. 278 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. 279 | */ 280 | 281 | button, 282 | input, 283 | select, 284 | textarea { 285 | font-family: inherit; /* 1 */ 286 | font-size: 100%; /* 2 */ 287 | margin: 0; /* 3 */ 288 | } 289 | 290 | /** 291 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 292 | * the UA stylesheet. 293 | */ 294 | 295 | button, 296 | input { 297 | line-height: normal; 298 | } 299 | 300 | /** 301 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 302 | * All other form control elements do not inherit `text-transform` values. 303 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. 304 | * Correct `select` style inheritance in Firefox 4+ and Opera. 305 | */ 306 | 307 | button, 308 | select { 309 | text-transform: none; 310 | } 311 | 312 | /** 313 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 314 | * and `video` controls. 315 | * 2. Correct inability to style clickable `input` types in iOS. 316 | * 3. Improve usability and consistency of cursor style between image-type 317 | * `input` and others. 318 | */ 319 | 320 | button, 321 | html input[type="button"], /* 1 */ 322 | input[type="reset"], 323 | input[type="submit"] { 324 | -webkit-appearance: button; /* 2 */ 325 | cursor: pointer; /* 3 */ 326 | } 327 | 328 | /** 329 | * Re-set default cursor for disabled elements. 330 | */ 331 | 332 | button[disabled], 333 | html input[disabled] { 334 | cursor: default; 335 | } 336 | 337 | /** 338 | * 1. Address box sizing set to `content-box` in IE 8/9. 339 | * 2. Remove excess padding in IE 8/9. 340 | */ 341 | 342 | input[type="checkbox"], 343 | input[type="radio"] { 344 | box-sizing: border-box; /* 1 */ 345 | padding: 0; /* 2 */ 346 | } 347 | 348 | /** 349 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 350 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 351 | * (include `-moz` to future-proof). 352 | */ 353 | 354 | input[type="search"] { 355 | -webkit-appearance: textfield; /* 1 */ 356 | -moz-box-sizing: content-box; 357 | -webkit-box-sizing: content-box; /* 2 */ 358 | box-sizing: content-box; 359 | } 360 | 361 | /** 362 | * Remove inner padding and search cancel button in Safari 5 and Chrome 363 | * on OS X. 364 | */ 365 | 366 | input[type="search"]::-webkit-search-cancel-button, 367 | input[type="search"]::-webkit-search-decoration { 368 | -webkit-appearance: none; 369 | } 370 | 371 | /** 372 | * Remove inner padding and border in Firefox 4+. 373 | */ 374 | 375 | button::-moz-focus-inner, 376 | input::-moz-focus-inner { 377 | border: 0; 378 | padding: 0; 379 | } 380 | 381 | /** 382 | * 1. Remove default vertical scrollbar in IE 8/9. 383 | * 2. Improve readability and alignment in all browsers. 384 | */ 385 | 386 | textarea { 387 | overflow: auto; /* 1 */ 388 | vertical-align: top; /* 2 */ 389 | } 390 | 391 | /* ========================================================================== 392 | Tables 393 | ========================================================================== */ 394 | 395 | /** 396 | * Remove most spacing between table cells. 397 | */ 398 | 399 | table { 400 | border-collapse: collapse; 401 | border-spacing: 0; 402 | } 403 | -------------------------------------------------------------------------------- /src/foundation-scss/sass/app.scss: -------------------------------------------------------------------------------- 1 | // Global Foundation Settings 2 | @import "settings"; 3 | // Comment out this import if you don't want to use normalize 4 | @import "normalize"; 5 | // Comment out this import if you are customizing you imports below 6 | @import "foundation"; 7 | 8 | // Import specific parts of Foundation by commenting the import "foundation" 9 | // and uncommenting what you want below. You must uncomment the following if customizing 10 | 11 | // @import "foundation/components/global"; // *always required 12 | // @import "foundation/components/grid"; 13 | // @import "foundation/components/visibility"; 14 | // @import "foundation/components/block-grid"; 15 | // @import "foundation/components/type"; 16 | // @import "foundation/components/buttons"; 17 | // @import "foundation/components/forms"; // *requires components/buttons 18 | // @import "foundation/components/custom-forms"; // *requires components/buttons, components/forms 19 | // @import "foundation/components/button-groups"; // *requires components/buttons 20 | // @import "foundation/components/dropdown-buttons"; // *requires components/buttons 21 | // @import "foundation/components/split-buttons"; // *requires components/buttons 22 | // @import "foundation/components/flex-video"; 23 | // @import "foundation/components/section"; 24 | // @import "foundation/components/top-bar"; // *requires components/grid 25 | // @import "foundation/components/orbit"; 26 | // @import "foundation/components/reveal"; 27 | // @import "foundation/components/joyride"; 28 | // @import "foundation/components/clearing"; 29 | // @import "foundation/components/alert-boxes"; 30 | // @import "foundation/components/breadcrumbs"; 31 | // @import "foundation/components/keystrokes"; 32 | // @import "foundation/components/labels"; 33 | // @import "foundation/components/inline-lists"; 34 | // @import "foundation/components/pagination"; 35 | // @import "foundation/components/panels"; 36 | // @import "foundation/components/pricing-tables"; 37 | // @import "foundation/components/progress-bars"; 38 | // @import "foundation/components/side-nav"; 39 | // @import "foundation/components/sub-nav"; 40 | // @import "foundation/components/switch"; 41 | // @import "foundation/components/magellan"; 42 | // @import "foundation/components/tables"; 43 | // @import "foundation/components/thumbs"; 44 | // @import "foundation/components/tooltips"; 45 | // @import "foundation/components/dropdown"; 46 | 47 | // automx footer 48 | #footer-desktop { 49 | position: fixed; 50 | bottom: 1em; 51 | height: 2em; 52 | width: 100%; 53 | } 54 | 55 | .automx-powered { 56 | padding-right: 1em; 57 | } 58 | 59 | // company banner 60 | #company-banner img { 61 | margin: 2em 0; 62 | } 63 | -------------------------------------------------------------------------------- /src/html/img/automx-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sys4/automx/d50b25e43f3946ee304245f51035f38266af0e33/src/html/img/automx-banner.png -------------------------------------------------------------------------------- /src/html/img/company-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sys4/automx/d50b25e43f3946ee304245f51035f38266af0e33/src/html/img/company-banner.png -------------------------------------------------------------------------------- /src/html/index.html.de: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | automx - Mobile Clients 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 |
42 |
43 | 44 | 47 | 52 | 53 | 58 | 59 | 60 | 61 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/html/index.html.en: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | automx - Mobile Clients 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 |
43 |
44 | 45 | 48 | 53 | 54 | 59 | 60 | 61 | 62 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/html/js/foundation/foundation.alerts.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ; 4 | (function ($, window, document, undefined) { 5 | 'use strict'; 6 | 7 | Foundation.libs.alerts = { 8 | name: 'alerts', 9 | 10 | version: '4.2.2', 11 | 12 | settings: { 13 | speed: 300, // fade out speed 14 | callback: function () { 15 | } 16 | }, 17 | 18 | init: function (scope, method, options) { 19 | this.scope = scope || this.scope; 20 | 21 | if (typeof method === 'object') { 22 | $.extend(true, this.settings, method); 23 | } 24 | 25 | if (typeof method !== 'string') { 26 | if (!this.settings.init) { 27 | this.events(); 28 | } 29 | 30 | return this.settings.init; 31 | } else { 32 | return this[method].call(this, options); 33 | } 34 | }, 35 | 36 | events: function () { 37 | var self = this; 38 | 39 | $(this.scope).on('click.fndtn.alerts', '[data-alert] a.close', function (e) { 40 | e.preventDefault(); 41 | $(this).closest("[data-alert]").fadeOut(self.speed, function () { 42 | $(this).remove(); 43 | self.settings.callback(); 44 | }); 45 | }); 46 | 47 | this.settings.init = true; 48 | }, 49 | 50 | off: function () { 51 | $(this.scope).off('.fndtn.alerts'); 52 | }, 53 | 54 | reflow: function () { 55 | } 56 | }; 57 | }(Foundation.zj, this, this.document)); 58 | -------------------------------------------------------------------------------- /src/html/js/foundation/foundation.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin v1.3 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2011, Klaus Hartl 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.opensource.org/licenses/GPL-2.0 9 | * 10 | * Modified to work with Zepto.js by ZURB 11 | */ 12 | (function ($, document, undefined) { 13 | 14 | var pluses = /\+/g; 15 | 16 | function raw(s) { 17 | return s; 18 | } 19 | 20 | function decoded(s) { 21 | return decodeURIComponent(s.replace(pluses, ' ')); 22 | } 23 | 24 | var config = $.cookie = function (key, value, options) { 25 | 26 | // write 27 | if (value !== undefined) { 28 | options = $.extend({}, config.defaults, options); 29 | 30 | if (value === null) { 31 | options.expires = -1; 32 | } 33 | 34 | if (typeof options.expires === 'number') { 35 | var days = options.expires, t = options.expires = new Date(); 36 | t.setDate(t.getDate() + days); 37 | } 38 | 39 | value = config.json ? JSON.stringify(value) : String(value); 40 | 41 | return (document.cookie = [ 42 | encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value), 43 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 44 | options.path ? '; path=' + options.path : '', 45 | options.domain ? '; domain=' + options.domain : '', 46 | options.secure ? '; secure' : '' 47 | ].join('')); 48 | } 49 | 50 | // read 51 | var decode = config.raw ? raw : decoded; 52 | var cookies = document.cookie.split('; '); 53 | for (var i = 0, l = cookies.length; i < l; i++) { 54 | var parts = cookies[i].split('='); 55 | if (decode(parts.shift()) === key) { 56 | var cookie = decode(parts.join('=')); 57 | return config.json ? JSON.parse(cookie) : cookie; 58 | } 59 | } 60 | 61 | return null; 62 | }; 63 | 64 | config.defaults = {}; 65 | 66 | $.removeCookie = function (key, options) { 67 | if ($.cookie(key) !== null) { 68 | $.cookie(key, null, options); 69 | return true; 70 | } 71 | return false; 72 | }; 73 | 74 | })(Foundation.zj, document); 75 | -------------------------------------------------------------------------------- /src/html/js/foundation/foundation.dropdown.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ; 4 | (function ($, window, document, undefined) { 5 | 'use strict'; 6 | 7 | Foundation.libs.dropdown = { 8 | name: 'dropdown', 9 | 10 | version: '4.2.0', 11 | 12 | settings: { 13 | activeClass: 'open', 14 | is_hover: false, 15 | opened: function () { 16 | }, 17 | closed: function () { 18 | } 19 | }, 20 | 21 | init: function (scope, method, options) { 22 | this.scope = scope || this.scope; 23 | Foundation.inherit(this, 'throttle scrollLeft data_options'); 24 | 25 | if (typeof method === 'object') { 26 | $.extend(true, this.settings, method); 27 | } 28 | 29 | if (typeof method !== 'string') { 30 | 31 | if (!this.settings.init) { 32 | this.events(); 33 | } 34 | 35 | return this.settings.init; 36 | } else { 37 | return this[method].call(this, options); 38 | } 39 | }, 40 | 41 | events: function () { 42 | var self = this; 43 | 44 | $(this.scope) 45 | .on('click.fndtn.dropdown', '[data-dropdown]', function (e) { 46 | var settings = $.extend({}, self.settings, self.data_options($(this))); 47 | e.preventDefault(); 48 | 49 | if (!settings.is_hover) self.toggle($(this)); 50 | }) 51 | .on('mouseenter', '[data-dropdown]', function (e) { 52 | var settings = $.extend({}, self.settings, self.data_options($(this))); 53 | if (settings.is_hover) self.toggle($(this)); 54 | }) 55 | .on('mouseleave', '[data-dropdown-content]', function (e) { 56 | var target = $('[data-dropdown="' + $(this).attr('id') + '"]'), 57 | settings = $.extend({}, self.settings, self.data_options(target)); 58 | if (settings.is_hover) self.close.call(self, $(this)); 59 | }) 60 | .on('opened.fndtn.dropdown', '[data-dropdown-content]', this.settings.opened) 61 | .on('closed.fndtn.dropdown', '[data-dropdown-content]', this.settings.closed); 62 | 63 | $('body').on('click.fndtn.dropdown', function (e) { 64 | var parent = $(e.target).closest('[data-dropdown-content]'); 65 | 66 | if ($(e.target).data('dropdown')) { 67 | return; 68 | } 69 | if (parent.length > 0 && ($(e.target).is('[data-dropdown-content]') || $.contains(parent.first()[0], e.target))) { 70 | e.stopPropagation(); 71 | return; 72 | } 73 | 74 | self.close.call(self, $('[data-dropdown-content]')); 75 | }); 76 | 77 | $(window).on('resize.fndtn.dropdown', self.throttle(function () { 78 | self.resize.call(self); 79 | }, 50)).trigger('resize'); 80 | 81 | this.settings.init = true; 82 | }, 83 | 84 | close: function (dropdown) { 85 | var self = this; 86 | dropdown.each(function () { 87 | if ($(this).hasClass(self.settings.activeClass)) { 88 | $(this) 89 | .css(Foundation.rtl ? 'right' : 'left', '-99999px') 90 | .removeClass(self.settings.activeClass); 91 | $(this).trigger('closed'); 92 | } 93 | }); 94 | }, 95 | 96 | open: function (dropdown, target) { 97 | this 98 | .css(dropdown 99 | .addClass(this.settings.activeClass), target); 100 | dropdown.trigger('opened'); 101 | }, 102 | 103 | toggle: function (target) { 104 | var dropdown = $('#' + target.data('dropdown')); 105 | 106 | this.close.call(this, $('[data-dropdown-content]').not(dropdown)); 107 | 108 | if (dropdown.hasClass(this.settings.activeClass)) { 109 | this.close.call(this, dropdown); 110 | } else { 111 | this.close.call(this, $('[data-dropdown-content]')); 112 | this.open.call(this, dropdown, target); 113 | } 114 | }, 115 | 116 | resize: function () { 117 | var dropdown = $('[data-dropdown-content].open'), 118 | target = $("[data-dropdown='" + dropdown.attr('id') + "']"); 119 | 120 | if (dropdown.length && target.length) { 121 | this.css(dropdown, target); 122 | } 123 | }, 124 | 125 | css: function (dropdown, target) { 126 | var offset_parent = dropdown.offsetParent(); 127 | // temporary workaround until 4.2 128 | if (offset_parent.length > 0 && /body/i.test(dropdown.offsetParent()[0].nodeName)) { 129 | var position = target.offset(); 130 | position.top -= dropdown.offsetParent().offset().top; 131 | position.left -= dropdown.offsetParent().offset().left; 132 | } else { 133 | var position = target.position(); 134 | } 135 | 136 | if (this.small()) { 137 | dropdown.css({ 138 | position: 'absolute', 139 | width: '95%', 140 | left: '2.5%', 141 | 'max-width': 'none', 142 | top: position.top + this.outerHeight(target) 143 | }); 144 | } else { 145 | if (!Foundation.rtl && $(window).width() > this.outerWidth(dropdown) + target.offset().left) { 146 | var left = position.left; 147 | if (dropdown.hasClass('right')) { 148 | dropdown.removeClass('right'); 149 | } 150 | } else { 151 | if (!dropdown.hasClass('right')) { 152 | dropdown.addClass('right'); 153 | } 154 | var left = position.left - (this.outerWidth(dropdown) - this.outerWidth(target)); 155 | } 156 | 157 | dropdown.attr('style', '').css({ 158 | position: 'absolute', 159 | top: position.top + this.outerHeight(target), 160 | left: left 161 | }); 162 | } 163 | 164 | return dropdown; 165 | }, 166 | 167 | small: function () { 168 | return $(window).width() < 768 || $('html').hasClass('lt-ie9'); 169 | }, 170 | 171 | off: function () { 172 | $(this.scope).off('.fndtn.dropdown'); 173 | $('html, body').off('.fndtn.dropdown'); 174 | $(window).off('.fndtn.dropdown'); 175 | $('[data-dropdown-content]').off('.fndtn.dropdown'); 176 | this.settings.init = false; 177 | }, 178 | 179 | reflow: function () { 180 | } 181 | }; 182 | }(Foundation.zj, this, this.document)); 183 | -------------------------------------------------------------------------------- /src/html/js/foundation/foundation.interchange.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ; 4 | (function ($, window, document, undefined) { 5 | 'use strict'; 6 | 7 | Foundation.libs.interchange = { 8 | name: 'interchange', 9 | 10 | version: '4.2.2', 11 | 12 | cache: {}, 13 | 14 | settings: { 15 | load_attr: 'interchange', 16 | 17 | named_queries: { 18 | 'default': 'only screen and (min-width: 1px)', 19 | small: 'only screen and (min-width: 768px)', 20 | medium: 'only screen and (min-width: 1280px)', 21 | large: 'only screen and (min-width: 1440px)', 22 | landscape: 'only screen and (orientation: landscape)', 23 | portrait: 'only screen and (orientation: portrait)', 24 | retina: 'only screen and (-webkit-min-device-pixel-ratio: 2),' + 25 | 'only screen and (min--moz-device-pixel-ratio: 2),' + 26 | 'only screen and (-o-min-device-pixel-ratio: 2/1),' + 27 | 'only screen and (min-device-pixel-ratio: 2),' + 28 | 'only screen and (min-resolution: 192dpi),' + 29 | 'only screen and (min-resolution: 2dppx)' 30 | }, 31 | 32 | directives: { 33 | replace: function (el, path) { 34 | if (/IMG/.test(el[0].nodeName)) { 35 | var path_parts = path.split('/'), 36 | path_file = path_parts[path_parts.length - 1], 37 | orig_path = el[0].src; 38 | 39 | if (new RegExp(path_file, 'i').test(el[0].src)) return; 40 | 41 | el[0].src = path; 42 | 43 | return el.trigger('replace', [el[0].src, orig_path]); 44 | } 45 | } 46 | } 47 | }, 48 | 49 | init: function (scope, method, options) { 50 | Foundation.inherit(this, 'throttle'); 51 | 52 | if (typeof method === 'object') { 53 | $.extend(true, this.settings, method); 54 | } 55 | 56 | this.events(); 57 | this.images(); 58 | 59 | if (typeof method !== 'string') { 60 | return this.settings.init; 61 | } else { 62 | return this[method].call(this, options); 63 | } 64 | }, 65 | 66 | events: function () { 67 | var self = this; 68 | 69 | $(window).on('resize.fndtn.interchange', self.throttle(function () { 70 | self.resize.call(self); 71 | }, 50)); 72 | }, 73 | 74 | resize: function () { 75 | var cache = this.cache; 76 | 77 | for (var uuid in cache) { 78 | if (cache.hasOwnProperty(uuid)) { 79 | var passed = this.results(uuid, cache[uuid]); 80 | 81 | if (passed) { 82 | this.settings.directives[passed 83 | .scenario[1]](passed.el, passed.scenario[0]); 84 | } 85 | } 86 | } 87 | 88 | }, 89 | 90 | results: function (uuid, scenarios) { 91 | var count = scenarios.length, 92 | results_arr = []; 93 | 94 | if (count > 0) { 95 | var el = $('[data-uuid="' + uuid + '"]'); 96 | 97 | for (var i = count - 1; i >= 0; i--) { 98 | var rule = scenarios[i][2]; 99 | if (this.settings.named_queries.hasOwnProperty(rule)) { 100 | var mq = matchMedia(this.settings.named_queries[rule]); 101 | } else { 102 | var mq = matchMedia(scenarios[i][2]); 103 | } 104 | if (mq.matches) { 105 | return {el: el, scenario: scenarios[i]}; 106 | } 107 | } 108 | } 109 | 110 | return false; 111 | }, 112 | 113 | images: function (force_update) { 114 | if (typeof this.cached_images === 'undefined' || force_update) { 115 | return this.update_images(); 116 | } 117 | 118 | return this.cached_images; 119 | }, 120 | 121 | update_images: function () { 122 | var images = document.getElementsByTagName('img'), 123 | count = images.length, 124 | data_attr = 'data-' + this.settings.load_attr; 125 | 126 | this.cached_images = []; 127 | 128 | for (var i = count - 1; i >= 0; i--) { 129 | this.loaded($(images[i]), (i === 0), function (image, last) { 130 | if (image) { 131 | var str = image.getAttribute(data_attr) || ''; 132 | 133 | if (str.length > 0) { 134 | this.cached_images.push(image); 135 | } 136 | } 137 | 138 | if (last) this.enhance(); 139 | 140 | }.bind(this)); 141 | } 142 | 143 | return 'deferred'; 144 | }, 145 | 146 | // based on jquery.imageready.js 147 | // @weblinc, @jsantell, (c) 2012 148 | 149 | loaded: function (image, last, callback) { 150 | function loaded() { 151 | callback(image[0], last); 152 | } 153 | 154 | function bindLoad() { 155 | this.one('load', loaded); 156 | 157 | if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { 158 | var src = this.attr('src'), 159 | param = src.match(/\?/) ? '&' : '?'; 160 | 161 | param += 'random=' + (new Date()).getTime(); 162 | this.attr('src', src + param); 163 | } 164 | } 165 | 166 | if (!image.attr('src')) { 167 | loaded(); 168 | return; 169 | } 170 | 171 | if (image[0].complete || image[0].readyState === 4) { 172 | loaded(); 173 | } else { 174 | bindLoad.call(image); 175 | } 176 | }, 177 | 178 | enhance: function () { 179 | var count = this.images().length; 180 | 181 | for (var i = count - 1; i >= 0; i--) { 182 | this._object($(this.images()[i])); 183 | } 184 | 185 | return $(window).trigger('resize'); 186 | }, 187 | 188 | parse_params: function (path, directive, mq) { 189 | return [this.trim(path), this.convert_directive(directive), this.trim(mq)]; 190 | }, 191 | 192 | convert_directive: function (directive) { 193 | var trimmed = this.trim(directive); 194 | 195 | if (trimmed.length > 0) { 196 | return trimmed; 197 | } 198 | 199 | return 'replace'; 200 | }, 201 | 202 | _object: function (el) { 203 | var raw_arr = this.parse_data_attr(el), 204 | scenarios = [], count = raw_arr.length; 205 | 206 | if (count > 0) { 207 | for (var i = count - 1; i >= 0; i--) { 208 | var split = raw_arr[i].split(/\((.*?)(\))$/); 209 | 210 | if (split.length > 1) { 211 | var cached_split = split[0].split(','), 212 | params = this.parse_params(cached_split[0], 213 | cached_split[1], split[1]); 214 | 215 | scenarios.push(params); 216 | } 217 | } 218 | } 219 | 220 | return this.store(el, scenarios); 221 | }, 222 | 223 | uuid: function (separator) { 224 | var delim = separator || "-"; 225 | 226 | function S4() { 227 | return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 228 | } 229 | 230 | return (S4() + S4() + delim + S4() + delim + S4() 231 | + delim + S4() + delim + S4() + S4() + S4()); 232 | }, 233 | 234 | store: function (el, scenarios) { 235 | var uuid = this.uuid(), 236 | current_uuid = el.data('uuid'); 237 | 238 | if (current_uuid) return this.cache[current_uuid]; 239 | 240 | el.attr('data-uuid', uuid); 241 | 242 | return this.cache[uuid] = scenarios; 243 | }, 244 | 245 | trim: function (str) { 246 | if (typeof str === 'string') { 247 | return $.trim(str); 248 | } 249 | 250 | return str; 251 | }, 252 | 253 | parse_data_attr: function (el) { 254 | var raw = el.data(this.settings.load_attr).split(/\[(.*?)\]/), 255 | count = raw.length, output = []; 256 | 257 | for (var i = count - 1; i >= 0; i--) { 258 | if (raw[i].replace(/[\W\d]+/, '').length > 4) { 259 | output.push(raw[i]); 260 | } 261 | } 262 | 263 | return output; 264 | }, 265 | 266 | reflow: function () { 267 | this.images(true); 268 | } 269 | 270 | }; 271 | 272 | }(Foundation.zj, this, this.document)); 273 | -------------------------------------------------------------------------------- /src/html/js/foundation/foundation.magellan.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ; 4 | (function ($, window, document, undefined) { 5 | 'use strict'; 6 | 7 | Foundation.libs.magellan = { 8 | name: 'magellan', 9 | 10 | version: '4.2.2', 11 | 12 | settings: { 13 | activeClass: 'active' 14 | }, 15 | 16 | init: function (scope, method, options) { 17 | this.scope = scope || this.scope; 18 | Foundation.inherit(this, 'data_options'); 19 | 20 | if (typeof method === 'object') { 21 | $.extend(true, this.settings, method); 22 | } 23 | 24 | if (typeof method !== 'string') { 25 | if (!this.settings.init) { 26 | this.fixed_magellan = $("[data-magellan-expedition]"); 27 | this.set_threshold(); 28 | this.last_destination = $('[data-magellan-destination]').last(); 29 | this.events(); 30 | } 31 | 32 | return this.settings.init; 33 | } else { 34 | return this[method].call(this, options); 35 | } 36 | }, 37 | 38 | events: function () { 39 | var self = this; 40 | $(this.scope).on('arrival.fndtn.magellan', '[data-magellan-arrival]', function (e) { 41 | var $destination = $(this), 42 | $expedition = $destination.closest('[data-magellan-expedition]'), 43 | activeClass = $expedition.attr('data-magellan-active-class') 44 | || self.settings.activeClass; 45 | 46 | $destination 47 | .closest('[data-magellan-expedition]') 48 | .find('[data-magellan-arrival]') 49 | .not($destination) 50 | .removeClass(activeClass); 51 | $destination.addClass(activeClass); 52 | }); 53 | 54 | this.fixed_magellan 55 | .on('update-position.fndtn.magellan', function () { 56 | var $el = $(this); 57 | // $el.data("magellan-fixed-position",""); 58 | // $el.data("magellan-top-offset", ""); 59 | }) 60 | .trigger('update-position'); 61 | 62 | $(window) 63 | .on('resize.fndtn.magellan', function () { 64 | this.fixed_magellan.trigger('update-position'); 65 | }.bind(this)) 66 | 67 | .on('scroll.fndtn.magellan', function () { 68 | var windowScrollTop = $(window).scrollTop(); 69 | self.fixed_magellan.each(function () { 70 | var $expedition = $(this); 71 | if (typeof $expedition.data('magellan-top-offset') === 'undefined') { 72 | $expedition.data('magellan-top-offset', $expedition.offset().top); 73 | } 74 | if (typeof $expedition.data('magellan-fixed-position') === 'undefined') { 75 | $expedition.data('magellan-fixed-position', false) 76 | } 77 | var fixed_position = (windowScrollTop + self.settings.threshold) > $expedition.data("magellan-top-offset"); 78 | var attr = $expedition.attr('data-magellan-top-offset'); 79 | 80 | if ($expedition.data("magellan-fixed-position") != fixed_position) { 81 | $expedition.data("magellan-fixed-position", fixed_position); 82 | if (fixed_position) { 83 | $expedition.addClass('fixed'); 84 | $expedition.css({position: "fixed", top: 0}); 85 | } else { 86 | $expedition.removeClass('fixed'); 87 | $expedition.css({position: "", top: ""}); 88 | } 89 | if (fixed_position && typeof attr != 'undefined' && attr != false) { 90 | $expedition.css({ 91 | position: "fixed", 92 | top: attr + "px" 93 | }); 94 | } 95 | } 96 | }); 97 | }); 98 | 99 | 100 | if (this.last_destination.length > 0) { 101 | $(window).on('scroll.fndtn.magellan', function (e) { 102 | var windowScrollTop = $(window).scrollTop(), 103 | scrolltopPlusHeight = windowScrollTop + $(window).height(), 104 | lastDestinationTop = Math.ceil(self.last_destination.offset().top); 105 | 106 | $('[data-magellan-destination]').each(function () { 107 | var $destination = $(this), 108 | destination_name = $destination.attr('data-magellan-destination'), 109 | topOffset = $destination.offset().top - windowScrollTop; 110 | 111 | if (topOffset <= self.settings.threshold) { 112 | $("[data-magellan-arrival='" + destination_name + "']").trigger('arrival'); 113 | } 114 | // In large screens we may hit the bottom of the page and dont reach the top of the last magellan-destination, so lets force it 115 | if (scrolltopPlusHeight >= $(self.scope).height() && lastDestinationTop > windowScrollTop && lastDestinationTop < scrolltopPlusHeight) { 116 | $('[data-magellan-arrival]').last().trigger('arrival'); 117 | } 118 | }); 119 | }); 120 | } 121 | 122 | this.settings.init = true; 123 | }, 124 | 125 | set_threshold: function () { 126 | if (!this.settings.threshold) { 127 | this.settings.threshold = (this.fixed_magellan.length > 0) ? 128 | this.outerHeight(this.fixed_magellan, true) : 0; 129 | } 130 | }, 131 | 132 | off: function () { 133 | $(this.scope).off('.fndtn.magellan'); 134 | }, 135 | 136 | reflow: function () { 137 | } 138 | }; 139 | }(Foundation.zj, this, this.document)); 140 | -------------------------------------------------------------------------------- /src/html/js/foundation/foundation.placeholder.js: -------------------------------------------------------------------------------- 1 | /*! http://mths.be/placeholder v2.0.7 by @mathias 2 | Modified to work with Zepto.js by ZURB 3 | */ 4 | ; 5 | (function (window, document, $) { 6 | 7 | var isInputSupported = 'placeholder' in document.createElement('input'), 8 | isTextareaSupported = 'placeholder' in document.createElement('textarea'), 9 | prototype = $.fn, 10 | valHooks = $.valHooks, 11 | hooks, 12 | placeholder; 13 | 14 | if (isInputSupported && isTextareaSupported) { 15 | 16 | placeholder = prototype.placeholder = function () { 17 | return this; 18 | }; 19 | 20 | placeholder.input = placeholder.textarea = true; 21 | 22 | } else { 23 | 24 | placeholder = prototype.placeholder = function () { 25 | var $this = this; 26 | $this 27 | .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]') 28 | .not('.placeholder') 29 | .bind({ 30 | 'focus.placeholder': clearPlaceholder, 31 | 'blur.placeholder': setPlaceholder 32 | }) 33 | .data('placeholder-enabled', true) 34 | .trigger('blur.placeholder'); 35 | return $this; 36 | }; 37 | 38 | placeholder.input = isInputSupported; 39 | placeholder.textarea = isTextareaSupported; 40 | 41 | hooks = { 42 | 'get': function (element) { 43 | var $element = $(element); 44 | return $element.data('placeholder-enabled') && $element.hasClass('placeholder') ? '' : element.value; 45 | }, 46 | 'set': function (element, value) { 47 | var $element = $(element); 48 | if (!$element.data('placeholder-enabled')) { 49 | return element.value = value; 50 | } 51 | if (value == '') { 52 | element.value = value; 53 | // Issue #56: Setting the placeholder causes problems if the element continues to have focus. 54 | if (element != document.activeElement) { 55 | // We can't use `triggerHandler` here because of dummy text/password inputs :( 56 | setPlaceholder.call(element); 57 | } 58 | } else if ($element.hasClass('placeholder')) { 59 | clearPlaceholder.call(element, true, value) || (element.value = value); 60 | } else { 61 | element.value = value; 62 | } 63 | // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363 64 | return $element; 65 | } 66 | }; 67 | 68 | isInputSupported || (valHooks.input = hooks); 69 | isTextareaSupported || (valHooks.textarea = hooks); 70 | 71 | $(function () { 72 | // Look for forms 73 | $(document).delegate('form', 'submit.placeholder', function () { 74 | // Clear the placeholder values so they don't get submitted 75 | var $inputs = $('.placeholder', this).each(clearPlaceholder); 76 | setTimeout(function () { 77 | $inputs.each(setPlaceholder); 78 | }, 10); 79 | }); 80 | }); 81 | 82 | // Clear placeholder values upon page reload 83 | $(window).bind('beforeunload.placeholder', function () { 84 | $('.placeholder').each(function () { 85 | this.value = ''; 86 | }); 87 | }); 88 | 89 | } 90 | 91 | function args(elem) { 92 | // Return an object of element attributes 93 | var newAttrs = {}, 94 | rinlinejQuery = /^jQuery\d+$/; 95 | $.each(elem.attributes, function (i, attr) { 96 | if (attr.specified && !rinlinejQuery.test(attr.name)) { 97 | newAttrs[attr.name] = attr.value; 98 | } 99 | }); 100 | return newAttrs; 101 | } 102 | 103 | function clearPlaceholder(event, value) { 104 | var input = this, 105 | $input = $(input); 106 | if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) { 107 | if ($input.data('placeholder-password')) { 108 | $input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id')); 109 | // If `clearPlaceholder` was called from `$.valHooks.input.set` 110 | if (event === true) { 111 | return $input[0].value = value; 112 | } 113 | $input.focus(); 114 | } else { 115 | input.value = ''; 116 | $input.removeClass('placeholder'); 117 | input == document.activeElement && input.select(); 118 | } 119 | } 120 | } 121 | 122 | function setPlaceholder() { 123 | var $replacement, 124 | input = this, 125 | $input = $(input), 126 | $origInput = $input, 127 | id = this.id; 128 | if (input.value == '') { 129 | if (input.type == 'password') { 130 | if (!$input.data('placeholder-textinput')) { 131 | try { 132 | $replacement = $input.clone().attr({'type': 'text'}); 133 | } catch (e) { 134 | $replacement = $('').attr($.extend(args(this), {'type': 'text'})); 135 | } 136 | $replacement 137 | .removeAttr('name') 138 | .data({ 139 | 'placeholder-password': true, 140 | 'placeholder-id': id 141 | }) 142 | .bind('focus.placeholder', clearPlaceholder); 143 | $input 144 | .data({ 145 | 'placeholder-textinput': $replacement, 146 | 'placeholder-id': id 147 | }) 148 | .before($replacement); 149 | } 150 | $input = $input.removeAttr('id').hide().prev().attr('id', id).show(); 151 | // Note: `$input[0] != input` now! 152 | } 153 | $input.addClass('placeholder'); 154 | $input[0].value = $input.attr('placeholder'); 155 | } else { 156 | $input.removeClass('placeholder'); 157 | } 158 | } 159 | 160 | }(this, document, Foundation.zj)); 161 | 162 | (function ($, window, document, undefined) { 163 | 'use strict'; 164 | 165 | Foundation.libs.placeholder = { 166 | name: 'placeholder', 167 | 168 | version: '4.2.2', 169 | 170 | init: function (scope, method, options) { 171 | this.scope = scope || this.scope; 172 | 173 | if (typeof method !== 'string') { 174 | window.onload = function () { 175 | $('input, textarea').placeholder(); 176 | } 177 | } 178 | } 179 | }; 180 | }(Foundation.zj, this, this.document)); 181 | -------------------------------------------------------------------------------- /src/html/js/foundation/foundation.tooltips.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ; 4 | (function ($, window, document, undefined) { 5 | 'use strict'; 6 | 7 | Foundation.libs.tooltips = { 8 | name: 'tooltips', 9 | 10 | version: '4.2.2', 11 | 12 | settings: { 13 | selector: '.has-tip', 14 | additionalInheritableClasses: [], 15 | tooltipClass: '.tooltip', 16 | appendTo: 'body', 17 | 'disable-for-touch': false, 18 | tipTemplate: function (selector, content) { 19 | return '' + content + ''; 22 | } 23 | }, 24 | 25 | cache: {}, 26 | 27 | init: function (scope, method, options) { 28 | Foundation.inherit(this, 'data_options'); 29 | var self = this; 30 | 31 | if (typeof method === 'object') { 32 | $.extend(true, this.settings, method); 33 | } else if (typeof options !== 'undefined') { 34 | $.extend(true, this.settings, options); 35 | } 36 | 37 | if (typeof method !== 'string') { 38 | if (Modernizr.touch) { 39 | $(this.scope) 40 | .on('click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip', 41 | '[data-tooltip]', function (e) { 42 | var settings = $.extend({}, self.settings, self.data_options($(this))); 43 | if (!settings['disable-for-touch']) { 44 | e.preventDefault(); 45 | $(settings.tooltipClass).hide(); 46 | self.showOrCreateTip($(this)); 47 | } 48 | }) 49 | .on('click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip', 50 | this.settings.tooltipClass, function (e) { 51 | e.preventDefault(); 52 | $(this).fadeOut(150); 53 | }); 54 | } else { 55 | $(this.scope) 56 | .on('mouseenter.fndtn.tooltip mouseleave.fndtn.tooltip', 57 | '[data-tooltip]', function (e) { 58 | var $this = $(this); 59 | 60 | if (/enter|over/i.test(e.type)) { 61 | self.showOrCreateTip($this); 62 | } else if (e.type === 'mouseout' || e.type === 'mouseleave') { 63 | self.hide($this); 64 | } 65 | }); 66 | } 67 | 68 | // $(this.scope).data('fndtn-tooltips', true); 69 | } else { 70 | return this[method].call(this, options); 71 | } 72 | 73 | }, 74 | 75 | showOrCreateTip: function ($target) { 76 | var $tip = this.getTip($target); 77 | 78 | if ($tip && $tip.length > 0) { 79 | return this.show($target); 80 | } 81 | 82 | return this.create($target); 83 | }, 84 | 85 | getTip: function ($target) { 86 | var selector = this.selector($target), 87 | tip = null; 88 | 89 | if (selector) { 90 | tip = $('span[data-selector="' + selector + '"]' + this.settings.tooltipClass); 91 | } 92 | 93 | return (typeof tip === 'object') ? tip : false; 94 | }, 95 | 96 | selector: function ($target) { 97 | var id = $target.attr('id'), 98 | dataSelector = $target.attr('data-tooltip') || $target.attr('data-selector'); 99 | 100 | if ((id && id.length < 1 || !id) && typeof dataSelector != 'string') { 101 | dataSelector = 'tooltip' + Math.random().toString(36).substring(7); 102 | $target.attr('data-selector', dataSelector); 103 | } 104 | 105 | return (id && id.length > 0) ? id : dataSelector; 106 | }, 107 | 108 | create: function ($target) { 109 | var $tip = $(this.settings.tipTemplate(this.selector($target), $('
').html($target.attr('title')).html())), 110 | classes = this.inheritable_classes($target); 111 | 112 | $tip.addClass(classes).appendTo(this.settings.appendTo); 113 | if (Modernizr.touch) { 114 | $tip.append('tap to close '); 115 | } 116 | $target.removeAttr('title').attr('title', ''); 117 | this.show($target); 118 | }, 119 | 120 | reposition: function (target, tip, classes) { 121 | var width, nub, nubHeight, nubWidth, column, objPos; 122 | 123 | tip.css('visibility', 'hidden').show(); 124 | 125 | width = target.data('width'); 126 | nub = tip.children('.nub'); 127 | nubHeight = this.outerHeight(nub); 128 | nubWidth = this.outerHeight(nub); 129 | 130 | objPos = function (obj, top, right, bottom, left, width) { 131 | return obj.css({ 132 | 'top': (top) ? top : 'auto', 133 | 'bottom': (bottom) ? bottom : 'auto', 134 | 'left': (left) ? left : 'auto', 135 | 'right': (right) ? right : 'auto', 136 | 'width': (width) ? width : 'auto' 137 | }).end(); 138 | }; 139 | 140 | objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', target.offset().left, width); 141 | 142 | if ($(window).width() < 767) { 143 | objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', 12.5, $(this.scope).width()); 144 | tip.addClass('tip-override'); 145 | objPos(nub, -nubHeight, 'auto', 'auto', target.offset().left); 146 | } else { 147 | var left = target.offset().left; 148 | if (Foundation.rtl) { 149 | left = target.offset().left + target.offset().width - this.outerWidth(tip); 150 | } 151 | objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', left, width); 152 | tip.removeClass('tip-override'); 153 | if (classes && classes.indexOf('tip-top') > -1) { 154 | objPos(tip, (target.offset().top - this.outerHeight(tip)), 'auto', 'auto', left, width) 155 | .removeClass('tip-override'); 156 | } else if (classes && classes.indexOf('tip-left') > -1) { 157 | objPos(tip, (target.offset().top + (this.outerHeight(target) / 2) - nubHeight * 2.5), 'auto', 'auto', (target.offset().left - this.outerWidth(tip) - nubHeight), width) 158 | .removeClass('tip-override'); 159 | } else if (classes && classes.indexOf('tip-right') > -1) { 160 | objPos(tip, (target.offset().top + (this.outerHeight(target) / 2) - nubHeight * 2.5), 'auto', 'auto', (target.offset().left + this.outerWidth(target) + nubHeight), width) 161 | .removeClass('tip-override'); 162 | } 163 | } 164 | 165 | tip.css('visibility', 'visible').hide(); 166 | }, 167 | 168 | inheritable_classes: function (target) { 169 | var inheritables = ['tip-top', 'tip-left', 'tip-bottom', 'tip-right', 'noradius'].concat(this.settings.additionalInheritableClasses), 170 | classes = target.attr('class'), 171 | filtered = classes ? $.map(classes.split(' '), function (el, i) { 172 | if ($.inArray(el, inheritables) !== -1) { 173 | return el; 174 | } 175 | }).join(' ') : ''; 176 | 177 | return $.trim(filtered); 178 | }, 179 | 180 | show: function ($target) { 181 | var $tip = this.getTip($target); 182 | 183 | this.reposition($target, $tip, $target.attr('class')); 184 | $tip.fadeIn(150); 185 | }, 186 | 187 | hide: function ($target) { 188 | var $tip = this.getTip($target); 189 | 190 | $tip.fadeOut(150); 191 | }, 192 | 193 | // deprecate reload 194 | reload: function () { 195 | var $self = $(this); 196 | 197 | return ($self.data('fndtn-tooltips')) ? $self.foundationTooltips('destroy').foundationTooltips('init') : $self.foundationTooltips('init'); 198 | }, 199 | 200 | off: function () { 201 | $(this.scope).off('.fndtn.tooltip'); 202 | $(this.settings.tooltipClass).each(function (i) { 203 | $('[data-tooltip]').get(i).attr('title', $(this).text()); 204 | }).remove(); 205 | }, 206 | 207 | reflow: function () { 208 | } 209 | }; 210 | }(Foundation.zj, this, this.document)); 211 | --------------------------------------------------------------------------------