pax_global_header00006660000000000000000000000064121703440310014505gustar00rootroot0000000000000052 comment=d80a9422d505f25c06145ee48873ca926e8c7d19 automx-0.10.0/000077500000000000000000000000001217034403100131005ustar00rootroot00000000000000automx-0.10.0/.gitignore000066400000000000000000000001701217034403100150660ustar00rootroot00000000000000.project .pydevproject .settings/org.eclipse.core.resources.prefs *.*~ *.swp .DS_Store src/foundation-scss/.sass-cache/ automx-0.10.0/BASIC_CONFIGURATION_README000066400000000000000000000124421217034403100167530ustar00rootroot00000000000000Basic Configuration This document contains information for initial and/or basic configuration of automx. 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. Format The general format of the automx.conf file is as follows: • 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. • 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. • Section and property names are case sensitive. • A line with a number sign (“#”) begins a comment. Anything following a number sign will be ignored by automx. Sections 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. [automx] Controlling automx Runtime Behaviour 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. [automx] The following example shows a typical [automx] section setup: [automx] provider = example.com 1 domains = * 2 logfile = /var/log/automx/automx.log 3 debug = yes 4 memcache = 127.0.0.1:11211 5 memcache_ttl = 86400 client_error_limit = 5 6 rate_limit_exception_networks = 127.0.0.0/8, ::1/128 7 1 The provider property configures automx to identify the webservice as example.com. 2 The wildcard option * used in domains instructs automx to answer any configuration request regardless of the domain sent by the mail client. 3 All log information should go to /var/log/automx/automx.log. 4 Debugging is enabled and infos will be sent to logfile. 5 Statistical data controlling errors caused by clients accessing database backends will be sent to the specified memcache service. 6 In this example a client may not cause more than 5 errors before automx will refuse to answer further queries. 7 Clients listed in rate_limit_exception_networks are excluded from rate limiting. [DEFAULT] Properties present in all other sections This section is optional. Settings in this section define properties which will be present in all other sections. It is useful to avoid redundancy. [DEFAULT] The following example shows a typical [DEFAULT] section setup: [DEFAULT] action = settings 1 account_type = email 2 account_name = Example Inc. 3 account_name_short = Example 4 1 The default action for automx is to provide account settings. Note The Microsoft schema forsees other actions that account provisioning. 2 The account_type should be an email account. 3 The account should show up as Example Inc. in the clients account list. 4 The accounts short name should be Example. [global] A global backend 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. 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. Note This is useful when many domains should use the same backend or when automx domain property configures it to run as wildcard service. [global] 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: [global] backend = ldap account_name = ${cn} (Example Inc.) display_name = ${givenName} ${sn} smtp = yes smtp_server = mail.example.com smtp_port = 587 smtp_encryption = starttls smtp_auth = plaintext smtp_auth_identity = ${mail} smtp_expiration_date = 2012-12-31 smtp_refresh_ttl = 0 smtp_default = yes imap = yes imap_server = mail.example.com imap_port = 993 imap_encryption = ssl imap_auth = plaintext imap_auth_identity = ${mail} imap_expiration_date = 2012-12-31 imap_refresh_ttl = 0 pop = no pop_server = mail.example.com pop_port = 995 pop_encryption = ssl pop_auth = plaintext pop_auth_identity = ${mail} pop_expiration_date = 2012-12-31 pop_refresh_ttl = 0 host = ldap://ldap.example.com base = ou=people,dc=example,dc=com result_attrs = mail, cn, givenName, sn scope = sub filter = (&(objectClass=*)(uniqueIdentifier=%s)) bindmethod = sasl saslmech = EXTERNAL usetls = yes reqcert = demand cert = /etc/ssl/certs/mail.example.com.crt.pem key = /etc/ssl/private/mail.example.com.key.pem cacert = /etc/ssl/certs/ca-certificates.crt Authors Christian Rößner Wrote the program. Patrick Ben Koetter Wrote the documentation. automx-0.10.0/CHANGES000066400000000000000000000012731217034403100140760ustar00rootroot00000000000000* Version 0.10.0 --------------------------------- - NEW: Implemented mobileconfig support - Minor bug fixes * Version 0.9.2 - ?? --------------------------------- - Bug fixes * Version 0.9.1 - unreleased --------------------------------- - Documentation updates * Version 0.9 - 2012/10/12 --------------------------------- - NEW: Added ActiveSync MobileSync support * Version 0.9_beta2 - 2012/04/18 --------------------------------- - NEW: Result modifiers for dynamic backends * Version 0.9_beta1 - 2012/04/04 --------------------------------- - NEW: Denial-of-Service-Attack protection - NEW: Multiple service profiles - NEW: Centralized logging - Code cleanup - Improved exception handling automx-0.10.0/CREDITS000066400000000000000000000002711217034403100141200ustar00rootroot00000000000000Repository, automx-test and Build Management Marc Schiffbauer Programming Christian Roessner Website, Documentation and Idea Patrick Ben Koetter automx-0.10.0/INSTALL000066400000000000000000000224331217034403100141350ustar00rootroot00000000000000INSTALL This document contains step by step instructions to install automx. automx Download If you haven't done so yet download automx from its website: $ wget http://automx.org/download/latest.tar.gz Unpack the tar archive and change into the newly created directory: $ tar xzf latest.tar.gz $ cd automx-VERSION Software Requirements automx is a Python application. You must install a few extra modules to handle frontend and backend communication as well to deal with XML data. frontend Install mod_wsgi for the Apache web server and the python-wsgi module. backend (optional) If you plan to use either LDAP or SQL as backends to retrieve configuration options from install ldap for LDAP and python-sqlalchemy for SQL. Further you also need to install the SQL backend driver that communicates with your database. For MySQL this might be mysqldb. XML handling Install the python-packages dateutil, ipaddr, lxml and memcache. Otherwise automx will not be able to handle the XML data it needs to deal with. plist (mobileconfig) handling Mobileconfig profiles may be signed with your webservers cert and key. You need to install M2Crypto, which does the S/MIME-signing. Once you've satisfied the requirements, you can start to install automx. Installing automx automx is a wsgi script depending on some automx-specific libraries. It reads its configuration from a single configuration file. The program, the libraries and the configuration file need to be installed at different locations. Installing the program Create a directory for the automx program and copy it to that location: $ mkdir -p /usr/local/lib/automx $ cp automx-VERSION/src/automx_wsgi.py /usr/local/lib/automx/ Installing the test program Copy the automx-test program to a location that is in your $PATH: $ cp automx-VERSION/src/automx-test /usr/local/bin/automx-test Installing automx-specific libraries Python loads packages from various locations depending on your distribution and python version. To correctly determine the used paths please type the following commands: $ python >>> import sys >>> sys.path >>> (CTRL+D) You'll get a list of used paths. Please remember the first shown path entry (for example '/usr/lib/python2.7') - this is the best location for placing the automx-directory: $ cp -r automx-VERSION/src/automx /usr/lib/pythonVERSION Installing man Pages Try using the manpath command to find out where man pages are stored on your computer: $ manpath /usr/local/man:/usr/local/share/man:/usr/share/man Copy the man pages to that location: $ cp -a automx-VERSION/doc/man/ /usr/local/share/man Installing the configuration file Place the sample automx.conf file into /etc: $ cp automx-VERSION/src/conf/automx.conf /etc/ Follow automx.conf(5) Adopt this configuration file to your needs. You may find detailed information in the man page automx.conf(5). Tip Set debug=yes in the section automx while you setup, configure and test automx. It will help you detect problems more easily. This will log the request GET/ POST and the response to the error.log file(s). DNS Configuration Mail clients seeking mail account autoconfiguration will either request an IP address for autoconfig.example.com (Mozilla schema) or autodiscover.example.com (Microsoft schema). Provide settings in your DNS that directs them to the server running the automx service: autoconfig.example.com. IN A 192.168.2.1 autodiscover.example.com. IN A 192.168.2.1 Note If you install automx on an existing host, which has it's own domain-name, then it is also possible to use above entries as nicknames: somehost.example.com. IN A 192.168.2.1 autoconfig IN CNAME somehost autodiscover IN CNAME somehost Web Server Configuration Finally configure the web server. It will accept configuration requests from mail clients, pass the information to automx and in turn will respond with account profiles once automx has figured out the details. First enable the wsgi module. Follow your OS documentation to find out how it needs to be done. (e.g. 'a2enmod wsgi' for Apache on Debian) automx is able to provision mail clients following the Mozilla autoconfig schema as well as mail clients following the Microsoft autodiscover schema. Both schemas have different requirements regarding hostname, port and level of security when a request is sent to the configuration server: Microsoft Mail clients following the Microsoft autodiscover schema require a https connection. The web server must identify itself as autodiscover.example.com on port 443 and it must use a valid server certificate that is trusted by the mail client requesting configuration. Mozilla Mail clients following the Mozilla autoconfig schema can use either a http or a https connection. The web server must identify itself as autoconfig.example.com on port 80 or 443. If it connects on 443 a valid server certificate that is trusted by the mail client requesting configuration has to be used. To provision Apple iOS devices or Mac OS X Mail, you need to place the file automx.html somewhere in your document root of your webserver. After that you can use your iOS device and open the Safari browser calling this website. After entering the form data, you will receive a mobileconfig file and the device switches to the settings assistent. On Mac OS X, you also can call this document and save it to disk. After opening it, the profile manager opens and the steps are similar to iOS. For signed profiles see the man page automx.conf (5). Here is a simple example that configures an autoconfig and an autodiscover service (both use the same automx script). You need to copy & paste this lines into your existing website configuration files (for Debian take a look in /etc/ apache2/sites-enabled/...): ServerName example.com ServerAlias autoconfig.example.com ServerAdmin webmaster@example.com WSGIScriptAliasMatch \ (?i)^/.+/(autodiscover|config-v1.1)>xml \ /usr/lib/automx/automx_wsgi.py Order allow,deny Allow from all ServerName example.com:443 ServerAlias autodiscover.example.com:443 ServerAdmin webmaster@example.com WSGIScriptAliasMatch \ (?i)^/.+/(autodiscover|config-v1.1)>xml \ /usr/lib/automx/automx_wsgi.py WSGIScriptAlias \ /mobileconfig \ /usr/lib/automx/automx_wsgi.py Order allow,deny Allow from all Note If you haven't done so, you also need to configure and enable SSL in your apache-configuration. At least that means enabling the default SSL-site, install (self signed) certificates and activating the ssl-support (e.g. 'a2enmod ssl' for Apache on debian). Don't forget to restart your web-server afterwards! You need also to ajust the paths to automx_wsgi.py in the example above. Note For Nginx see the example configuration file nginx-automx.conf. You can place this file into /etc/nginx/conf.d (this depends on your distribution) and adopt it to your needs. Note ISPs In an advanced environment with thousands of domains, you can redirect mail clients via DNS entries to your ISP automx provisioning server for Microsoft clients and a web server instance with a wild card ServerName to serve the Mozilla schema. Add this to your DNS-configuation: *.example.com. A 192.168.2.1 and this to your virtualhost-definition in your webserver-configuration: ServerAlias *.example.com automx comes with a little utility that helps testing proper operation. The next section explains how to use it. Testing And Debugging automx The automx-test utility sends configuration requests for Microsoft and Mozilla clients to the web server: $ automx-test user@example.com The domainpart in the address determines the list of hostnames that will be queried. In this example autoconfig.example.com and autodiscover.example.com will be contacted. You should see the web server header. The script will say Success or Failed. If things go wrong, the error.log is your friend. It will indicate configuration issues, if python modules are missing, if your database can not be queried or anything else that might go wrong. If you also enabled debug in / etc/automx.conf, you will find further information in your automx.log file. Please turn on debug, if you want to send us a bug report. PLEASE NOTICE! Mobileconfig will display a users password in cleartext! So please remove that from bug reports first! Note If you split error logs by port, e.g. port 80 and 443, you need to check both. Autoconfig requests will mostly show up in the port 80 error.log, whereas autodiscover will only show up in your 443 error.log. Authors Christian Roessner Wrote the program. Patrick Ben Koetter Wrote the documentation. Christian Sudec 04-22-2013: Updated the documentation to support automx 0.9.2 automx-0.10.0/LICENSE000066400000000000000000001045131217034403100141110ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . automx-0.10.0/README.md000066400000000000000000000000671217034403100143620ustar00rootroot00000000000000For installation instructions see file INSTALL please. automx-0.10.0/RELEASE_NOTES000066400000000000000000000034761217034403100150650ustar00rootroot00000000000000Version 0.9.x to 0.10: Automx does support mobileconfig profiles for Apple iOS devices as well as Mac OS X Mail. The profiles are optionally signed. The backend "file" was expanded and does support the new option mobileconfig, which may point to a mobileconfig file (it is the equivalent to the autoconfig/autodiscover option of that specific backend). Version 0.8 to 0.9: Failure Counter (Denial-of-Service-Attack protection) A failure counter tracks bad (email address) requests. The counter serves to protect automx from Denial-of-Service-Attacks. An attacker might try to iterate a list of email addresses against automx to see, if the server returns valid results or 500 temp errors. This feature is expected to be especially useful for automx setups using database driven backends. automx will stop querying the database once the border limit has been reached. The protection is built using memcached, as it allows a central instance to collect for multiple automx processes. Multi Service Profiles Multi service profile support allows to serve more than one setting for the same service within a profile. For this we added several new backends and a new option that allows automx to follow any further backends. These backends may either substitute the former settings or add new information. Improved Logging As we made some changes to the code, we realized that it important to change logging behavior. So we now log all automx messages to an extra file and also the debugging output does give us much more information as the version 0.8. Code Cleanups Many changes have been done inside the code itself. Code cleanups and a better exception handling for situations, where no data could be retrieved from databases. Also the main file now is much cleaner, as we introduced some constant variables for error codes. # vim: set tw=80: automx-0.10.0/ROADMAP000066400000000000000000000003601217034403100141050ustar00rootroot00000000000000Version 0.9 to 1.0: * We may implement a web services backend. * Starting with 0.10.0, we will drop python 2.6.x and below support. We will make automx compatible with 2.7.x and 3.x versions. No further plans at this time of writing. automx-0.10.0/doc/000077500000000000000000000000001217034403100136455ustar00rootroot00000000000000automx-0.10.0/doc/html/000077500000000000000000000000001217034403100146115ustar00rootroot00000000000000automx-0.10.0/doc/html/BASIC_CONFIGURATION_README.html000066400000000000000000000325051217034403100214310ustar00rootroot00000000000000 Basic Configuration

Basic Configuration

This document contains information for initial and/or basic configuration of automx.

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.

Format

The general format of the automx.conf file is as follows:

  • 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.
  • 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.
  • Section and property names are case sensitive.
  • A line with a number sign (“#”) begins a comment. Anything following a number sign will be ignored by automx.

Sections

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.

[automx]

Controlling automx Runtime Behaviour

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.

[automx]

The following example shows a typical [automx] section setup:

[automx]
provider = example.com  1
domains = *  2
logfile = /var/log/automx/automx.log  3
debug = yes  4
memcache = 127.0.0.1:11211  5
memcache_ttl = 86400
client_error_limit = 5  6
rate_limit_exception_networks = 127.0.0.0/8, ::1/128  7
1
The provider property configures automx to identify the webservice as example.com.
2
The wildcard option * used in domains instructs automx to answer any configuration request regardless of the domain sent by the mail client.
3
All log information should go to /var/log/automx/automx.log.
4
Debugging is enabled and infos will be sent to logfile.
5
Statistical data controlling errors caused by clients accessing database backends will be sent to the specified memcache service.
6
In this example a client may not cause more than 5 errors before automx will refuse to answer further queries.
7
Clients listed in rate_limit_exception_networks are excluded from rate limiting.

[DEFAULT]

Properties present in all other sections

This section is optional. Settings in this section define properties which will be present in all other sections. It is useful to avoid redundancy.

[DEFAULT]

The following example shows a typical [DEFAULT] section setup:

[DEFAULT]
action = settings  1
account_type = email  2
account_name = Example Inc.  3
account_name_short = Example  4
1

The default action for automx is to provide account settings.

Note

The Microsoft schema forsees other actions that account provisioning.

2
The account_type should be an email account.
3
The account should show up as Example Inc. in the clients account list.
4
The accounts short name should be Example.

[global]

A global backend

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.

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.

Note

This is useful when many domains should use the same backend or when automx domain property configures it to run as wildcard service.

[global]

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:

[global]
backend = ldap

account_name = ${cn} (Example Inc.)
display_name = ${givenName} ${sn}

smtp = yes
smtp_server = mail.example.com
smtp_port = 587
smtp_encryption = starttls
smtp_auth = plaintext
smtp_auth_identity = ${mail}
smtp_expiration_date = 2012-12-31
smtp_refresh_ttl = 0
smtp_default = yes

imap = yes
imap_server = mail.example.com
imap_port = 993
imap_encryption = ssl
imap_auth = plaintext
imap_auth_identity = ${mail}
imap_expiration_date = 2012-12-31
imap_refresh_ttl = 0

pop = no
pop_server = mail.example.com
pop_port = 995
pop_encryption = ssl
pop_auth = plaintext
pop_auth_identity = ${mail}
pop_expiration_date = 2012-12-31
pop_refresh_ttl = 0

host = ldap://ldap.example.com
base = ou=people,dc=example,dc=com
result_attrs = mail, cn, givenName, sn
scope = sub
filter = (&(objectClass=*)(uniqueIdentifier=%s))

bindmethod = sasl
saslmech = EXTERNAL
usetls = yes
reqcert = demand
cert = /etc/ssl/certs/mail.example.com.crt.pem
key = /etc/ssl/private/mail.example.com.key.pem
cacert = /etc/ssl/certs/ca-certificates.crt

Authors

Christian Rößner <cr@ys4.de>
Wrote the program.
Patrick Ben Koetter <p@sys4.de>
Wrote the documentation.
automx-0.10.0/doc/html/INSTALL.html000066400000000000000000000445401217034403100166140ustar00rootroot00000000000000 INSTALL

INSTALL

This document contains step by step instructions to install automx.

automx Download

If you haven't done so yet download automx from its website:

$ wget http://automx.org/download/latest.tar.gz

Unpack the tar archive and change into the newly created directory:

$ tar xzf latest.tar.gz
$ cd automx-VERSION

Software Requirements

automx is a Python application. You must install a few extra modules to handle frontend and backend communication as well to deal with XML data.

frontend
Install mod_wsgi for the Apache web server and the python-wsgi module.
backend (optional)
If you plan to use either LDAP or SQL as backends to retrieve configuration options from install ldap for LDAP and python-sqlalchemy for SQL. Further you also need to install the SQL backend driver that communicates with your database. For MySQL this might be mysqldb.
XML handling
Install the python-packages dateutil, ipaddr, lxml and memcache. Otherwise automx will not be able to handle the XML data it needs to deal with.
plist (mobileconfig) handling
Mobileconfig profiles may be signed with your webservers cert and key. You need to install M2Crypto, which does the S/MIME-signing.

Once you've satisfied the requirements, you can start to install automx.

Installing automx

automx is a wsgi script depending on some automx-specific libraries. It reads its configuration from a single configuration file. The program, the libraries and the configuration file need to be installed at different locations.

Installing the program

Create a directory for the automx program and copy it to that location:

$ mkdir -p /usr/local/lib/automx
$ cp automx-VERSION/src/automx_wsgi.py /usr/local/lib/automx/

Installing the test program

Copy the automx-test program to a location that is in your $PATH:

$ cp automx-VERSION/src/automx-test /usr/local/bin/automx-test

Installing automx-specific libraries

Python loads packages from various locations depending on your distribution and python version. To correctly determine the used paths please type the following commands:

$ python
>>> import sys
>>> sys.path
>>> (CTRL+D)

You'll get a list of used paths. Please remember the first shown path entry (for example '/usr/lib/python2.7') - this is the best location for placing the automx-directory:

$ cp -r automx-VERSION/src/automx /usr/lib/pythonVERSION

Installing man Pages

Try using the manpath command to find out where man pages are stored on your computer:

$ manpath /usr/local/man:/usr/local/share/man:/usr/share/man

Copy the man pages to that location:

$ cp -a automx-VERSION/doc/man/ /usr/local/share/man

Installing the configuration file

Place the sample automx.conf file into /etc:

$ cp automx-VERSION/src/conf/automx.conf /etc/

Follow automx.conf(5) Adopt this configuration file to your needs. You may find detailed information in the man page automx.conf(5).

Tip

Set debug=yes in the section automx while you setup, configure and test automx. It will help you detect problems more easily. This will log the request GET/POST and the response to the error.log file(s).

DNS Configuration

Mail clients seeking mail account autoconfiguration will either request an IP address for autoconfig.example.com (Mozilla schema) or autodiscover.example.com (Microsoft schema). Provide settings in your DNS that directs them to the server running the automx service:

autoconfig.example.com.              IN    A     192.168.2.1
autodiscover.example.com.            IN    A     192.168.2.1

Note

If you install automx on an existing host, which has it's own domain-name, then it is also possible to use above entries as nicknames:

somehost.example.com. IN A 192.168.2.1
autoconfig IN CNAME somehost autodiscover IN CNAME somehost

Web Server Configuration

Finally configure the web server. It will accept configuration requests from mail clients, pass the information to automx and in turn will respond with account profiles once automx has figured out the details.

First enable the wsgi module. Follow your OS documentation to find out how it needs to be done. (e.g. 'a2enmod wsgi' for Apache on Debian)

automx is able to provision mail clients following the Mozilla autoconfig schema as well as mail clients following the Microsoft autodiscover schema. Both schemas have different requirements regarding hostname, port and level of security when a request is sent to the configuration server:

Microsoft
Mail clients following the Microsoft autodiscover schema require a https connection. The web server must identify itself as autodiscover.example.com on port 443 and it must use a valid server certificate that is trusted by the mail client requesting configuration.
Mozilla
Mail clients following the Mozilla autoconfig schema can use either a http or a https connection. The web server must identify itself as autoconfig.example.com on port 80 or 443. If it connects on 443 a valid server certificate that is trusted by the mail client requesting configuration has to be used.

To provision Apple iOS devices or Mac OS X Mail, you need to place the file automx.html somewhere in your document root of your webserver. After that you can use your iOS device and open the Safari browser calling this website. After entering the form data, you will receive a mobileconfig file and the device switches to the settings assistent. On Mac OS X, you also can call this document and save it to disk. After opening it, the profile manager opens and the steps are similar to iOS. For signed profiles see the man page automx.conf(5).

Here is a simple example that configures an autoconfig and an autodiscover service (both use the same automx script). You need to copy & paste this lines into your existing website configuration files (for Debian take a look in /etc/apache2/sites-enabled/...):

<VirtualHost *:80>
        ServerName example.com
        ServerAlias autoconfig.example.com
        ServerAdmin webmaster@example.com
        <IfModule mod_wsgi.c>
                WSGIScriptAliasMatch \
                        (?i)^/.+/(autodiscover|config-v1.1)>xml \
                        /usr/lib/automx/automx_wsgi.py
                <Directory "/usr/lib/automx">
                        Order allow,deny
                        Allow from all
                </Directory>
        </IfModule>
</VirtualHost>

<VirtualHost *:443>
        ServerName example.com:443
        ServerAlias autodiscover.example.com:443
        ServerAdmin webmaster@example.com
        <IfModule mod_wsgi.c>
                WSGIScriptAliasMatch \
                        (?i)^/.+/(autodiscover|config-v1.1)>xml \
                        /usr/lib/automx/automx_wsgi.py
                WSGIScriptAlias \
                        /mobileconfig \
                        /usr/lib/automx/automx_wsgi.py
                <Directory "/usr/lib/automx">
                        Order allow,deny
                        Allow from all
                </Directory>
        </IfModule>
</VirtualHost>

Note

If you haven't done so, you also need to configure and enable SSL in your apache-configuration. At least that means enabling the default SSL-site, install (self signed) certificates and activating the ssl-support (e.g. 'a2enmod ssl' for Apache on debian). Don't forget to restart your web-server afterwards! You need also to ajust the paths to automx_wsgi.py in the example above.

Note

ISPs

In an advanced environment with thousands of domains, you can redirect mail clients via DNS entries to your ISP automx provisioning server for Microsoft clients and a web server instance with a wild card ServerName to serve the Mozilla schema.

Add this to your DNS-configuation:

*.example.com. A 192.168.2.1

and this to your virtualhost-definition in your webserver-configuration:

ServerAlias *.example.com

automx comes with a little utility that helps testing proper operation. The next section explains how to use it.

Testing And Debugging automx

The automx-test utility sends configuration requests for Microsoft and Mozilla clients to the web server:

$ automx-test user@example.com

The domainpart in the address determines the list of hostnames that will be queried. In this example autoconfig.example.com and autodiscover.example.com will be contacted.

You should see the web server header. The script will say Success or Failed.

If things go wrong, the error.log is your friend. It will indicate configuration issues, if python modules are missing, if your database can not be queried or anything else that might go wrong. If you also enabled debug in /etc/automx.conf, you will find further information in your automx.log file. Please turn on debug, if you want to send us a bug report. PLEASE NOTICE! Mobileconfig will display a users password in cleartext! So please remove that from bug reports first!

Note

If you split error logs by port, e.g. port 80 and 443, you need to check both. Autoconfig requests will mostly show up in the port 80 error.log, whereas autodiscover will only show up in your 443 error.log.

Authors

Christian Roessner <cr@ys4.de>
Wrote the program.
Patrick Ben Koetter <p@sys4.de>
Wrote the documentation.
Christian Sudec <c.sudec@htlwrn.ac.at>
04-22-2013: Updated the documentation to support automx 0.9.2
automx-0.10.0/doc/html/automx-test.1.html000066400000000000000000000220101217034403100201230ustar00rootroot00000000000000 automx-test

automx-test

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.

Synopsis

automx-test localpart@domainpart

Description

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

Authors

Christian Roessner <cr@sys4.de>
Wrote the program.
Patrick Ben Koetter <p@sys4.de>
Wrote the documentation.
automx-0.10.0/doc/html/automx.conf.5.html000066400000000000000000000572721217034403100201200ustar00rootroot00000000000000 automx.conf

automx.conf

Date: 02/08/2013
Subtitle:automx configuration parameters
Manual Section:5
Manual Group:automx
Copyright: This document has been placed in the public domain.

Description

The automx automx.conf configuration file specifies all parameters that control the automx configuration system. Parameters not specified in automx.conf are left at their default values.

Syntax

The general format of the automx.conf file is as follows:

  • Each logical line has the form parameter = value. Whitespace around the = is ignored, as is whitespace at the end of a logical line.
  • Empty lines and whitespace-only lines are ignored, as are lines whose first non-whitespace character is a #.
  • When the same parameter is defined multiple times, only the last instance is remembered.
  • Uppercase and lowercase matters. Use parameter names, macros and variables exactly as specified.

Structure

The configuration file is split into sections.

  • A section begins with the section name surrounded by square brackets, e.g. [example.com].
  • A section name identifies a domain or subdomain automx should respond with autoconfiguration instructions upon client request.
  • A section defines services which will be sent as autoconfiguration instructions to a client.
  • Section names automx, DEFAULT and global are reserved - they have special meaning.

Services

Each section may specify one more services that should be provided to the client. A service must be defined in a section in order to be enabled. Options specific to a service are given using a concatenation of a service name and the parameter it should configure.

The following concatenation of service name smtp and service option _server creates the smtp_server parameter:

smtp_server = mail.example.com

Service names available in automx are shown in the following list. The service options to create parameters are specified in the section called Parameters:

imap
This name specifies a service as defined in RFC 3501. The protocol to connect to this server is IMAP. Specifying this name is only applicable for account_type = email.
pop
This name specifies a service as defined in RFC 1939. The protocol to connect to this server is POP3. Specifying this name is only applicable for account_type = email.
smtp
This name specifies an SMTP service as defined in RFC 5321. The protocol to connect to this server is SMTP. Specifying this name is only applicable for account_type = email.

Parameters

autoconfig (no default)

Specifies a path to a file that contains static autoconfiguration options following to the Mozilla schema.

Note

This parameter is valid only if backend = file has been specified.

autodiscover (no default)

Specifies a path to a file that contains static autoconfiguration options following to the Microsoft schema.

Note

This parameter is valid only if backend = file has been specified.

account_name (no default, mandatory)
Specifies a display name in MUA account listings.
account_name_short (no default, mandatory)
Specifies a short display name in MUA account listings.
account_type (default: email, mandatory)

Specifies the account type that should be configured:

email

Setting this option will create an email configuration.

Note

The Microsoft schema specifies additional account_types. Currently automx only supports email.

action (default: settings, mandatory)

Specifies whether the response to the client contains configuration settings or if it should visit a different server or use a different address.

Note

This option applies to Microsoft schema only.

settings
The client should use the configuration settings sent in this response.
backend (default: DEFAULT, mandatory)
Specifies the backend method to lookup configuration data. The following options are available:
file

automx should use logic provided within this section to identify a different section which holds configuration settings:

backend = file
filter

automx should use logic provided within this section to identify a different section which holds configuration settings:

backend = filter
global

automx should use general settings defined in the global section:

backend = global
ldap

automx should use a mixture of general and individual settings. General settings are set like static settings. Individual settings should be retrieved from an LDAP query:

backend = ldap

See also automx_ldap(5) for a list of LDAP related configuration options.

sql

automx should use a mixture of general and individual settings. General settings are set like static settings. Individual settings should be retrieved from an SQL query:

backend = sql

See also automx_sql(5) for a list of SQL related configuration options.

static

automx should use general settings provided within the current section:

backend = static
debug (default: no)
Specifies if automx should note client request and server response to the (SSL) error log.
display_name (no default, optional)

Specifies an “optional display name that indicates the name of the sender (...) that could be displayed to the user of a mail application” (see: 3.4. Address Specification in RFC 5322). The client can decide to accept or change the name.

Note

This option applies to Microsoft schema only.

domains (no default)

Specifies a list of domains automx will output autoconfiguration information for.

  • Specify * to let automx reply for any domains listed in a section.
domain, domain, ...
Specify a comma separated list of domains automx should provide autoconfiguration for.
mobileconfig (no default)

Specifies a path to a file that contains static mobileconfiguration options following to the Mozilla schema.

Note

This parameter is valid only if backend = file has been specified.

provider (no default, mandatory)

The FQDN domain name of the domain that provides the configuration service:

provider = example.com
section_filter (default: domainpart, optional)

Specifies a list of one or more filters whose result outputs a section name. The filters will be used in order specified. The first match ends execution of subsequent filters.

These filters will be used instead of the hard coded, internal domainpart filter, which strictly uses the domainpart taken from the email address the client submitted in its configuration request:

section_filters = server_1, server_2
server_1 = /usr/sbin/postmap -q "%u" hash:/etc/postfix/virtual_alias_domains | \
        sed -e 's/^.*@\(\.*\)/\1/g' | grep internal.example.com
server_2 = /usr/sbin/postmap -q "%u" hash:/etc/postfix/virtual_alias_domains | \
        sed -e 's/^.*@\(\.*\)/\1/g' | grep dmz.example.com
service (default: no)
Specifies the service type that should be provided in the configuration response. By default all services are disabled. See the section called Services for a list of valid service names.
service_auth_identity (no default)
Specifies the login name the client should use when it identifies the user in order to gain access to the service. See the section called Macros and Variables for available options.
service_auth (no default)

Specifies the method the client should use when it identifies the user in order to gain access to the service. The following options are available:

Note

Thunderbird 3.0 accepts only plain and secure. It will ignore the whole XML file, if other values are given.

plaintext
The client should use the SASL mechanisms PLAIN or LOGIN to identify the user.
encrypted
The client should use the SASL mechanisms CRAM-MD5 or DIGEST-MD5 to identify the user.
ntlm
The client should use the SASL NTLM mechanism to identify the user.
gssapi
The client should use the SASL GSSAPI mechanism to identify the user.
client-ip-address
The client will not send identification data. Instead the server should recognize the user based on the clients IP address.
tls-client-cert
The client should send a TLS client certificate when the server requests one.
smtp-after-pop
The client should authenticate using POP first, and then start sending messages over SMTP later.
none
The client should not send any identification data.
service_port (no default)
Specifies port number on which the service is offered. Typical, standardized port numbers are:
service_server (no default)
Specifies the IP address or hostname on which the service is offered.
service_encryption (no default)

Specifies whether the client should use a plaintext or an encrypted transport layer for client-server communication. The following options are available:

auto

The client should try to start with starttls, proceed with ssl and settle with none, if only that is available.

Note

This feature is not available in clients following the Mozilla schema. For these clients automx will always output none as encryption level.

none
The client should use an unencrypted transport layer.
ssl

The client should use an SSL3 or TLS1 encrypted transport layer from the start.

Note

This option is typical for smtps, pop3s and imaps services and usually requires a dedicated port on the server for SSL encryption only.

starttls

The client should begin communication on an unencrypted port and then upgrade the communication to TLS via the STARTTLS command.

Note

This option is typical for smtp, pop3 and imap services.

smtp_author (default: %s)

Specifies the envelope sender address used when the client sends a message. See the section called Macros and Variables for available options.

Note

This parameter is experimental. The feature is available for Microsoft clients only. For a definition of “author” see also RFC 5598, Section 2.1 User Actors.

smtp_default (no default)

Specifies if this service should be used globally for all outgoing messages from all accounts.

Note

This feature is available to clients following the Mozilla schema only.

sign_mobileconfig (default: no)
Specifies whether configuration files for iOS and MacOS should be sent signed or not. By default signing is disabled.
sign_cert (no default)
Specifies the path to the cert used to sign configuration files for iOS and MacOS. The file must contain all certificates - certificate and all intermediate certificates concatenated.
sign_key (no default)
Specifies the path to the key used to sign configuration files for iOS and MacOS.

Macros and Variables

The following macros and variables can be used within automx to build service configuration.

%%
This is replaced by a literal % character.
%d
When the input key is an address of the form localpart@domainpart, this macro will be replaced by the (RFC 2253) quoted domain part of the address.
%s
When the input key is an address of the form localpart@domainpart, this macro will be replaced by this (RFC 2253) quoted mail address.
${varname}
The value of ${varname}, retrieved from an LDAP or SQL query, will be used.
%u
When the input key is an address of the form localpart@domainpart, this macro will be replaced by the (RFC 2253) quoted local part of the address.

Authors

Christian Roessner <cr@sys4.de>
Wrote the program.
Patrick Ben Koetter <p@sys4.de>
Wrote the documentation.
automx-0.10.0/doc/html/automx_ldap.5.html000066400000000000000000000325051217034403100201640ustar00rootroot00000000000000 automx_ldap

automx_ldap

Date: 02/08/2013
Subtitle:automx LDAP backend configuration parameters
Manual Section:5
Manual Group:automx
Copyright: This document has been placed in the public domain.

Description

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

Parameters

authzid (no default)
Specifies the SASL proxy authorization identity.
base (default: none)
Specifies the default base DN to use when performing ldap operations. The base must be specified as a Distinguished Name in LDAP format.
binddn (default: none)
Specifies the default bind DN to use when performing ldap operations. The bind DN must be specified as a Distinguished Name in LDAP format.
bindmethod (default: simple)
Specifies how authentication should take place. Valid options are either simple for a simple bind or sasl for a bind that requires SASL authentication.
bindpw (default: none)
Specifies the password used when binddn identifies itself with the LDAP server.
cacert (default: none)
Specifies the path to a file that contains all certificates of Certification Authorities automx should trust.
cert (default: none)
Specifies the path to a file that contains automx's certificate.
cipher (default: TLSv1)
See ciphers(1) for a list of valid options.
filter (default: (objectClass=*))

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.

Note

See the section “Macros and Variables” in automx.conf(5) for a list of available query macros.

host (default: ldap://127.0.0.1/)

Specifies one or more LDAP servers separated by commas as shown in the following example:

host = ldap://127.0.0.1, ldap://192.168.2.1

Important

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.

key (default: none)
Specifies the path to a file that contains automx's private key, which matches automx certificate given with cert.
reqcert (default: never)

Specifies what checks to perform on server certificates in a TLS session, if any. The <level> can be specified as one of the following keywords:

never
The client will not request or check any server certificate. This is the default setting.
allow
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.
try
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.
demand
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.
result_attrs (default: none)
If automx finds one or more entries, the attributes specified by result_attrs are returned. If * is listed, all user attributes are returned.
saslmech (default: none)

Specifies the SASL mechanism to be used for authentication.

cram-md5
The SASL cram-md5 mechanism (see: RFC 2195) will be used to authenticate LDAP bind requests.
digest-md5
The SASL digest-md5 mechanism (see: RFC 2831) will be used to authenticate LDAP bind requests.
external
The SASL external mechanism (see: RFC 4422) will be used to authenticate LDAP bind requests.
gssapi
The SASL gssapi mechanism (see: RFC 4752) will be used to authenticate LDAP bind requests.
none
No SASL mechanism will be use to authenticate LDAP bind requests.
scope (default: sub)
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.
usetls (default: false)
Specifies if automx should use TLS when it connects to the LDAP host.

Authors

Christian Roessner <cr@sys4.de>
Wrote the program.
Patrick Ben Koetter <p@sys4.de>
Wrote the documentation.
automx-0.10.0/doc/html/automx_script.5.html000066400000000000000000000241331217034403100205460ustar00rootroot00000000000000 automx_script

automx_script

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.

Description

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

Parameters

result_attrs (default: none)

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:

result_attrs = server, email, auth, socket

Important

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.

script (default: none)

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

script = /usr/local/bin/example_com.sh "%s"
separator (default: whitespace)

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

separator = ,

Authors

Christian Roessner <cr@sys4.de>
Wrote the program.
Michael Menge
Wrote and contributed this backend.
Patrick Ben Koetter <p@sys4.de>
Wrote the documentation.
automx-0.10.0/doc/html/automx_sql.5.html000066400000000000000000000243551217034403100200470ustar00rootroot00000000000000 automx_sql

automx_sql

Date: 02/08/2013
Subtitle:automx SQL backend configuration parameters
Manual Section:5
Manual Group:automx
Copyright: This document has been placed in the public domain.

Description

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

Parameters

host (default: none)

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:

host = driver://username:password@hostname/database

Important

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.

query (default: none)

Specifies the query that should be sent to the database specified with the host parameter:

query = SELECT displayname, mailaddr FROM mail WHERE mailaddr='%s';

Note

See the section called “Macros and Variables” in automx.conf(5) for a list of available query macros.

result_attrs (default: none)

Specifies the attributes whose values should be used in an automx account setup:

result_attrs = displayname, mailaddr

Authors

Christian Roessner <cr@sys4.de>
Wrote the program.
Patrick Ben Koetter <p@sys4.de>
Wrote the documentation.
automx-0.10.0/doc/man/000077500000000000000000000000001217034403100144205ustar00rootroot00000000000000automx-0.10.0/doc/man/man1/000077500000000000000000000000001217034403100152545ustar00rootroot00000000000000automx-0.10.0/doc/man/man1/automx-test.1000066400000000000000000000026561217034403100176410ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . .TH AUTOMX-TEST 1 "02/08/2013" "" "automx" .SH NAME automx-test \- automx command line client . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp automx\-test \fIlocalpart@domainpart\fP .SH DESCRIPTION .sp The \fBautomx\-test\fP command line client requests autoconfiguration data in Mozilla and Microsoft schema requests and outputs the server response to \fBSTDOUT\fP. .SH AUTHORS .INDENT 0.0 .TP .B Christian Roessner <\fI\%cr@sys4.de\fP> Wrote the program. .TP .B Patrick Ben Koetter <\fI\%p@sys4.de\fP> Wrote the documentation. .UNINDENT .SH SEE ALSO .sp \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 .SH COPYRIGHT This document has been placed in the public domain. .\" Generated by docutils manpage writer. . automx-0.10.0/doc/man/man5/000077500000000000000000000000001217034403100152605ustar00rootroot00000000000000automx-0.10.0/doc/man/man5/automx.conf.5000066400000000000000000000340001217034403100176040ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . .TH AUTOMX.CONF 5 "02/08/2013" "" "automx" .SH NAME automx.conf \- automx configuration parameters . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH DESCRIPTION .sp The \fBautomx\fP automx.conf configuration file specifies all parameters that control the \fBautomx\fP configuration system. Parameters not specified in automx.conf are left at their default values. .SH SYNTAX .sp The general format of the automx.conf file is as follows: .INDENT 0.0 .IP \(bu 2 Each logical line has the form \fBparameter = value\fP. Whitespace around the \fB=\fP is ignored, as is whitespace at the end of a logical line. .IP \(bu 2 Empty lines and whitespace\-only lines are ignored, as are lines whose first non\-whitespace character is a \fB#\fP. .IP \(bu 2 When the same parameter is defined multiple times, only the last instance is remembered. .IP \(bu 2 Uppercase and lowercase matters. Use parameter names, macros and variables exactly as specified. .UNINDENT .SH STRUCTURE .sp The configuration file is split into sections. .INDENT 0.0 .IP \(bu 2 A section begins with the section name surrounded by square brackets, e.g. \fB[example.com]\fP. .IP \(bu 2 A section name identifies a domain or subdomain automx should respond with autoconfiguration instructions upon client request. .IP \(bu 2 A section defines services which will be sent as autoconfiguration instructions to a client. .IP \(bu 2 Section names \fBautomx\fP, \fBDEFAULT\fP and \fBglobal\fP are reserved \- they have special meaning. .UNINDENT .SH SERVICES .sp Each section may specify one more services that should be provided to the client. A service must be defined in a section in order to be enabled. Options specific to a service are given using a concatenation of a service name and the parameter it should configure. .sp The following concatenation of service name smtp and service option _server creates the smtp_server parameter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C smtp_server = mail.example.com .ft P .fi .UNINDENT .UNINDENT .sp Service names available in automx are shown in the following list. The service options to create parameters are specified in the section called \fI\%Parameters\fP: .INDENT 0.0 .TP .B imap This name specifies a service as defined in RFC 3501. The protocol to connect to this server is IMAP. Specifying this name is only applicable for account_type = email. .TP .B pop This name specifies a service as defined in RFC 1939. The protocol to connect to this server is POP3. Specifying this name is only applicable for account_type = email. .TP .B smtp This name specifies an SMTP service as defined in RFC 5321. The protocol to connect to this server is SMTP. Specifying this name is only applicable for account_type = email. .UNINDENT .SH PARAMETERS .INDENT 0.0 .TP .B autoconfig (no default) Specifies a path to a file that contains static autoconfiguration options following to the Mozilla schema. .INDENT 7.0 .INDENT 3.5 .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 This parameter is valid only if backend = file has been specified. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .TP .B autodiscover (no default) Specifies a path to a file that contains static autoconfiguration options following to the Microsoft schema. .INDENT 7.0 .INDENT 3.5 .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 This parameter is valid only if backend = file has been specified. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .TP .B account_name (no default, mandatory) Specifies a display name in MUA account listings. .TP .B account_name_short (no default, mandatory) Specifies a short display name in MUA account listings. .TP .B account_type (default: email, mandatory) Specifies the account type that should be configured: .INDENT 7.0 .TP .B email Setting this option will create an email configuration. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 The Microsoft schema specifies additional account_types. Currently automx only supports email. .UNINDENT .UNINDENT .UNINDENT .TP .B action (default: settings, mandatory) Specifies whether the response to the client contains configuration settings or if it should visit a different server or use a different address. .INDENT 7.0 .INDENT 3.5 .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 This option applies to Microsoft schema only. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B settings The client should use the configuration settings sent in this response. .TP .B backend (default: DEFAULT, mandatory) Specifies the backend method to lookup configuration data. The following options are available: .TP .B file automx should use logic provided within this section to identify a different section which holds configuration settings: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C backend = file .ft P .fi .UNINDENT .UNINDENT .TP .B filter automx should use logic provided within this section to identify a different section which holds configuration settings: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C backend = filter .ft P .fi .UNINDENT .UNINDENT .TP .B global automx should use general settings defined in the global section: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C backend = global .ft P .fi .UNINDENT .UNINDENT .TP .B ldap automx should use a mixture of general and individual settings. General settings are set like static settings. Individual settings should be retrieved from an LDAP query: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C backend = ldap .ft P .fi .UNINDENT .UNINDENT .sp See also automx_ldap(5) for a list of LDAP related configuration options. .TP .B sql automx should use a mixture of general and individual settings. General settings are set like static settings. Individual settings should be retrieved from an SQL query: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C backend = sql .ft P .fi .UNINDENT .UNINDENT .sp See also automx_sql(5) for a list of SQL related configuration options. .TP .B static automx should use general settings provided within the current section: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C backend = static .ft P .fi .UNINDENT .UNINDENT .UNINDENT .TP .B debug (default: no) Specifies if automx should note client request and server response to the (SSL) error log. .TP .B display_name (no default, optional) Specifies an “optional display name that indicates the name of the sender (...) that could be displayed to the user of a mail application” (see: 3.4. Address Specification in RFC 5322). The client can decide to accept or change the name. .INDENT 7.0 .INDENT 3.5 .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 This option applies to Microsoft schema only. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .TP .B domains (no default) Specifies a list of domains automx will output autoconfiguration information for. .INDENT 7.0 .IP \(bu 2 Specify \fB*\fP to let automx reply for any domains listed in a section. .UNINDENT .INDENT 7.0 .TP .B domain, domain, ... Specify a comma separated list of domains automx should provide autoconfiguration for. .UNINDENT .TP .B mobileconfig (no default) Specifies a path to a file that contains static mobileconfiguration options following to the Mozilla schema. .INDENT 7.0 .INDENT 3.5 .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 This parameter is valid only if backend = file has been specified. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .TP .B provider (no default, mandatory) The FQDN domain name of the domain that provides the configuration service: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C provider = example.com .ft P .fi .UNINDENT .UNINDENT .TP .B section_filter (default: domainpart, optional) Specifies a list of one or more filters whose result outputs a section name. The filters will be used in order specified. The first match ends execution of subsequent filters. .sp These filters will be used instead of the hard coded, internal domainpart filter, which strictly uses the domainpart taken from the email address the client submitted in its configuration request: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C section_filters = server_1, server_2 server_1 = /usr/sbin/postmap \-q "%u" hash:/etc/postfix/virtual_alias_domains | \e sed \-e \(aqs/^.*@\e(\e.*\e)/\e1/g\(aq | grep internal.example.com server_2 = /usr/sbin/postmap \-q "%u" hash:/etc/postfix/virtual_alias_domains | \e sed \-e \(aqs/^.*@\e(\e.*\e)/\e1/g\(aq | grep dmz.example.com .ft P .fi .UNINDENT .UNINDENT .TP .B service (default: no) Specifies the service type that should be provided in the configuration response. By default all services are disabled. See the section called \fI\%Services\fP for a list of valid service names. .TP .B service_auth_identity (no default) Specifies the login name the client should use when it identifies the user in order to gain access to the service. See the section called \fI\%Macros and Variables\fP for available options. .TP .B service_auth (no default) Specifies the method the client should use when it identifies the user in order to gain access to the service. The following options are available: .INDENT 7.0 .INDENT 3.5 .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Thunderbird 3.0 accepts only \fBplain\fP and \fBsecure\fP. It will ignore the whole XML file, if other values are given. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B plaintext The client should use the SASL mechanisms PLAIN or LOGIN to identify the user. .TP .B encrypted The client should use the SASL mechanisms CRAM\-MD5 or DIGEST\-MD5 to identify the user. .TP .B ntlm The client should use the SASL NTLM mechanism to identify the user. .TP .B gssapi The client should use the SASL GSSAPI mechanism to identify the user. .TP .B client\-ip\-address The client will not send identification data. Instead the server should recognize the user based on the clients IP address. .TP .B tls\-client\-cert The client should send a TLS client certificate when the server requests one. .TP .B smtp\-after\-pop The client should authenticate using POP first, and then start sending messages over SMTP later. .TP .B none The client should not send any identification data. .UNINDENT .TP .B service_port (no default) Specifies port number on which the service is offered. Typical, standardized port numbers are: .TP .B service_server (no default) Specifies the IP address or hostname on which the service is offered. .TP .B service_encryption (no default) Specifies whether the client should use a plaintext or an encrypted transport layer for client\-server communication. The following options are available: .INDENT 7.0 .TP .B auto The client should try to start with starttls, proceed with ssl and settle with none, if only that is available. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This feature is not available in clients following the Mozilla schema. For these clients automx will always output none as encryption level. .UNINDENT .UNINDENT .TP .B none The client should use an unencrypted transport layer. .TP .B ssl The client should use an SSL3 or TLS1 encrypted transport layer from the start. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This option is typical for smtps, pop3s and imaps services and usually requires a dedicated port on the server for SSL encryption only. .UNINDENT .UNINDENT .TP .B starttls The client should begin communication on an unencrypted port and then upgrade the communication to TLS via the STARTTLS command. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This option is typical for smtp, pop3 and imap services. .UNINDENT .UNINDENT .UNINDENT .TP .B smtp_author (default: %s) Specifies the envelope sender address used when the client sends a message. See the section called \fI\%Macros and Variables\fP for available options. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This parameter is experimental. The feature is available for Microsoft clients only. For a definition of “author” see also RFC 5598, Section 2.1 User Actors. .UNINDENT .UNINDENT .TP .B smtp_default (no default) Specifies if this service should be used globally for all outgoing messages from all accounts. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This feature is available to clients following the Mozilla schema only. .UNINDENT .UNINDENT .TP .B sign_mobileconfig (default: no) Specifies whether configuration files for iOS and MacOS should be sent signed or not. By default signing is disabled. .TP .B sign_cert (no default) Specifies the path to the cert used to sign configuration files for iOS and MacOS. The file must contain all certificates \- certificate and all intermediate certificates concatenated. .TP .B sign_key (no default) Specifies the path to the key used to sign configuration files for iOS and MacOS. .UNINDENT .SH MACROS AND VARIABLES .sp The following macros and variables can be used within automx to build service configuration. .INDENT 0.0 .TP .B %% This is replaced by a literal \fB%\fP character. .TP .B %d When the input key is an address of the form \fI\%localpart@domainpart\fP, this macro will be replaced by the (RFC 2253) quoted domain part of the address. .TP .B %s When the input key is an address of the form \fI\%localpart@domainpart\fP, this macro will be replaced by this (RFC 2253) quoted mail address. .TP .B ${varname} The value of ${varname}, retrieved from an LDAP or SQL query, will be used. .TP .B %u When the input key is an address of the form \fI\%localpart@domainpart\fP, this macro will be replaced by the (RFC 2253) quoted local part of the address. .UNINDENT .SH AUTHORS .INDENT 0.0 .TP .B Christian Roessner <\fI\%cr@sys4.de\fP> Wrote the program. .TP .B Patrick Ben Koetter <\fI\%p@sys4.de\fP> Wrote the documentation. .UNINDENT .SH SEE ALSO .sp \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 .SH COPYRIGHT This document has been placed in the public domain. .\" Generated by docutils manpage writer. . automx-0.10.0/doc/man/man5/automx_ldap.5000066400000000000000000000125451217034403100176720ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . .TH AUTOMX_LDAP 5 "02/08/2013" "" "automx" .SH NAME automx_ldap \- automx LDAP backend configuration parameters . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH DESCRIPTION .sp The automx_ldap(5) man page specifies all parameters that control access from within automx to a LDAP backend. .SH PARAMETERS .INDENT 0.0 .TP .B authzid (no default) Specifies the SASL proxy authorization identity. .TP .B base (default: none) Specifies the default base DN to use when performing ldap operations. The base must be specified as a Distinguished Name in LDAP format. .TP .B binddn (default: none) Specifies the default bind DN to use when performing ldap operations. The bind DN must be specified as a Distinguished Name in LDAP format. .TP .B bindmethod (default: simple) Specifies how authentication should take place. Valid options are either simple for a simple bind or sasl for a bind that requires SASL authentication. .TP .B bindpw (default: none) Specifies the password used when binddn identifies itself with the LDAP server. .TP .B cacert (default: none) Specifies the path to a file that contains all certificates of Certification Authorities automx should trust. .TP .B cert (default: none) Specifies the path to a file that contains automx\(aqs certificate. .TP .B cipher (default: TLSv1) See ciphers(1) for a list of valid options. .TP .B filter (default: (objectClass=*)) 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. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 See the section “Macros and Variables” in automx.conf(5) for a list of available query macros. .UNINDENT .UNINDENT .TP .B host (default: \fI\%ldap://127.0.0.1/\fP) Specifies one or more LDAP servers separated by commas as shown in the following example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C host = ldap://127.0.0.1, ldap://192.168.2.1 .ft P .fi .UNINDENT .UNINDENT .sp \fBIMPORTANT:\fP .INDENT 7.0 .INDENT 3.5 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. .UNINDENT .UNINDENT .TP .B key (default: none) Specifies the path to a file that contains automx\(aqs private key, which matches automx certificate given with cert. .TP .B reqcert (default: never) Specifies what checks to perform on server certificates in a TLS session, if any. The can be specified as one of the following keywords: .INDENT 7.0 .TP .B never The client will not request or check any server certificate. This is the default setting. .TP .B allow 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. .TP .B try 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. .TP .B demand 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. .UNINDENT .TP .B result_attrs (default: none) If automx finds one or more entries, the attributes specified by result_attrs are returned. If * is listed, all user attributes are returned. .TP .B saslmech (default: none) Specifies the SASL mechanism to be used for authentication. .INDENT 7.0 .TP .B cram\-md5 The SASL cram\-md5 mechanism (see: RFC 2195) will be used to authenticate LDAP bind requests. .TP .B digest\-md5 The SASL digest\-md5 mechanism (see: RFC 2831) will be used to authenticate LDAP bind requests. .TP .B external The SASL external mechanism (see: RFC 4422) will be used to authenticate LDAP bind requests. .TP .B gssapi The SASL gssapi mechanism (see: RFC 4752) will be used to authenticate LDAP bind requests. .TP .B none No SASL mechanism will be use to authenticate LDAP bind requests. .UNINDENT .TP .B scope (default: sub) 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. .TP .B usetls (default: false) Specifies if automx should use TLS when it connects to the LDAP host. .UNINDENT .SH AUTHORS .INDENT 0.0 .TP .B Christian Roessner <\fI\%cr@sys4.de\fP> Wrote the program. .TP .B Patrick Ben Koetter <\fI\%p@sys4.de\fP> Wrote the documentation. .UNINDENT .SH SEE ALSO .sp \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 .SH COPYRIGHT This document has been placed in the public domain. .\" Generated by docutils manpage writer. . automx-0.10.0/doc/man/man5/automx_script.5000066400000000000000000000050041217034403100202460ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . .TH AUTOMX_SCRIPT 5 "02/08/2013" "" "automx" .SH NAME automx_script \- automx script backend configuration parameters . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH DESCRIPTION .sp The automx_sript(5) man page specifies all parameters that control access from within automx to a script backend. .SH PARAMETERS .INDENT 0.0 .TP .B result_attrs (default: none) 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: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C result_attrs = server, email, auth, socket .ft P .fi .UNINDENT .UNINDENT .UNINDENT .sp \fBIMPORTANT:\fP .INDENT 0.0 .INDENT 3.5 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. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B script (default: none) Specifies the absolute path to the script which should be run by the backend automx_script(5) backend: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C script = /usr/local/bin/example_com.sh "%s" .ft P .fi .UNINDENT .UNINDENT .TP .B separator (default: whitespace) Specifies the character that separates values returned by the external script: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C separator = , .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SH AUTHORS .INDENT 0.0 .TP .B Christian Roessner <\fI\%cr@sys4.de\fP> Wrote the program. .TP .B Michael Menge Wrote and contributed this backend. .TP .B Patrick Ben Koetter <\fI\%p@sys4.de\fP> Wrote the documentation. .UNINDENT .SH SEE ALSO .sp \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 .SH COPYRIGHT This document has been placed in the public domain. .\" Generated by docutils manpage writer. . automx-0.10.0/doc/man/man5/automx_sql.5000066400000000000000000000051631217034403100175470ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . .TH AUTOMX_SQL 5 "02/08/2013" "" "automx" .SH NAME automx_sql \- automx SQL backend configuration parameters . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH DESCRIPTION .sp The automx_sql(5) man page specifies all parameters that control access from within automx to a SQL backend. .SH PARAMETERS .INDENT 0.0 .TP .B host (default: none) 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: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C host = driver://username:password@hostname/database .ft P .fi .UNINDENT .UNINDENT .sp \fBIMPORTANT:\fP .INDENT 7.0 .INDENT 3.5 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. .UNINDENT .UNINDENT .TP .B query (default: none) Specifies the query that should be sent to the database specified with the host parameter: .INDENT 7.0 .INDENT 3.5 query = SELECT displayname, mailaddr FROM mail WHERE mailaddr=\(aq%s\(aq; .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 See the section called “Macros and Variables” in automx.conf(5) for a list of available query macros. .UNINDENT .UNINDENT .TP .B result_attrs (default: none) Specifies the attributes whose values should be used in an automx account setup: .INDENT 7.0 .INDENT 3.5 result_attrs = displayname, mailaddr .UNINDENT .UNINDENT .UNINDENT .SH AUTHORS .INDENT 0.0 .TP .B Christian Roessner <\fI\%cr@sys4.de\fP> Wrote the program. .TP .B Patrick Ben Koetter <\fI\%p@sys4.de\fP> Wrote the documentation. .UNINDENT .SH SEE ALSO .sp \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 .SH COPYRIGHT This document has been placed in the public domain. .\" Generated by docutils manpage writer. . automx-0.10.0/doc/txt/000077500000000000000000000000001217034403100144645ustar00rootroot00000000000000automx-0.10.0/doc/txt/automx-test.1.txt000066400000000000000000000011671217034403100176630ustar00rootroot00000000000000automx-test 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. Synopsis automx-test localpart@domainpart Description The automx-test command line client requests autoconfiguration data in Mozilla and Microsoft schema requests and outputs the server response to STDOUT. Authors Christian Roessner Wrote the program. Patrick Ben Koetter Wrote the documentation. See also automx(8), automx.conf(5), automx_ldap(5), automx_script(5), automx_sql(5), automx-test(1) automx-0.10.0/doc/txt/automx.conf.5.txt000066400000000000000000000277111217034403100176410ustar00rootroot00000000000000automx.conf Date: 02/08/2013 Subtitle: automx configuration parameters Manual Section: 5 Manual Group: automx Copyright: This document has been placed in the public domain. Description The automx automx.conf configuration file specifies all parameters that control the automx configuration system. Parameters not specified in automx.conf are left at their default values. Syntax The general format of the automx.conf file is as follows: • Each logical line has the form parameter = value. Whitespace around the = is ignored, as is whitespace at the end of a logical line. • Empty lines and whitespace-only lines are ignored, as are lines whose first non-whitespace character is a #. • When the same parameter is defined multiple times, only the last instance is remembered. • Uppercase and lowercase matters. Use parameter names, macros and variables exactly as specified. Structure The configuration file is split into sections. • A section begins with the section name surrounded by square brackets, e.g. [example.com]. • A section name identifies a domain or subdomain automx should respond with autoconfiguration instructions upon client request. • A section defines services which will be sent as autoconfiguration instructions to a client. • Section names automx, DEFAULT and global are reserved - they have special meaning. Services Each section may specify one more services that should be provided to the client. A service must be defined in a section in order to be enabled. Options specific to a service are given using a concatenation of a service name and the parameter it should configure. The following concatenation of service name smtp and service option _server creates the smtp_server parameter: smtp_server = mail.example.com Service names available in automx are shown in the following list. The service options to create parameters are specified in the section called Parameters: imap This name specifies a service as defined in RFC 3501. The protocol to connect to this server is IMAP. Specifying this name is only applicable for account_type = email. pop This name specifies a service as defined in RFC 1939. The protocol to connect to this server is POP3. Specifying this name is only applicable for account_type = email. smtp This name specifies an SMTP service as defined in RFC 5321. The protocol to connect to this server is SMTP. Specifying this name is only applicable for account_type = email. Parameters autoconfig (no default) Specifies a path to a file that contains static autoconfiguration options following to the Mozilla schema. Note This parameter is valid only if backend = file has been specified. autodiscover (no default) Specifies a path to a file that contains static autoconfiguration options following to the Microsoft schema. Note This parameter is valid only if backend = file has been specified. account_name (no default, mandatory) Specifies a display name in MUA account listings. account_name_short (no default, mandatory) Specifies a short display name in MUA account listings. account_type (default: email, mandatory) Specifies the account type that should be configured: email Setting this option will create an email configuration. Note The Microsoft schema specifies additional account_types. Currently automx only supports email. action (default: settings, mandatory) Specifies whether the response to the client contains configuration settings or if it should visit a different server or use a different address. Note This option applies to Microsoft schema only. settings The client should use the configuration settings sent in this response. backend (default: DEFAULT, mandatory) Specifies the backend method to lookup configuration data. The following options are available: file automx should use logic provided within this section to identify a different section which holds configuration settings: backend = file filter automx should use logic provided within this section to identify a different section which holds configuration settings: backend = filter global automx should use general settings defined in the global section: backend = global ldap automx should use a mixture of general and individual settings. General settings are set like static settings. Individual settings should be retrieved from an LDAP query: backend = ldap See also automx_ldap(5) for a list of LDAP related configuration options. sql automx should use a mixture of general and individual settings. General settings are set like static settings. Individual settings should be retrieved from an SQL query: backend = sql See also automx_sql(5) for a list of SQL related configuration options. static automx should use general settings provided within the current section: backend = static debug (default: no) Specifies if automx should note client request and server response to the (SSL) error log. display_name (no default, optional) Specifies an “optional display name that indicates the name of the sender (...) that could be displayed to the user of a mail application” (see: 3.4. Address Specification in RFC 5322). The client can decide to accept or change the name. Note This option applies to Microsoft schema only. domains (no default) Specifies a list of domains automx will output autoconfiguration information for. □ Specify * to let automx reply for any domains listed in a section. domain, domain, ... Specify a comma separated list of domains automx should provide autoconfiguration for. mobileconfig (no default) Specifies a path to a file that contains static mobileconfiguration options following to the Mozilla schema. Note This parameter is valid only if backend = file has been specified. provider (no default, mandatory) The FQDN domain name of the domain that provides the configuration service: provider = example.com section_filter (default: domainpart, optional) Specifies a list of one or more filters whose result outputs a section name. The filters will be used in order specified. The first match ends execution of subsequent filters. These filters will be used instead of the hard coded, internal domainpart filter, which strictly uses the domainpart taken from the email address the client submitted in its configuration request: section_filters = server_1, server_2 server_1 = /usr/sbin/postmap -q "%u" hash:/etc/postfix/virtual_alias_domains | \ sed -e 's/^.*@\(\.*\)/\1/g' | grep internal.example.com server_2 = /usr/sbin/postmap -q "%u" hash:/etc/postfix/virtual_alias_domains | \ sed -e 's/^.*@\(\.*\)/\1/g' | grep dmz.example.com service (default: no) Specifies the service type that should be provided in the configuration response. By default all services are disabled. See the section called Services for a list of valid service names. service_auth_identity (no default) Specifies the login name the client should use when it identifies the user in order to gain access to the service. See the section called Macros and Variables for available options. service_auth (no default) Specifies the method the client should use when it identifies the user in order to gain access to the service. The following options are available: Note Thunderbird 3.0 accepts only plain and secure. It will ignore the whole XML file, if other values are given. plaintext The client should use the SASL mechanisms PLAIN or LOGIN to identify the user. encrypted The client should use the SASL mechanisms CRAM-MD5 or DIGEST-MD5 to identify the user. ntlm The client should use the SASL NTLM mechanism to identify the user. gssapi The client should use the SASL GSSAPI mechanism to identify the user. client-ip-address The client will not send identification data. Instead the server should recognize the user based on the clients IP address. tls-client-cert The client should send a TLS client certificate when the server requests one. smtp-after-pop The client should authenticate using POP first, and then start sending messages over SMTP later. none The client should not send any identification data. service_port (no default) Specifies port number on which the service is offered. Typical, standardized port numbers are: service_server (no default) Specifies the IP address or hostname on which the service is offered. service_encryption (no default) Specifies whether the client should use a plaintext or an encrypted transport layer for client-server communication. The following options are available: auto The client should try to start with starttls, proceed with ssl and settle with none, if only that is available. Note This feature is not available in clients following the Mozilla schema. For these clients automx will always output none as encryption level. none The client should use an unencrypted transport layer. ssl The client should use an SSL3 or TLS1 encrypted transport layer from the start. Note This option is typical for smtps, pop3s and imaps services and usually requires a dedicated port on the server for SSL encryption only. starttls The client should begin communication on an unencrypted port and then upgrade the communication to TLS via the STARTTLS command. Note This option is typical for smtp, pop3 and imap services. smtp_author (default: %s) Specifies the envelope sender address used when the client sends a message. See the section called Macros and Variables for available options. Note This parameter is experimental. The feature is available for Microsoft clients only. For a definition of “author” see also RFC 5598, Section 2.1 User Actors. smtp_default (no default) Specifies if this service should be used globally for all outgoing messages from all accounts. Note This feature is available to clients following the Mozilla schema only. sign_mobileconfig (default: no) Specifies whether configuration files for iOS and MacOS should be sent signed or not. By default signing is disabled. sign_cert (no default) Specifies the path to the cert used to sign configuration files for iOS and MacOS. The file must contain all certificates - certificate and all intermediate certificates concatenated. sign_key (no default) Specifies the path to the key used to sign configuration files for iOS and MacOS. Macros and Variables The following macros and variables can be used within automx to build service configuration. %% This is replaced by a literal % character. %d When the input key is an address of the form localpart@domainpart, this macro will be replaced by the (RFC 2253) quoted domain part of the address. %s When the input key is an address of the form localpart@domainpart, this macro will be replaced by this (RFC 2253) quoted mail address. ${varname} The value of ${varname}, retrieved from an LDAP or SQL query, will be used. %u When the input key is an address of the form localpart@domainpart, this macro will be replaced by the (RFC 2253) quoted local part of the address. Authors Christian Roessner Wrote the program. Patrick Ben Koetter Wrote the documentation. See also automx(8), automx.conf(5), automx_ldap(5), automx_script(5), automx_sql(5), automx-test(1) automx-0.10.0/doc/txt/automx_ldap.5.txt000066400000000000000000000110031217034403100177000ustar00rootroot00000000000000automx_ldap Date: 02/08/2013 Subtitle: automx LDAP backend configuration parameters Manual Section: 5 Manual Group: automx Copyright: This document has been placed in the public domain. Description The automx_ldap(5) man page specifies all parameters that control access from within automx to a LDAP backend. Parameters authzid (no default) Specifies the SASL proxy authorization identity. base (default: none) Specifies the default base DN to use when performing ldap operations. The base must be specified as a Distinguished Name in LDAP format. binddn (default: none) Specifies the default bind DN to use when performing ldap operations. The bind DN must be specified as a Distinguished Name in LDAP format. bindmethod (default: simple) Specifies how authentication should take place. Valid options are either simple for a simple bind or sasl for a bind that requires SASL authentication. bindpw (default: none) Specifies the password used when binddn identifies itself with the LDAP server. cacert (default: none) Specifies the path to a file that contains all certificates of Certification Authorities automx should trust. cert (default: none) Specifies the path to a file that contains automx's certificate. cipher (default: TLSv1) See ciphers(1) for a list of valid options. filter (default: (objectClass=*)) 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. Note See the section “Macros and Variables” in automx.conf(5) for a list of available query macros. host (default: ldap://127.0.0.1/) Specifies one or more LDAP servers separated by commas as shown in the following example: host = ldap://127.0.0.1, ldap://192.168.2.1 Important 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. key (default: none) Specifies the path to a file that contains automx's private key, which matches automx certificate given with cert. reqcert (default: never) Specifies what checks to perform on server certificates in a TLS session, if any. The can be specified as one of the following keywords: never The client will not request or check any server certificate. This is the default setting. allow 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. try 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. demand 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. result_attrs (default: none) If automx finds one or more entries, the attributes specified by result_attrs are returned. If * is listed, all user attributes are returned. saslmech (default: none) Specifies the SASL mechanism to be used for authentication. cram-md5 The SASL cram-md5 mechanism (see: RFC 2195) will be used to authenticate LDAP bind requests. digest-md5 The SASL digest-md5 mechanism (see: RFC 2831) will be used to authenticate LDAP bind requests. external The SASL external mechanism (see: RFC 4422) will be used to authenticate LDAP bind requests. gssapi The SASL gssapi mechanism (see: RFC 4752) will be used to authenticate LDAP bind requests. none No SASL mechanism will be use to authenticate LDAP bind requests. scope (default: sub) 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. usetls (default: false) Specifies if automx should use TLS when it connects to the LDAP host. Authors Christian Roessner Wrote the program. Patrick Ben Koetter Wrote the documentation. See also automx(8), automx.conf(5), automx_ldap(5), automx_script(5), automx_sql(5), automx-test(1) automx-0.10.0/doc/txt/automx_script.5.txt000066400000000000000000000027161217034403100202770ustar00rootroot00000000000000automx_script 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. Description The automx_sript(5) man page specifies all parameters that control access from within automx to a script backend. Parameters result_attrs (default: none) 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: result_attrs = server, email, auth, socket Important 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. script (default: none) Specifies the absolute path to the script which should be run by the backend automx_script(5) backend: script = /usr/local/bin/example_com.sh "%s" separator (default: whitespace) Specifies the character that separates values returned by the external script: separator = , Authors Christian Roessner Wrote the program. Michael Menge Wrote and contributed this backend. Patrick Ben Koetter Wrote the documentation. See also automx(8), automx.conf(5), automx_ldap(5), automx_script(5), automx_sql(5), automx-test(1) automx-0.10.0/doc/txt/automx_sql.5.txt000066400000000000000000000031661217034403100175720ustar00rootroot00000000000000automx_sql Date: 02/08/2013 Subtitle: automx SQL backend configuration parameters Manual Section: 5 Manual Group: automx Copyright: This document has been placed in the public domain. Description The automx_sql(5) man page specifies all parameters that control access from within automx to a SQL backend. Parameters host (default: none) 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: host = driver://username:password@hostname/database Important 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. query (default: none) Specifies the query that should be sent to the database specified with the host parameter: query = SELECT displayname, mailaddr FROM mail WHERE mailaddr='%s'; Note See the section called “Macros and Variables” in automx.conf(5) for a list of available query macros. result_attrs (default: none) Specifies the attributes whose values should be used in an automx account setup: result_attrs = displayname, mailaddr Authors Christian Roessner Wrote the program. Patrick Ben Koetter Wrote the documentation. See also automx(8), automx.conf(5), automx_ldap(5), automx_script(5), automx_sql(5), automx-test(1) automx-0.10.0/setup.py000077500000000000000000000005771217034403100146260ustar00rootroot00000000000000#!/usr/bin/env python from distutils.core import setup setup(name='automx', description='Provides account configuration data to mailclients', url='http://automx.org/', license='GPL', version='0.10.0', py_modules=['automx_wsgi'], packages=['automx'], package_dir={'': 'src'}, data_files=[('/etc', ['src/conf/automx.conf'])], ) automx-0.10.0/src/000077500000000000000000000000001217034403100136675ustar00rootroot00000000000000automx-0.10.0/src/automx-test000077500000000000000000000101061217034403100161050ustar00rootroot00000000000000#!/bin/bash # # automx - auto configuration service # Copyright (c) 2011-2013 [*] sys4 AG # # Authors: Christian Roessner, Patrick Ben Koetter, Marc Schiffbauer # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # VERSION="0.10.0" trap clean_exit EXIT function clean_exit() { [[ -f "$OLREQUEST" ]] && rm "$OLREQUEST" [[ -f "$MBREQUEST" ]] && rm "$MBREQUEST" [[ -f "$MBCRESPONSE" ]] && rm "$MBCRESPONSE" } # We need a mail address if [[ $1 ]]; then PROFILE="$1" else echo "Provide the mail address for which configuration settings should be retrieved." read -ep "Mail address: " PROFILE fi DOMAIN="${PROFILE#*@}" PROGRAM_NAME=$(basename $0) OLREQUEST="$(mktemp /tmp/${PROGRAM_NAME}.XXXXXX)" MBREQUEST="$(mktemp /tmp/${PROGRAM_NAME}.XXXXXX)" MBCRESPONSE="$(mktemp /tmp/${PROGRAM_NAME}.XXXXXX)" # Test Mozilla schema AUTOCONF="autoconfig.$DOMAIN" if [[ $(dig +short $AUTOCONF) ]]; then CON="http://$AUTOCONF/mail/config-v1.1.xml?emailaddress=$PROFILE" echo echo "Testing Autoconfig ..." echo "Connecting to $CON ..." echo wget -S -O - -q --no-check-certificate $CON else echo echo "Autodiscovery domain for Mozilla Thunderbird not found ($AUTOCONF)" echo fi # Test Microsoft schema AUTODISC="autodiscover.$DOMAIN" if [[ -z $(dig +short $AUTODISC) ]]; then # default domain does not exist, try to discover non-default AUTODISC="$(dig +short -t srv _autodiscover._tcp.$DOMAIN)" AUTODISC="${AUTODISC//* /}" AUTODISC="${AUTODISC%.*}" fi if [[ $AUTODISC ]]; then # Test Microsoft Outlook schema CON="https://$AUTODISC/autodiscover/autodiscover.xml" cat <<-REQ >$OLREQUEST http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a $PROFILE REQ echo echo "Testing Autodiscover (Microsoft Outlook(tm)) ..." echo "Connecting to $CON ..." echo wget -S -O - -q --post-file=$OLREQUEST --no-check-certificate $CON rm $OLREQUEST # Test Microsoft Mobile schema cat <<-REQ >$MBREQUEST http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006 $PROFILE REQ echo echo "Testing Autodiscover (mobilesync) ..." echo "Connecting to $CON ..." echo wget -S -O - -q --post-file=$MBREQUEST --no-check-certificate $CON rm $MBREQUEST else echo echo "Autodiscovery domain for Microsoft Outlook(tm) not found (autodiscover.$DOMAIN)" echo fi # Test mobileconfig schema AUTODISC="autodiscover.$DOMAIN" if [[ -z $(dig +short $AUTODISC) ]]; then # default domain does not exist, try to discover non-default AUTODISC="$(dig +short -t srv _autodiscover._tcp.$DOMAIN)" AUTODISC="${AUTODISC//* /}" AUTODISC="${AUTODISC%.*}" fi if [[ $AUTODISC ]]; then # Test Apple mobileconfig schema CON="https://$AUTODISC/mobileconfig" echo -n "_mobileconfig=true&cn=&emailaddress=$PROFILE&password=" > $MBREQUEST echo echo "Testing mobileconfig..." echo "Connecting to $CON ..." echo wget -S -O - -q --post-file=$MBREQUEST --no-check-certificate $CON | hexdump -C else echo echo "Domain for iOS(tm) not found ($DOMAIN)" echo fi # EOF automx-0.10.0/src/automx/000077500000000000000000000000001217034403100152045ustar00rootroot00000000000000automx-0.10.0/src/automx/__init__.py000066400000000000000000000015331217034403100173170ustar00rootroot00000000000000""" automx - auto configuration service Copyright (c) 2011-2013 [*] sys4 AG This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ __version__ = '0.10.0' __author__ = "Christian Roessner, Patrick Ben Koetter" __copyright__ = "Copyright (c) 2011-2013 [*] sys4 AG" __all__ = [ 'config', 'view' ] automx-0.10.0/src/automx/config.py000066400000000000000000001075411217034403100170330ustar00rootroot00000000000000""" automx - auto configuration service Copyright (c) 2011-2013 [*] sys4 AG This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ __version__ = '0.10.0' __author__ = "Christian Roessner, Patrick Ben Koetter" __copyright__ = "Copyright (c) 2011-2013 [*] sys4 AG" import os import sys import ConfigParser import subprocess import shlex import StringIO import re import memcache import logging from ConfigParser import NoOptionError, NoSectionError from ipaddr import IPAddress, IPNetwork from dateutil import parser try: # Python 2.7 from collections import OrderedDict except: # Python 2.5 up to Python 2.7 from automx.ordereddict import OrderedDict class DataNotFoundException(Exception): pass class Config(object, ConfigParser.RawConfigParser): """ This class creates the internal data structure that is completely independend from the view. It may query different backends to gather all required information needed to generate XML output later on in the view class. It uses a OrderdDict to guarentee the correct service order that is needed in the XML output. This said means that it is a difference, if a service like IMAP is configured before POP3 or upside down, because a MUA follows this order. The class currently support smtp, pop and imap services. The class currently supports the following backends: -> global - This backend tells automx to use the global section -> static - all kind of service information that can be sent directly to the MUA -> filter - This backend can execute commands and collects results from stdout. The result may be "", which means we skip further searching. It may return data, which should point to a section that we try to follow. -> ldap - Read all kind of information from LDAP servers. The result attributes are stored in an internal dictionary and if options later on in this backend section (is read as static backend) do contain variables in the form ${attributename}, these are expanded to the collected data. -> sql - Read all kind of information from SQL servers. The result attributes are stored in an internal dictionary. See ldpa -> script - Execute a script and split a result into attributes, which are stored in an internal dictionary, See ldap -> file - Provide static files. If present, all collected data are discarded and only the static file is sent to the remote client. This may change in future releases. Note: There may exist a DEFAULT section that is appended to _all_ sections in the configuration file. That said you can do really complex configurations that on the other hand make life easier. This section also may contain variables, which, if found in the vars-dictionary, are used. """ def __init__(self, environ): ConfigParser.RawConfigParser.__init__(self, defaults=None, dict_type=OrderedDict) self.read("/etc/automx.conf") if not self.has_section("automx"): raise Exception("Missing section 'automx'") if self.has_option("automx", "logfile"): self.logfile = self.get("automx", "logfile") else: self.logfile = None if self.has_option("automx", "debug"): self.debug = self.getboolean("automx", "debug") else: self.debug = False self.memcache = Memcache(self, environ) def configure(self, emailaddress, cn=None, password=None): if emailaddress is None: return OrderedDict() # Full email address containing local part _and_ domain self.__emailaddress = emailaddress # Mobileconfig if cn is None: self.__cn = "" else: self.__cn = cn if password is None: self.__password = "" else: self.__password = password domain = emailaddress.split("@")[1] # The domain that is searched in the config file self.__search_domain = domain self.__automx = dict() # domain individual settings (overwrites some or all defaults) self.__domain = OrderedDict() # if we use dynamic backends, we might earn variables self.__vars = dict() try: provider = self.get("automx", "provider") # provider must be a domainname pattern = "^[0-9a-zA-Z.-]+[a-zA-Z]{2,9}$" prog = re.compile(pattern) result = prog.match(provider) if result is not None: self.__automx["provider"] = result.group(0) else: logging.error(" setting broken!") self.__automx["provider"] = "provider.broken" tmp = self.create_list(self.get("automx", "domains")) self.__automx["domains"] = tmp except TypeError: raise Exception("Missing options in section automx") # if a domain has its own section, use settings from it cmp_domains = [dom.lower() for dom in self.__automx["domains"]] if (domain.lower() in iter(cmp_domains) or self.__automx["domains"][0] == "*"): cmp_sections = [dom.lower() for dom in self.sections()] if domain.lower() in iter(cmp_sections): self.__eval_options(domain) else: if self.has_section("global"): self.__eval_options("global") else: raise Exception("Missing section 'global'") # we need to use default values from config file self.__domain = self.__replace_makro(self.__domain) def __eval_options(self, section, backend=None): settings = self.__domain settings["domain"] = self.__search_domain settings["emailaddress"] = self.__emailaddress section = self.__find_section(section) if self.has_option(section, "backend"): if backend is None: try: backend = self.get(section, "backend") except NoOptionError: raise Exception("Missing option ") if backend in ("static", "static_append"): for opt in iter(self.options(section)): if opt in ("action", "account_type", "account_name", "account_name_short", "display_name", "server_url", "server_name"): tmp = self.get(section, opt) result = self.__expand_vars(tmp) result = self.__replace_makro(result) settings[opt] = result elif opt == "smtp": service = self.__service(section, "smtp") elif opt == "imap": service = self.__service(section, "imap") elif opt == "pop": service = self.__service(section, "pop") elif opt == "sign_mobileconfig": try: settings[opt] = self.getboolean(section, opt) except: logging.error("%s is not boolean!" % opt) settings[opt] = False elif opt in ("sign_cert", "sign_key"): result = self.get(section, opt) if os.path.exists(result): settings[opt] = result else: logging.error("%s cannot read %s" % (opt, result)) else: pass if opt in ("smtp", "imap", "pop"): if backend == "static_append": if settings.has_key(opt): if self.debug: logging.debug("APPEND %s" % service) settings[opt].append(service) else: if self.debug: logging.debug("APPEND NEW %s" % service) settings[opt] = [service] else: # do not include empty services if len(service) != 0: if self.debug: logging.debug("STATIC %s" % service) service_category = OrderedDict() service_category[opt] = [service] settings.update(service_category) # always follow at the end! if "follow" in self.options(section): tmp = self.get(section, "follow") result = self.__expand_vars(tmp) result = self.__replace_makro(result) self.__eval_options(result) elif backend in ("ldap", "ldap_append"): try: import ldap import ldap.sasl except: raise Exception("python ldap missing") ldap_cfg = dict(host = "ldap://127.0.0.1/", base = "", bindmethod = "simple", binddn = None, bindpw = None, saslmech = None, authzid = "", filter = "(objectClass=*)", result_attrs = [], scope = "sub", usetls = "no", cipher = "TLSv1", reqcert ="never", cert = None, key = None, cacert = None) tls = False sasl = False for opt in iter(self.options(section)): if opt in ("host", "base", "bindmethod", "binddn", "bindpw", "saslmech", "authzid", "filter", "result_attrs", "scope", "usetls", "cipher", "reqcert", "cert", "key", "cacert"): result = self.get(section, opt) if opt in ("host", "result_attrs"): result = self.create_list(result) ldap_cfg[opt] = result # Do we connect with TLS? if ldap_cfg["usetls"].lower() in ("yes", "true", "1"): if ldap_cfg["reqcert"] in ("never", "allow", "try", "demand"): rc = ldap_cfg["reqcert"] if rc == "never": reqcert = ldap.OPT_X_TLS_NEVER elif rc == "allow": reqcert = ldap.OPT_X_TLS_ALLOW elif rc == "try": reqcert = ldap.OPT_X_TLS_TRY elif rc == "demand": reqcert = ldap.OPT_X_TLS_DEMAND ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, reqcert) ldap.set_option(ldap.OPT_X_TLS_CIPHER_SUITE, ldap_cfg["cipher"]) if ldap_cfg["cacert"] is not None: ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, ldap_cfg["cacert"]) if ldap_cfg["cert"] is not None: ldap.set_option(ldap.OPT_X_TLS_CERTFILE, ldap_cfg["cert"]) if ldap_cfg["key"] is not None: ldap.set_option(ldap.OPT_X_TLS_KEYFILE, ldap_cfg["key"]) tls = True # Are we SASL binding to our servers? if ldap_cfg["bindmethod"] == "sasl": mech = ldap_cfg["saslmech"] if mech is not None: if mech.lower() == "digest-md5": auth_tokens = ldap.sasl.digest_md5( ldap_cfg["binddn"], ldap_cfg["bindpw"]) elif mech.lower() == "cram-md5": auth_tokens = ldap.sasl.cram_md5( ldap_cfg["binddn"], ldap_cfg["bindpw"]) elif mech.lower() == "external": auth_tokens = ldap.sasl.external( ldap_cfg["authzid"]) elif mech.lower() == "gssapi": auth_tokens = ldap.sasl.gssapi(ldap_cfg["authzid"]) sasl = True con = None for server in iter(ldap_cfg["host"]): try: con = ldap.initialize(server) if tls: con.start_tls_s() if sasl: con.sasl_interactive_bind_s("", auth_tokens) else: con.simple_bind_s(ldap_cfg["binddn"], ldap_cfg["bindpw"]) except Exception, e: logging.error("LDAP: %s" % e) continue break if con is not None: if ldap_cfg["scope"] in ("sub", "subtree"): scope = ldap.SCOPE_SUBTREE elif ldap_cfg["scope"] in ("one", "onelevel"): scope = ldap.SCOPE_ONELEVEL elif ldap_cfg["scope"] in ("base", "exact"): scope = ldap.SCOPE_BASE filter = self.__replace_makro(ldap_cfg["filter"]) rid = con.search(ldap_cfg["base"], scope, filter, ldap_cfg["result_attrs"]) raw_res = (None, None) raw_res = con.result(rid, True, 60) if raw_res[0] == None: con.abandon(rid) raise Exception("LDAP server timeout reached") # connection established, we have results self.__vars = dict() # we did not receive data from LDAP if raw_res[1] != []: for entry in raw_res[1]: for key, value in entry[1].items(): # result attributes might be multi values, but # we only accept the first value. self.__vars[key] = unicode(value[0], "utf-8") else: logging.warning("No LDAP result from server!") raise DataNotFoundException try: con.unbind() except ldap.LDAPError, e: pass if backend == "ldap": self.__eval_options(section, backend="static") else: self.__eval_options(section, backend="static_append") elif backend in ("sql", "sql_append"): try: from sqlalchemy.engine import create_engine except: raise Exception("python sqlalchemy missing") sql_cfg = dict(host = None, query = "", result_attrs = []) for opt in iter(self.options(section)): if opt in ("host", "result_attrs"): result = self.create_list(self.get(section, opt)) sql_cfg[opt] = result if self.has_option(section, "query"): query = self.get(section, "query") sql_cfg["query"] = self.__replace_makro(query) else: raise Exception("Missing option ") for con in iter(sql_cfg["host"]): try: engine = create_engine(con) connection = engine.connect() except Exception, e: logging.error("SQL: %s" % e) continue result = connection.execute(sql_cfg["query"]) for row in result: keys = row.keys() for key in iter(keys): if key in iter(sql_cfg["result_attrs"]): self.__vars[key] = row[key] # Implicit LIMIT 1 here break else: logging.warning("No SQL result from server!") connection.close() raise DataNotFoundException connection.close() break if backend == "sql": self.__eval_options(section, backend="static") else: self.__eval_options(section, backend="static_append") elif backend in ("file", "file_append"): for opt in iter(self.options(section)): if opt in ("autoconfig", "autodiscover", "mobileconfig"): tmp = self.get(section, opt) result = self.__expand_vars(tmp) if os.path.exists(result): settings[opt] = result if backend == "file": self.__eval_options(section, backend="static") else: self.__eval_options(section, backend="static_append") elif backend in ("script", "script_append"): if self.has_option(section, "script"): script_args = self.get(section, "script") else: raise Exception("Missing option

Welcome to Foundation

This is version 4.2.3.


The Grid

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

Six columns

Six columns

Four columns

Four columns

Four columns

Buttons

Getting Started

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

Other Resources

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

  • Foundation Documentation
    Everything you need to know about using the framework.
  • Foundation on Github
    Latest code, issue reports, feature requests and more.
  • @foundationzurb
    Ping us on Twitter if you have questions. If you build something with this we'd love to see it (and send you a totally boss sticker).
automx-0.10.0/src/foundation-scss/javascripts/000077500000000000000000000000001217034403100213375ustar00rootroot00000000000000automx-0.10.0/src/foundation-scss/javascripts/foundation/000077500000000000000000000000001217034403100235055ustar00rootroot00000000000000automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.alerts.js000066400000000000000000000022411217034403100275010ustar00rootroot00000000000000/*jslint unparam: true, browser: true, indent: 2 */ ;(function ($, window, document, undefined) { 'use strict'; Foundation.libs.alerts = { name : 'alerts', version : '4.2.2', settings : { speed: 300, // fade out speed callback: function (){} }, init : function (scope, method, options) { this.scope = scope || this.scope; if (typeof method === 'object') { $.extend(true, this.settings, method); } if (typeof method !== 'string') { if (!this.settings.init) { this.events(); } return this.settings.init; } else { return this[method].call(this, options); } }, events : function () { var self = this; $(this.scope).on('click.fndtn.alerts', '[data-alert] a.close', function (e) { e.preventDefault(); $(this).closest("[data-alert]").fadeOut(self.speed, function () { $(this).remove(); self.settings.callback(); }); }); this.settings.init = true; }, off : function () { $(this.scope).off('.fndtn.alerts'); }, reflow : function () {} }; }(Foundation.zj, this, this.document));automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.clearing.js000066400000000000000000000350271217034403100300030ustar00rootroot00000000000000/*jslint unparam: true, browser: true, indent: 2 */ ;(function ($, window, document, undefined) { 'use strict'; Foundation.libs.clearing = { name : 'clearing', version : '4.2.2', settings : { templates : { viewing : '×' + '' }, // comma delimited list of selectors that, on click, will close clearing, // add 'div.clearing-blackout, div.visible-img' to close on background click close_selectors : '.clearing-close', // event initializers and locks init : false, locked : false }, init : function (scope, method, options) { var self = this; Foundation.inherit(this, 'set_data get_data remove_data throttle data_options'); if (typeof method === 'object') { options = $.extend(true, this.settings, method); } if (typeof method !== 'string') { $(this.scope).find('ul[data-clearing]').each(function () { var $el = $(this), options = options || {}, lis = $el.find('li'), settings = self.get_data($el); if (!settings && lis.length > 0) { options.$parent = $el.parent(); self.set_data($el, $.extend({}, self.settings, options, self.data_options($el))); self.assemble($el.find('li')); if (!self.settings.init) { self.events().swipe_events(); } } }); return this.settings.init; } else { // fire method return this[method].call(this, options); } }, // event binding and initial setup events : function () { var self = this; $(this.scope) .on('click.fndtn.clearing', 'ul[data-clearing] li', function (e, current, target) { var current = current || $(this), target = target || current, next = current.next('li'), settings = self.get_data(current.parent()), image = $(e.target); e.preventDefault(); if (!settings) self.init(); // if clearing is open and the current image is // clicked, go to the next image in sequence if (target.hasClass('visible') && current[0] === target[0] && next.length > 0 && self.is_open(current)) { target = next; image = target.find('img'); } // set current and target to the clicked li if not otherwise defined. self.open(image, current, target); self.update_paddles(target); }) .on('click.fndtn.clearing', '.clearing-main-next', function (e) { this.nav(e, 'next') }.bind(this)) .on('click.fndtn.clearing', '.clearing-main-prev', function (e) { this.nav(e, 'prev') }.bind(this)) .on('click.fndtn.clearing', this.settings.close_selectors, function (e) { Foundation.libs.clearing.close(e, this) }) .on('keydown.fndtn.clearing', function (e) { this.keydown(e) }.bind(this)); $(window).on('resize.fndtn.clearing', function () { this.resize() }.bind(this)); this.settings.init = true; return this; }, swipe_events : function () { var self = this; $(this.scope) .on('touchstart.fndtn.clearing', '.visible-img', function(e) { if (!e.touches) { e = e.originalEvent; } var data = { start_page_x: e.touches[0].pageX, start_page_y: e.touches[0].pageY, start_time: (new Date()).getTime(), delta_x: 0, is_scrolling: undefined }; $(this).data('swipe-transition', data); e.stopPropagation(); }) .on('touchmove.fndtn.clearing', '.visible-img', function(e) { if (!e.touches) { e = e.originalEvent; } // Ignore pinch/zoom events if(e.touches.length > 1 || e.scale && e.scale !== 1) return; var data = $(this).data('swipe-transition'); if (typeof data === 'undefined') { data = {}; } data.delta_x = e.touches[0].pageX - data.start_page_x; if ( typeof data.is_scrolling === 'undefined') { data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) ); } if (!data.is_scrolling && !data.active) { e.preventDefault(); var direction = (data.delta_x < 0) ? 'next' : 'prev'; data.active = true; self.nav(e, direction); } }) .on('touchend.fndtn.clearing', '.visible-img', function(e) { $(this).data('swipe-transition', {}); e.stopPropagation(); }); }, assemble : function ($li) { var $el = $li.parent(); $el.after('
'); var holder = $('#foundationClearingHolder'), settings = this.get_data($el), grid = $el.detach(), data = { grid: '', viewing: settings.templates.viewing }, wrapper = '
' + data.viewing + data.grid + '
'; return holder.after(wrapper).remove(); }, // event callbacks open : function ($image, current, target) { var root = target.closest('.clearing-assembled'), container = root.find('div').first(), visible_image = container.find('.visible-img'), image = visible_image.find('img').not($image); if (!this.locked()) { // set the image to the selected thumbnail image .attr('src', this.load($image)) .css('visibility', 'hidden'); this.loaded(image, function () { image.css('visibility', 'visible'); // toggle the gallery root.addClass('clearing-blackout'); container.addClass('clearing-container'); visible_image.show(); this.fix_height(target) .caption(visible_image.find('.clearing-caption'), $image) .center(image) .shift(current, target, function () { target.siblings().removeClass('visible'); target.addClass('visible'); }); }.bind(this)); } }, close : function (e, el) { e.preventDefault(); var root = (function (target) { if (/blackout/.test(target.selector)) { return target; } else { return target.closest('.clearing-blackout'); } }($(el))), container, visible_image; if (el === e.target && root) { container = root.find('div').first(); visible_image = container.find('.visible-img'); this.settings.prev_index = 0; root.find('ul[data-clearing]') .attr('style', '').closest('.clearing-blackout') .removeClass('clearing-blackout'); container.removeClass('clearing-container'); visible_image.hide(); } return false; }, is_open : function (current) { return current.parent().attr('style').length > 0; }, keydown : function (e) { var clearing = $('.clearing-blackout').find('ul[data-clearing]'); if (e.which === 39) this.go(clearing, 'next'); if (e.which === 37) this.go(clearing, 'prev'); if (e.which === 27) $('a.clearing-close').trigger('click'); }, nav : function (e, direction) { var clearing = $('.clearing-blackout').find('ul[data-clearing]'); e.preventDefault(); this.go(clearing, direction); }, resize : function () { var image = $('.clearing-blackout .visible-img').find('img'); if (image.length) { this.center(image); } }, // visual adjustments fix_height : function (target) { var lis = target.parent().children(), self = this; lis.each(function () { var li = $(this), image = li.find('img'); if (li.height() > self.outerHeight(image)) { li.addClass('fix-height'); } }) .closest('ul') .width(lis.length * 100 + '%'); return this; }, update_paddles : function (target) { var visible_image = target .closest('.carousel') .siblings('.visible-img'); if (target.next().length > 0) { visible_image .find('.clearing-main-next') .removeClass('disabled'); } else { visible_image .find('.clearing-main-next') .addClass('disabled'); } if (target.prev().length > 0) { visible_image .find('.clearing-main-prev') .removeClass('disabled'); } else { visible_image .find('.clearing-main-prev') .addClass('disabled'); } }, center : function (target) { if (!this.rtl) { target.css({ marginLeft : -(this.outerWidth(target) / 2), marginTop : -(this.outerHeight(target) / 2) }); } else { target.css({ marginRight : -(this.outerWidth(target) / 2), marginTop : -(this.outerHeight(target) / 2) }); } return this; }, // image loading and preloading load : function ($image) { if ($image[0].nodeName === "A") { var href = $image.attr('href'); } else { var href = $image.parent().attr('href'); } this.preload($image); if (href) return href; return $image.attr('src'); }, preload : function ($image) { this .img($image.closest('li').next()) .img($image.closest('li').prev()); }, loaded : function (image, callback) { // based on jquery.imageready.js // @weblinc, @jsantell, (c) 2012 function loaded () { callback(); } function bindLoad () { this.one('load', loaded); if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { var src = this.attr( 'src' ), param = src.match( /\?/ ) ? '&' : '?'; param += 'random=' + (new Date()).getTime(); this.attr('src', src + param); } } if (!image.attr('src')) { loaded(); return; } if (image[0].complete || image[0].readyState === 4) { loaded(); } else { bindLoad.call(image); } }, img : function (img) { if (img.length) { var new_img = new Image(), new_a = img.find('a'); if (new_a.length) { new_img.src = new_a.attr('href'); } else { new_img.src = img.find('img').attr('src'); } } return this; }, // image caption caption : function (container, $image) { var caption = $image.data('caption'); if (caption) { container .html(caption) .show(); } else { container .text('') .hide(); } return this; }, // directional methods go : function ($ul, direction) { var current = $ul.find('.visible'), target = current[direction](); if (target.length) { target .find('img') .trigger('click', [current, target]); } }, shift : function (current, target, callback) { var clearing = target.parent(), old_index = this.settings.prev_index || target.index(), direction = this.direction(clearing, current, target), left = parseInt(clearing.css('left'), 10), width = this.outerWidth(target), skip_shift; // we use jQuery animate instead of CSS transitions because we // need a callback to unlock the next animation if (target.index() !== old_index && !/skip/.test(direction)){ if (/left/.test(direction)) { this.lock(); clearing.animate({left : left + width}, 300, this.unlock()); } else if (/right/.test(direction)) { this.lock(); clearing.animate({left : left - width}, 300, this.unlock()); } } else if (/skip/.test(direction)) { // the target image is not adjacent to the current image, so // do we scroll right or not skip_shift = target.index() - this.settings.up_count; this.lock(); if (skip_shift > 0) { clearing.animate({left : -(skip_shift * width)}, 300, this.unlock()); } else { clearing.animate({left : 0}, 300, this.unlock()); } } callback(); }, direction : function ($el, current, target) { var lis = $el.find('li'), li_width = this.outerWidth(lis) + (this.outerWidth(lis) / 4), up_count = Math.floor(this.outerWidth($('.clearing-container')) / li_width) - 1, target_index = lis.index(target), response; this.settings.up_count = up_count; if (this.adjacent(this.settings.prev_index, target_index)) { if ((target_index > up_count) && target_index > this.settings.prev_index) { response = 'right'; } else if ((target_index > up_count - 1) && target_index <= this.settings.prev_index) { response = 'left'; } else { response = false; } } else { response = 'skip'; } this.settings.prev_index = target_index; return response; }, adjacent : function (current_index, target_index) { for (var i = target_index + 1; i >= target_index - 1; i--) { if (i === current_index) return true; } return false; }, // lock management lock : function () { this.settings.locked = true; }, unlock : function () { this.settings.locked = false; }, locked : function () { return this.settings.locked; }, // plugin management/browser quirks outerHTML : function (el) { // support FireFox < 11 return el.outerHTML || new XMLSerializer().serializeToString(el); }, off : function () { $(this.scope).off('.fndtn.clearing'); $(window).off('.fndtn.clearing'); this.remove_data(); // empty settings cache this.settings.init = false; }, reflow : function () { this.init(); } }; }(Foundation.zj, this, this.document)); automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.cookie.js000066400000000000000000000037271217034403100274720ustar00rootroot00000000000000/*! * jQuery Cookie Plugin v1.3 * https://github.com/carhartl/jquery-cookie * * Copyright 2011, Klaus Hartl * Dual licensed under the MIT or GPL Version 2 licenses. * http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/GPL-2.0 * * Modified to work with Zepto.js by ZURB */ (function ($, document, undefined) { var pluses = /\+/g; function raw(s) { return s; } function decoded(s) { return decodeURIComponent(s.replace(pluses, ' ')); } var config = $.cookie = function (key, value, options) { // write if (value !== undefined) { options = $.extend({}, config.defaults, options); if (value === null) { options.expires = -1; } if (typeof options.expires === 'number') { var days = options.expires, t = options.expires = new Date(); t.setDate(t.getDate() + days); } value = config.json ? JSON.stringify(value) : String(value); return (document.cookie = [ encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value), options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE options.path ? '; path=' + options.path : '', options.domain ? '; domain=' + options.domain : '', options.secure ? '; secure' : '' ].join('')); } // read var decode = config.raw ? raw : decoded; var cookies = document.cookie.split('; '); for (var i = 0, l = cookies.length; i < l; i++) { var parts = cookies[i].split('='); if (decode(parts.shift()) === key) { var cookie = decode(parts.join('=')); return config.json ? JSON.parse(cookie) : cookie; } } return null; }; config.defaults = {}; $.removeCookie = function (key, options) { if ($.cookie(key) !== null) { $.cookie(key, null, options); return true; } return false; }; })(Foundation.zj, document);automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.dropdown.js000066400000000000000000000124371217034403100300530ustar00rootroot00000000000000/*jslint unparam: true, browser: true, indent: 2 */ ;(function ($, window, document, undefined) { 'use strict'; Foundation.libs.dropdown = { name : 'dropdown', version : '4.2.0', settings : { activeClass: 'open', is_hover: false, opened: function(){}, closed: function(){} }, init : function (scope, method, options) { this.scope = scope || this.scope; Foundation.inherit(this, 'throttle scrollLeft data_options'); if (typeof method === 'object') { $.extend(true, this.settings, method); } if (typeof method !== 'string') { if (!this.settings.init) { this.events(); } return this.settings.init; } else { return this[method].call(this, options); } }, events : function () { var self = this; $(this.scope) .on('click.fndtn.dropdown', '[data-dropdown]', function (e) { var settings = $.extend({}, self.settings, self.data_options($(this))); e.preventDefault(); if (!settings.is_hover) self.toggle($(this)); }) .on('mouseenter', '[data-dropdown]', function (e) { var settings = $.extend({}, self.settings, self.data_options($(this))); if (settings.is_hover) self.toggle($(this)); }) .on('mouseleave', '[data-dropdown-content]', function (e) { var target = $('[data-dropdown="' + $(this).attr('id') + '"]'), settings = $.extend({}, self.settings, self.data_options(target)); if (settings.is_hover) self.close.call(self, $(this)); }) .on('opened.fndtn.dropdown', '[data-dropdown-content]', this.settings.opened) .on('closed.fndtn.dropdown', '[data-dropdown-content]', this.settings.closed); $('body').on('click.fndtn.dropdown', function (e) { var parent = $(e.target).closest('[data-dropdown-content]'); if ($(e.target).data('dropdown')) { return; } if (parent.length > 0 && ($(e.target).is('[data-dropdown-content]') || $.contains(parent.first()[0], e.target))) { e.stopPropagation(); return; } self.close.call(self, $('[data-dropdown-content]')); }); $(window).on('resize.fndtn.dropdown', self.throttle(function () { self.resize.call(self); }, 50)).trigger('resize'); this.settings.init = true; }, close: function (dropdown) { var self = this; dropdown.each(function () { if ($(this).hasClass(self.settings.activeClass)) { $(this) .css(Foundation.rtl ? 'right':'left', '-99999px') .removeClass(self.settings.activeClass); $(this).trigger('closed'); } }); }, open: function (dropdown, target) { this .css(dropdown .addClass(this.settings.activeClass), target); dropdown.trigger('opened'); }, toggle : function (target) { var dropdown = $('#' + target.data('dropdown')); this.close.call(this, $('[data-dropdown-content]').not(dropdown)); if (dropdown.hasClass(this.settings.activeClass)) { this.close.call(this, dropdown); } else { this.close.call(this, $('[data-dropdown-content]')) this.open.call(this, dropdown, target); } }, resize : function () { var dropdown = $('[data-dropdown-content].open'), target = $("[data-dropdown='" + dropdown.attr('id') + "']"); if (dropdown.length && target.length) { this.css(dropdown, target); } }, css : function (dropdown, target) { var offset_parent = dropdown.offsetParent(); // temporary workaround until 4.2 if (offset_parent.length > 0 && /body/i.test(dropdown.offsetParent()[0].nodeName)) { var position = target.offset(); position.top -= dropdown.offsetParent().offset().top; position.left -= dropdown.offsetParent().offset().left; } else { var position = target.position(); } if (this.small()) { dropdown.css({ position : 'absolute', width: '95%', left: '2.5%', 'max-width': 'none', top: position.top + this.outerHeight(target) }); } else { if (!Foundation.rtl && $(window).width() > this.outerWidth(dropdown) + target.offset().left) { var left = position.left; if (dropdown.hasClass('right')) { dropdown.removeClass('right'); } } else { if (!dropdown.hasClass('right')) { dropdown.addClass('right'); } var left = position.left - (this.outerWidth(dropdown) - this.outerWidth(target)); } dropdown.attr('style', '').css({ position : 'absolute', top: position.top + this.outerHeight(target), left: left }); } return dropdown; }, small : function () { return $(window).width() < 768 || $('html').hasClass('lt-ie9'); }, off: function () { $(this.scope).off('.fndtn.dropdown'); $('html, body').off('.fndtn.dropdown'); $(window).off('.fndtn.dropdown'); $('[data-dropdown-content]').off('.fndtn.dropdown'); this.settings.init = false; }, reflow : function () {} }; }(Foundation.zj, this, this.document)); automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.forms.js000066400000000000000000000424131217034403100273420ustar00rootroot00000000000000(function ($, window, document, undefined) { 'use strict'; Foundation.libs.forms = { name: 'forms', version: '4.2.3', cache: {}, settings: { disable_class: 'no-custom', last_combo : null }, init: function (scope, method, options) { if (typeof method === 'object') { $.extend(true, this.settings, method); } if (typeof method !== 'string') { if (!this.settings.init) { this.events(); } this.assemble(); return this.settings.init; } else { return this[method].call(this, options); } }, assemble: function () { $('form.custom input[type="radio"]', $(this.scope)) .not('[data-customforms="disabled"]') .not('.' + this.settings.disable_class) .each(this.append_custom_markup); $('form.custom input[type="checkbox"]', $(this.scope)) .not('[data-customforms="disabled"]') .not('.' + this.settings.disable_class) .each(this.append_custom_markup); $('form.custom select', $(this.scope)) .not('[data-customforms="disabled"]') .not('.' + this.settings.disable_class) .not('[multiple=multiple]') .each(this.append_custom_select); }, events: function () { var self = this; $(this.scope) .on('click.fndtn.forms', 'form.custom span.custom.checkbox', function (e) { e.preventDefault(); e.stopPropagation(); self.toggle_checkbox($(this)); }) .on('click.fndtn.forms', 'form.custom span.custom.radio', function (e) { e.preventDefault(); e.stopPropagation(); self.toggle_radio($(this)); }) .on('change.fndtn.forms', 'form.custom select', function (e, force_refresh) { if ($(this).is('[data-customforms="disabled"]')) return; self.refresh_custom_select($(this), force_refresh); }) .on('click.fndtn.forms', 'form.custom label', function (e) { if ($(e.target).is('label')) { var $associatedElement = $('#' + self.escape($(this).attr('for'))).not('[data-customforms="disabled"]'), $customCheckbox, $customRadio; if ($associatedElement.length !== 0) { if ($associatedElement.attr('type') === 'checkbox') { e.preventDefault(); $customCheckbox = $(this).find('span.custom.checkbox'); //the checkbox might be outside after the label or inside of another element if ($customCheckbox.length === 0) { $customCheckbox = $associatedElement.add(this).siblings('span.custom.checkbox').first(); } self.toggle_checkbox($customCheckbox); } else if ($associatedElement.attr('type') === 'radio') { e.preventDefault(); $customRadio = $(this).find('span.custom.radio'); //the radio might be outside after the label or inside of another element if ($customRadio.length === 0) { $customRadio = $associatedElement.add(this).siblings('span.custom.radio').first(); } self.toggle_radio($customRadio); } } } }) .on('mousedown.fndtn.forms', 'form.custom div.custom.dropdown', function () { return false; }) .on('click.fndtn.forms', 'form.custom div.custom.dropdown a.current, form.custom div.custom.dropdown a.selector', function (e) { var $this = $(this), $dropdown = $this.closest('div.custom.dropdown'), $select = getFirstPrevSibling($dropdown, 'select'); // make sure other dropdowns close if (!$dropdown.hasClass('open')) $(self.scope).trigger('click'); e.preventDefault(); if (false === $select.is(':disabled')) { $dropdown.toggleClass('open'); if ($dropdown.hasClass('open')) { $(self.scope).on('click.fndtn.forms.customdropdown', function () { $dropdown.removeClass('open'); $(self.scope).off('.fndtn.forms.customdropdown'); }); } else { $(self.scope).on('.fndtn.forms.customdropdown'); } return false; } }) .on('click.fndtn.forms touchend.fndtn.forms', 'form.custom div.custom.dropdown li', function (e) { var $this = $(this), $customDropdown = $this.closest('div.custom.dropdown'), $select = getFirstPrevSibling($customDropdown, 'select'), selectedIndex = 0; e.preventDefault(); e.stopPropagation(); if (!$(this).hasClass('disabled')) { $('div.dropdown').not($customDropdown).removeClass('open'); var $oldThis = $this.closest('ul') .find('li.selected'); $oldThis.removeClass('selected'); $this.addClass('selected'); $customDropdown.removeClass('open') .find('a.current') .text($this.text()); $this.closest('ul').find('li').each(function (index) { if ($this[0] === this) { selectedIndex = index; } }); $select[0].selectedIndex = selectedIndex; //store the old value in data $select.data('prevalue', $oldThis.html()); $select.trigger('change'); } }); $(window).on('keydown', function (e) { var focus = document.activeElement, self = Foundation.libs.forms, dropdown = $('.custom.dropdown.open'); if (dropdown.length > 0) { e.preventDefault(); if (e.which === 13) { dropdown.find('li.selected').trigger('click'); } if (e.which === 27) { dropdown.removeClass('open'); } if (e.which >= 65 && e.which <= 90) { var next = self.go_to(dropdown, e.which), current = dropdown.find('li.selected'); if (next) { current.removeClass('selected'); self.scrollTo(next.addClass('selected'), 300); } } if (e.which === 38) { var current = dropdown.find('li.selected'), prev = current.prev(':not(.disabled)'); if (prev.length > 0) { prev.parent()[0].scrollTop = prev.parent().scrollTop() - self.outerHeight(prev); current.removeClass('selected'); prev.addClass('selected'); } } else if (e.which === 40) { var current = dropdown.find('li.selected'), next = current.next(':not(.disabled)'); if (next.length > 0) { next.parent()[0].scrollTop = next.parent().scrollTop() + self.outerHeight(next); current.removeClass('selected'); next.addClass('selected'); } } } }); this.settings.init = true; }, go_to: function (dropdown, character) { var lis = dropdown.find('li'), count = lis.length; if (count > 0) { for (var i = 0; i < count; i++) { var first_letter = lis.eq(i).text().charAt(0).toLowerCase(); if (first_letter === String.fromCharCode(character).toLowerCase()) return lis.eq(i); } } }, scrollTo: function (el, duration) { if (duration < 0) return; var parent = el.parent(); var li_height = this.outerHeight(el); var difference = (li_height * (el.index())) - parent.scrollTop(); var perTick = difference / duration * 10; this.scrollToTimerCache = setTimeout(function () { if (!isNaN(parseInt(perTick, 10))) { parent[0].scrollTop = parent.scrollTop() + perTick; this.scrollTo(el, duration - 10); } }.bind(this), 10); }, append_custom_markup: function (idx, sel) { var $this = $(sel), type = $this.attr('type'), $span = $this.next('span.custom.' + type); if (!$this.parent().hasClass('switch')) { $this.addClass('hidden-field'); } if ($span.length === 0) { $span = $('').insertAfter($this); } $span.toggleClass('checked', $this.is(':checked')); $span.toggleClass('disabled', $this.is(':disabled')); }, append_custom_select: function (idx, sel) { var self = Foundation.libs.forms, $this = $(sel), $customSelect = $this.next('div.custom.dropdown'), $customList = $customSelect.find('ul'), $selectCurrent = $customSelect.find(".current"), $selector = $customSelect.find(".selector"), $options = $this.find('option'), $selectedOption = $options.filter(':selected'), copyClasses = $this.attr('class') ? $this.attr('class').split(' ') : [], maxWidth = 0, liHtml = '', $listItems, $currentSelect = false; if ($customSelect.length === 0) { var customSelectSize = $this.hasClass('small') ? 'small' : $this.hasClass('medium') ? 'medium' : $this.hasClass('large') ? 'large' : $this.hasClass('expand') ? 'expand' : ''; $customSelect = $('
    '); $selector = $customSelect.find(".selector"); $customList = $customSelect.find("ul"); liHtml = $options.map(function () { var copyClasses = $(this).attr('class') ? $(this).attr('class') : ''; return "
  • " + $(this).html() + "
  • "; }).get().join(''); $customList.append(liHtml); $currentSelect = $customSelect .prepend('' + $selectedOption.html() + '') .find(".current"); $this.after($customSelect) .addClass('hidden-field'); } else { liHtml = $options.map(function () { return "
  • " + $(this).html() + "
  • "; }) .get().join(''); $customList.html('') .append(liHtml); } // endif $customSelect.length === 0 self.assign_id($this, $customSelect); $customSelect.toggleClass('disabled', $this.is(':disabled')); $listItems = $customList.find('li'); // cache list length self.cache[$customSelect.data('id')] = $listItems.length; $options.each(function (index) { if (this.selected) { $listItems.eq(index).addClass('selected'); if ($currentSelect) { $currentSelect.html($(this).html()); } } if ($(this).is(':disabled')) { $listItems.eq(index).addClass('disabled'); } }); // // If we're not specifying a predetermined form size. // if (!$customSelect.is('.small, .medium, .large, .expand')) { // ------------------------------------------------------------------------------------ // This is a work-around for when elements are contained within hidden parents. // For example, when custom-form elements are inside of a hidden reveal modal. // // We need to display the current custom list element as well as hidden parent elements // in order to properly calculate the list item element's width property. // ------------------------------------------------------------------------------------- $customSelect.addClass('open'); // // Quickly, display all parent elements. // This should help us calcualate the width of the list item's within the drop down. // var self = Foundation.libs.forms; self.hidden_fix.adjust($customList); maxWidth = (self.outerWidth($listItems) > maxWidth) ? self.outerWidth($listItems) : maxWidth; Foundation.libs.forms.hidden_fix.reset(); $customSelect.removeClass('open'); } // endif }, assign_id: function ($select, $customSelect) { var id = [+new Date(), Foundation.random_str(5)].join('-'); $select.attr('data-id', id); $customSelect.attr('data-id', id); }, refresh_custom_select: function ($select, force_refresh) { var self = this; var maxWidth = 0, $customSelect = $select.next(), $options = $select.find('option'), $listItems = $customSelect.find('li'); if ($listItems.length !== this.cache[$customSelect.data('id')] || force_refresh) { $customSelect.find('ul').html(''); $options.each(function () { var $li = $('
  • ' + $(this).html() + '
  • '); $customSelect.find('ul').append($li); }); // re-populate $options.each(function (index) { if (this.selected) { $customSelect.find('li').eq(index).addClass('selected'); $customSelect.find('.current').html($(this).html()); } if ($(this).is(':disabled')) { $customSelect.find('li').eq(index).addClass('disabled'); } }); // fix width $customSelect.removeAttr('style') .find('ul').removeAttr('style'); $customSelect.find('li').each(function () { $customSelect.addClass('open'); if (self.outerWidth($(this)) > maxWidth) { maxWidth = self.outerWidth($(this)); } $customSelect.removeClass('open'); }); $listItems = $customSelect.find('li'); // cache list length this.cache[$customSelect.data('id')] = $listItems.length; } }, toggle_checkbox: function ($element) { var $input = $element.prev(), input = $input[0]; if (false === $input.is(':disabled')) { input.checked = ((input.checked) ? false : true); $element.toggleClass('checked'); $input.trigger('change'); } }, toggle_radio: function ($element) { var $input = $element.prev(), $form = $input.closest('form.custom'), input = $input[0]; if (false === $input.is(':disabled')) { $form.find('input[type="radio"][name="' + this.escape($input.attr('name')) + '"]') .next().not($element).removeClass('checked'); if (!$element.hasClass('checked')) { $element.toggleClass('checked'); } input.checked = $element.hasClass('checked'); $input.trigger('change'); } }, escape: function (text) { if (!text) return ''; return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); }, hidden_fix: { /** * Sets all hidden parent elements and self to visibile. * * @method adjust * @param {jQuery Object} $child */ // We'll use this to temporarily store style properties. tmp: [], // We'll use this to set hidden parent elements. hidden: null, adjust: function ($child) { // Internal reference. var _self = this; // Set all hidden parent elements, including this element. _self.hidden = $child.parents(); _self.hidden = _self.hidden.add($child).filter(":hidden"); // Loop through all hidden elements. _self.hidden.each(function () { // Cache the element. var $elem = $(this); // Store the style attribute. // Undefined if element doesn't have a style attribute. _self.tmp.push($elem.attr('style')); // Set the element's display property to block, // but ensure it's visibility is hidden. $elem.css({ 'visibility': 'hidden', 'display': 'block' }); }); }, // end adjust /** * Resets the elements previous state. * * @method reset */ reset: function () { // Internal reference. var _self = this; // Loop through our hidden element collection. _self.hidden.each(function (i) { // Cache this element. var $elem = $(this), _tmp = _self.tmp[i]; // Get the stored 'style' value for this element. // If the stored value is undefined. if (_tmp === undefined) // Remove the style attribute. $elem.removeAttr('style'); else // Otherwise, reset the element style attribute. $elem.attr('style', _tmp); }); // Reset the tmp array. _self.tmp = []; // Reset the hidden elements variable. _self.hidden = null; } // end reset }, off: function () { $(this.scope).off('.fndtn.forms'); }, reflow : function () {} }; var getFirstPrevSibling = function($el, selector) { var $el = $el.prev(); while ($el.length) { if ($el.is(selector)) return $el; $el = $el.prev(); } return $(); }; }(Foundation.zj, this, this.document)); automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.interchange.js000066400000000000000000000152311217034403100305010ustar00rootroot00000000000000/*jslint unparam: true, browser: true, indent: 2 */ ;(function ($, window, document, undefined) { 'use strict'; Foundation.libs.interchange = { name : 'interchange', version : '4.2.2', cache : {}, settings : { load_attr : 'interchange', named_queries : { 'default' : 'only screen and (min-width: 1px)', small : 'only screen and (min-width: 768px)', medium : 'only screen and (min-width: 1280px)', large : 'only screen and (min-width: 1440px)', landscape : 'only screen and (orientation: landscape)', portrait : 'only screen and (orientation: portrait)', retina : 'only screen and (-webkit-min-device-pixel-ratio: 2),' + 'only screen and (min--moz-device-pixel-ratio: 2),' + 'only screen and (-o-min-device-pixel-ratio: 2/1),' + 'only screen and (min-device-pixel-ratio: 2),' + 'only screen and (min-resolution: 192dpi),' + 'only screen and (min-resolution: 2dppx)' }, directives : { replace : function (el, path) { if (/IMG/.test(el[0].nodeName)) { var path_parts = path.split('/'), path_file = path_parts[path_parts.length - 1], orig_path = el[0].src; if (new RegExp(path_file, 'i').test(el[0].src)) return; el[0].src = path; return el.trigger('replace', [el[0].src, orig_path]); } } } }, init : function (scope, method, options) { Foundation.inherit(this, 'throttle'); if (typeof method === 'object') { $.extend(true, this.settings, method); } this.events(); this.images(); if (typeof method !== 'string') { return this.settings.init; } else { return this[method].call(this, options); } }, events : function () { var self = this; $(window).on('resize.fndtn.interchange', self.throttle(function () { self.resize.call(self); }, 50)); }, resize : function () { var cache = this.cache; for (var uuid in cache) { if (cache.hasOwnProperty(uuid)) { var passed = this.results(uuid, cache[uuid]); if (passed) { this.settings.directives[passed .scenario[1]](passed.el, passed.scenario[0]); } } } }, results : function (uuid, scenarios) { var count = scenarios.length, results_arr = []; if (count > 0) { var el = $('[data-uuid="' + uuid + '"]'); for (var i = count - 1; i >= 0; i--) { var rule = scenarios[i][2]; if (this.settings.named_queries.hasOwnProperty(rule)) { var mq = matchMedia(this.settings.named_queries[rule]); } else { var mq = matchMedia(scenarios[i][2]); } if (mq.matches) { return {el: el, scenario: scenarios[i]}; } } } return false; }, images : function (force_update) { if (typeof this.cached_images === 'undefined' || force_update) { return this.update_images(); } return this.cached_images; }, update_images : function () { var images = document.getElementsByTagName('img'), count = images.length, data_attr = 'data-' + this.settings.load_attr; this.cached_images = []; for (var i = count - 1; i >= 0; i--) { this.loaded($(images[i]), (i === 0), function (image, last) { if (image) { var str = image.getAttribute(data_attr) || ''; if (str.length > 0) { this.cached_images.push(image); } } if (last) this.enhance(); }.bind(this)); } return 'deferred'; }, // based on jquery.imageready.js // @weblinc, @jsantell, (c) 2012 loaded : function (image, last, callback) { function loaded () { callback(image[0], last); } function bindLoad () { this.one('load', loaded); if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { var src = this.attr( 'src' ), param = src.match( /\?/ ) ? '&' : '?'; param += 'random=' + (new Date()).getTime(); this.attr('src', src + param); } } if (!image.attr('src')) { loaded(); return; } if (image[0].complete || image[0].readyState === 4) { loaded(); } else { bindLoad.call(image); } }, enhance : function () { var count = this.images().length; for (var i = count - 1; i >= 0; i--) { this._object($(this.images()[i])); } return $(window).trigger('resize'); }, parse_params : function (path, directive, mq) { return [this.trim(path), this.convert_directive(directive), this.trim(mq)]; }, convert_directive : function (directive) { var trimmed = this.trim(directive); if (trimmed.length > 0) { return trimmed; } return 'replace'; }, _object : function(el) { var raw_arr = this.parse_data_attr(el), scenarios = [], count = raw_arr.length; if (count > 0) { for (var i = count - 1; i >= 0; i--) { var split = raw_arr[i].split(/\((.*?)(\))$/); if (split.length > 1) { var cached_split = split[0].split(','), params = this.parse_params(cached_split[0], cached_split[1], split[1]); scenarios.push(params); } } } return this.store(el, scenarios); }, uuid : function (separator) { var delim = separator || "-"; function S4() { return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); } return (S4() + S4() + delim + S4() + delim + S4() + delim + S4() + delim + S4() + S4() + S4()); }, store : function (el, scenarios) { var uuid = this.uuid(), current_uuid = el.data('uuid'); if (current_uuid) return this.cache[current_uuid]; el.attr('data-uuid', uuid); return this.cache[uuid] = scenarios; }, trim : function(str) { if (typeof str === 'string') { return $.trim(str); } return str; }, parse_data_attr : function (el) { var raw = el.data(this.settings.load_attr).split(/\[(.*?)\]/), count = raw.length, output = []; for (var i = count - 1; i >= 0; i--) { if (raw[i].replace(/[\W\d]+/, '').length > 4) { output.push(raw[i]); } } return output; }, reflow : function () { this.images(true); } }; }(Foundation.zj, this, this.document));automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.joyride.js000066400000000000000000000636331217034403100276700ustar00rootroot00000000000000/*jslint unparam: true, browser: true, indent: 2 */ (function ($, window, document, undefined) { 'use strict'; Foundation.libs.joyride = { name: 'joyride', version : '4.2.2', defaults : { expose : false, // turn on or off the expose feature modal : false, // Whether to cover page with modal during the tour tipLocation : 'bottom', // 'top' or 'bottom' in relation to parent nubPosition : 'auto', // override on a per tooltip bases scrollSpeed : 300, // Page scrolling speed in milliseconds, 0 = no scroll animation timer : 0, // 0 = no timer , all other numbers = timer in milliseconds startTimerOnClick : true, // true or false - true requires clicking the first button start the timer startOffset : 0, // the index of the tooltip you want to start on (index of the li) nextButton : true, // true or false to control whether a next button is used tipAnimation : 'fade', // 'pop' or 'fade' in each tip pauseAfter : [], // array of indexes where to pause the tour after exposed : [], // array of expose elements tipAnimationFadeSpeed: 300, // when tipAnimation = 'fade' this is speed in milliseconds for the transition cookieMonster : false, // true or false to control whether cookies are used cookieName : 'joyride', // Name the cookie you'll use cookieDomain : false, // Will this cookie be attached to a domain, ie. '.notableapp.com' cookieExpires : 365, // set when you would like the cookie to expire. tipContainer : 'body', // Where will the tip be attached postRideCallback : function (){}, // A method to call once the tour closes (canceled or complete) postStepCallback : function (){}, // A method to call after each step preStepCallback : function (){}, // A method to call before each step preRideCallback : function (){}, // A method to call before the tour starts (passed index, tip, and cloned exposed element) postExposeCallback : function (){}, // A method to call after an element has been exposed template : { // HTML segments for tip layout link : '×', timer : '
    ', tip : '
    ', wrapper : '
    ', button : '', modal : '
    ', expose : '
    ', exposeCover: '
    ' }, exposeAddClass : '' // One or more space-separated class names to be added to exposed element }, settings : {}, init : function (scope, method, options) { this.scope = scope || this.scope; Foundation.inherit(this, 'throttle data_options scrollTo scrollLeft delay'); if (typeof method === 'object') { $.extend(true, this.settings, this.defaults, method); } else { $.extend(true, this.settings, this.defaults, options); } if (typeof method !== 'string') { if (!this.settings.init) this.events(); return this.settings.init; } else { return this[method].call(this, options); } }, events : function () { var self = this; $(this.scope) .on('click.joyride', '.joyride-next-tip, .joyride-modal-bg', function (e) { e.preventDefault(); if (this.settings.$li.next().length < 1) { this.end(); } else if (this.settings.timer > 0) { clearTimeout(this.settings.automate); this.hide(); this.show(); this.startTimer(); } else { this.hide(); this.show(); } }.bind(this)) .on('click.joyride', '.joyride-close-tip', function (e) { e.preventDefault(); this.end(); }.bind(this)); $(window).on('resize.fndtn.joyride', self.throttle(function () { if ($('[data-joyride]').length > 0 && self.settings.$next_tip) { if (self.settings.exposed.length > 0) { var $els = $(self.settings.exposed); $els.each(function () { var $this = $(this); self.un_expose($this); self.expose($this); }); } if (self.is_phone()) { self.pos_phone(); } else { self.pos_default(false, true); } } }, 100)); this.settings.init = true; }, start : function () { var self = this, $this = $(this.scope).find('[data-joyride]'), integer_settings = ['timer', 'scrollSpeed', 'startOffset', 'tipAnimationFadeSpeed', 'cookieExpires'], int_settings_count = integer_settings.length; if (!this.settings.init) this.init(); // non configureable settings this.settings.$content_el = $this; this.settings.$body = $(this.settings.tipContainer); this.settings.body_offset = $(this.settings.tipContainer).position(); this.settings.$tip_content = this.settings.$content_el.find('> li'); this.settings.paused = false; this.settings.attempts = 0; this.settings.tipLocationPatterns = { top: ['bottom'], bottom: [], // bottom should not need to be repositioned left: ['right', 'top', 'bottom'], right: ['left', 'top', 'bottom'] }; // can we create cookies? if (typeof $.cookie !== 'function') { this.settings.cookieMonster = false; } // generate the tips and insert into dom. if (!this.settings.cookieMonster || this.settings.cookieMonster && $.cookie(this.settings.cookieName) === null) { this.settings.$tip_content.each(function (index) { var $this = $(this); $.extend(true, self.settings, self.data_options($this)); // Make sure that settings parsed from data_options are integers where necessary for (var i = int_settings_count - 1; i >= 0; i--) { self.settings[integer_settings[i]] = parseInt(self.settings[integer_settings[i]], 10); } self.create({$li : $this, index : index}); }); // show first tip if (!this.settings.startTimerOnClick && this.settings.timer > 0) { this.show('init'); this.startTimer(); } else { this.show('init'); } } }, resume : function () { this.set_li(); this.show(); }, tip_template : function (opts) { var $blank, content; opts.tip_class = opts.tip_class || ''; $blank = $(this.settings.template.tip).addClass(opts.tip_class); content = $.trim($(opts.li).html()) + this.button_text(opts.button_text) + this.settings.template.link + this.timer_instance(opts.index); $blank.append($(this.settings.template.wrapper)); $blank.first().attr('data-index', opts.index); $('.joyride-content-wrapper', $blank).append(content); return $blank[0]; }, timer_instance : function (index) { var txt; if ((index === 0 && this.settings.startTimerOnClick && this.settings.timer > 0) || this.settings.timer === 0) { txt = ''; } else { txt = this.outerHTML($(this.settings.template.timer)[0]); } return txt; }, button_text : function (txt) { if (this.settings.nextButton) { txt = $.trim(txt) || 'Next'; txt = this.outerHTML($(this.settings.template.button).append(txt)[0]); } else { txt = ''; } return txt; }, create : function (opts) { var buttonText = opts.$li.attr('data-button') || opts.$li.attr('data-text'), tipClass = opts.$li.attr('class'), $tip_content = $(this.tip_template({ tip_class : tipClass, index : opts.index, button_text : buttonText, li : opts.$li })); $(this.settings.tipContainer).append($tip_content); }, show : function (init) { var $timer = null; // are we paused? if (this.settings.$li === undefined || ($.inArray(this.settings.$li.index(), this.settings.pauseAfter) === -1)) { // don't go to the next li if the tour was paused if (this.settings.paused) { this.settings.paused = false; } else { this.set_li(init); } this.settings.attempts = 0; if (this.settings.$li.length && this.settings.$target.length > 0) { if (init) { //run when we first start this.settings.preRideCallback(this.settings.$li.index(), this.settings.$next_tip); if (this.settings.modal) { this.show_modal(); } } this.settings.preStepCallback(this.settings.$li.index(), this.settings.$next_tip); if (this.settings.modal && this.settings.expose) { this.expose(); } this.settings.tipSettings = $.extend(this.settings, this.data_options(this.settings.$li)); this.settings.timer = parseInt(this.settings.timer, 10); this.settings.tipSettings.tipLocationPattern = this.settings.tipLocationPatterns[this.settings.tipSettings.tipLocation]; // scroll if not modal if (!/body/i.test(this.settings.$target.selector)) { this.scroll_to(); } if (this.is_phone()) { this.pos_phone(true); } else { this.pos_default(true); } $timer = this.settings.$next_tip.find('.joyride-timer-indicator'); if (/pop/i.test(this.settings.tipAnimation)) { $timer.width(0); if (this.settings.timer > 0) { this.settings.$next_tip.show(); this.delay(function () { $timer.animate({ width: $timer.parent().width() }, this.settings.timer, 'linear'); }.bind(this), this.settings.tipAnimationFadeSpeed); } else { this.settings.$next_tip.show(); } } else if (/fade/i.test(this.settings.tipAnimation)) { $timer.width(0); if (this.settings.timer > 0) { this.settings.$next_tip .fadeIn(this.settings.tipAnimationFadeSpeed) .show(); this.delay(function () { $timer.animate({ width: $timer.parent().width() }, this.settings.timer, 'linear'); }.bind(this), this.settings.tipAnimationFadeSpeed); } else { this.settings.$next_tip.fadeIn(this.settings.tipAnimationFadeSpeed); } } this.settings.$current_tip = this.settings.$next_tip; // skip non-existant targets } else if (this.settings.$li && this.settings.$target.length < 1) { this.show(); } else { this.end(); } } else { this.settings.paused = true; } }, is_phone : function () { if (Modernizr) { return Modernizr.mq('only screen and (max-width: 767px)') || $('.lt-ie9').length > 0; } return (this.settings.$window.width() < 767); }, hide : function () { if (this.settings.modal && this.settings.expose) { this.un_expose(); } if (!this.settings.modal) { $('.joyride-modal-bg').hide(); } this.settings.$current_tip.hide(); this.settings.postStepCallback(this.settings.$li.index(), this.settings.$current_tip); }, set_li : function (init) { if (init) { this.settings.$li = this.settings.$tip_content.eq(this.settings.startOffset); this.set_next_tip(); this.settings.$current_tip = this.settings.$next_tip; } else { this.settings.$li = this.settings.$li.next(); this.set_next_tip(); } this.set_target(); }, set_next_tip : function () { this.settings.$next_tip = $(".joyride-tip-guide[data-index='" + this.settings.$li.index() + "']"); this.settings.$next_tip.data('closed', ''); }, set_target : function () { var cl = this.settings.$li.attr('data-class'), id = this.settings.$li.attr('data-id'), $sel = function () { if (id) { return $(document.getElementById(id)); } else if (cl) { return $('.' + cl).first(); } else { return $('body'); } }; this.settings.$target = $sel(); }, scroll_to : function () { var window_half, tipOffset; window_half = $(window).height() / 2; tipOffset = Math.ceil(this.settings.$target.offset().top - window_half + this.outerHeight(this.settings.$next_tip)); if (tipOffset > 0) { this.scrollTo($('html, body'), tipOffset, this.settings.scrollSpeed); } }, paused : function () { return ($.inArray((this.settings.$li.index() + 1), this.settings.pauseAfter) === -1); }, restart : function () { this.hide(); this.settings.$li = undefined; this.show('init'); }, pos_default : function (init, resizing) { var half_fold = Math.ceil($(window).height() / 2), tip_position = this.settings.$next_tip.offset(), $nub = this.settings.$next_tip.find('.joyride-nub'), nub_width = Math.ceil(this.outerWidth($nub) / 2), nub_height = Math.ceil(this.outerHeight($nub) / 2), toggle = init || false; // tip must not be "display: none" to calculate position if (toggle) { this.settings.$next_tip.css('visibility', 'hidden'); this.settings.$next_tip.show(); } if (typeof resizing === 'undefined') { resizing = false; } if (!/body/i.test(this.settings.$target.selector)) { if (this.bottom()) { var leftOffset = this.settings.$target.offset().left; if (Foundation.rtl) { leftOffset = this.settings.$target.offset().width - this.settings.$next_tip.width() + leftOffset; } this.settings.$next_tip.css({ top: (this.settings.$target.offset().top + nub_height + this.outerHeight(this.settings.$target)), left: leftOffset}); this.nub_position($nub, this.settings.tipSettings.nubPosition, 'top'); } else if (this.top()) { var leftOffset = this.settings.$target.offset().left; if (Foundation.rtl) { leftOffset = this.settings.$target.offset().width - this.settings.$next_tip.width() + leftOffset; } this.settings.$next_tip.css({ top: (this.settings.$target.offset().top - this.outerHeight(this.settings.$next_tip) - nub_height), left: leftOffset}); this.nub_position($nub, this.settings.tipSettings.nubPosition, 'bottom'); } else if (this.right()) { this.settings.$next_tip.css({ top: this.settings.$target.offset().top, left: (this.outerWidth(this.settings.$target) + this.settings.$target.offset().left + nub_width)}); this.nub_position($nub, this.settings.tipSettings.nubPosition, 'left'); } else if (this.left()) { this.settings.$next_tip.css({ top: this.settings.$target.offset().top, left: (this.settings.$target.offset().left - this.outerWidth(this.settings.$next_tip) - nub_width)}); this.nub_position($nub, this.settings.tipSettings.nubPosition, 'right'); } if (!this.visible(this.corners(this.settings.$next_tip)) && this.settings.attempts < this.settings.tipSettings.tipLocationPattern.length) { $nub.removeClass('bottom') .removeClass('top') .removeClass('right') .removeClass('left'); this.settings.tipSettings.tipLocation = this.settings.tipSettings.tipLocationPattern[this.settings.attempts]; this.settings.attempts++; this.pos_default(); } } else if (this.settings.$li.length) { this.pos_modal($nub); } if (toggle) { this.settings.$next_tip.hide(); this.settings.$next_tip.css('visibility', 'visible'); } }, pos_phone : function (init) { var tip_height = this.outerHeight(this.settings.$next_tip), tip_offset = this.settings.$next_tip.offset(), target_height = this.outerHeight(this.settings.$target), $nub = $('.joyride-nub', this.settings.$next_tip), nub_height = Math.ceil(this.outerHeight($nub) / 2), toggle = init || false; $nub.removeClass('bottom') .removeClass('top') .removeClass('right') .removeClass('left'); if (toggle) { this.settings.$next_tip.css('visibility', 'hidden'); this.settings.$next_tip.show(); } if (!/body/i.test(this.settings.$target.selector)) { if (this.top()) { this.settings.$next_tip.offset({top: this.settings.$target.offset().top - tip_height - nub_height}); $nub.addClass('bottom'); } else { this.settings.$next_tip.offset({top: this.settings.$target.offset().top + target_height + nub_height}); $nub.addClass('top'); } } else if (this.settings.$li.length) { this.pos_modal($nub); } if (toggle) { this.settings.$next_tip.hide(); this.settings.$next_tip.css('visibility', 'visible'); } }, pos_modal : function ($nub) { this.center(); $nub.hide(); this.show_modal(); }, show_modal : function () { if (!this.settings.$next_tip.data('closed')) { var joyridemodalbg = $('.joyride-modal-bg'); if (joyridemodalbg.length < 1) { $('body').append(this.settings.template.modal).show(); } if (/pop/i.test(this.settings.tipAnimation)) { joyridemodalbg.show(); } else { joyridemodalbg.fadeIn(this.settings.tipAnimationFadeSpeed); } } }, expose : function () { var expose, exposeCover, el, origCSS, origClasses, randId = 'expose-'+Math.floor(Math.random()*10000); if (arguments.length > 0 && arguments[0] instanceof $) { el = arguments[0]; } else if(this.settings.$target && !/body/i.test(this.settings.$target.selector)){ el = this.settings.$target; } else { return false; } if(el.length < 1){ if(window.console){ console.error('element not valid', el); } return false; } expose = $(this.settings.template.expose); this.settings.$body.append(expose); expose.css({ top: el.offset().top, left: el.offset().left, width: this.outerWidth(el, true), height: this.outerHeight(el, true) }); exposeCover = $(this.settings.template.exposeCover); origCSS = { zIndex: el.css('z-index'), position: el.css('position') }; origClasses = el.attr('class') == null ? '' : el.attr('class'); el.css('z-index',parseInt(expose.css('z-index'))+1); if (origCSS.position == 'static') { el.css('position','relative'); } el.data('expose-css',origCSS); el.data('orig-class', origClasses); el.attr('class', origClasses + ' ' + this.settings.exposeAddClass); exposeCover.css({ top: el.offset().top, left: el.offset().left, width: this.outerWidth(el, true), height: this.outerHeight(el, true) }); this.settings.$body.append(exposeCover); expose.addClass(randId); exposeCover.addClass(randId); el.data('expose', randId); this.settings.postExposeCallback(this.settings.$li.index(), this.settings.$next_tip, el); this.add_exposed(el); }, un_expose : function () { var exposeId, el, expose , origCSS, origClasses, clearAll = false; if (arguments.length > 0 && arguments[0] instanceof $) { el = arguments[0]; } else if(this.settings.$target && !/body/i.test(this.settings.$target.selector)){ el = this.settings.$target; } else { return false; } if(el.length < 1){ if (window.console) { console.error('element not valid', el); } return false; } exposeId = el.data('expose'); expose = $('.' + exposeId); if (arguments.length > 1) { clearAll = arguments[1]; } if (clearAll === true) { $('.joyride-expose-wrapper,.joyride-expose-cover').remove(); } else { expose.remove(); } origCSS = el.data('expose-css'); if (origCSS.zIndex == 'auto') { el.css('z-index', ''); } else { el.css('z-index', origCSS.zIndex); } if (origCSS.position != el.css('position')) { if(origCSS.position == 'static') {// this is default, no need to set it. el.css('position', ''); } else { el.css('position', origCSS.position); } } origClasses = el.data('orig-class'); el.attr('class', origClasses); el.removeData('orig-classes'); el.removeData('expose'); el.removeData('expose-z-index'); this.remove_exposed(el); }, add_exposed: function(el){ this.settings.exposed = this.settings.exposed || []; if (el instanceof $ || typeof el === 'object') { this.settings.exposed.push(el[0]); } else if (typeof el == 'string') { this.settings.exposed.push(el); } }, remove_exposed: function(el){ var search, count; if (el instanceof $) { search = el[0] } else if (typeof el == 'string'){ search = el; } this.settings.exposed = this.settings.exposed || []; count = this.settings.exposed.length; for (var i=0; i < count; i++) { if (this.settings.exposed[i] == search) { this.settings.exposed.splice(i, 1); return; } } }, center : function () { var $w = $(window); this.settings.$next_tip.css({ top : ((($w.height() - this.outerHeight(this.settings.$next_tip)) / 2) + $w.scrollTop()), left : ((($w.width() - this.outerWidth(this.settings.$next_tip)) / 2) + this.scrollLeft($w)) }); return true; }, bottom : function () { return /bottom/i.test(this.settings.tipSettings.tipLocation); }, top : function () { return /top/i.test(this.settings.tipSettings.tipLocation); }, right : function () { return /right/i.test(this.settings.tipSettings.tipLocation); }, left : function () { return /left/i.test(this.settings.tipSettings.tipLocation); }, corners : function (el) { var w = $(window), window_half = w.height() / 2, //using this to calculate since scroll may not have finished yet. tipOffset = Math.ceil(this.settings.$target.offset().top - window_half + this.settings.$next_tip.outerHeight()), right = w.width() + this.scrollLeft(w), offsetBottom = w.height() + tipOffset, bottom = w.height() + w.scrollTop(), top = w.scrollTop(); if (tipOffset < top) { if (tipOffset < 0) { top = 0; } else { top = tipOffset; } } if (offsetBottom > bottom) { bottom = offsetBottom; } return [ el.offset().top < top, right < el.offset().left + el.outerWidth(), bottom < el.offset().top + el.outerHeight(), this.scrollLeft(w) > el.offset().left ]; }, visible : function (hidden_corners) { var i = hidden_corners.length; while (i--) { if (hidden_corners[i]) return false; } return true; }, nub_position : function (nub, pos, def) { if (pos === 'auto') { nub.addClass(def); } else { nub.addClass(pos); } }, startTimer : function () { if (this.settings.$li.length) { this.settings.automate = setTimeout(function () { this.hide(); this.show(); this.startTimer(); }.bind(this), this.settings.timer); } else { clearTimeout(this.settings.automate); } }, end : function () { if (this.settings.cookieMonster) { $.cookie(this.settings.cookieName, 'ridden', { expires: this.settings.cookieExpires, domain: this.settings.cookieDomain }); } if (this.settings.timer > 0) { clearTimeout(this.settings.automate); } if (this.settings.modal && this.settings.expose) { this.un_expose(); } this.settings.$next_tip.data('closed', true); $('.joyride-modal-bg').hide(); this.settings.$current_tip.hide(); this.settings.postStepCallback(this.settings.$li.index(), this.settings.$current_tip); this.settings.postRideCallback(this.settings.$li.index(), this.settings.$current_tip); $('.joyride-tip-guide').remove(); }, outerHTML : function (el) { // support FireFox < 11 return el.outerHTML || new XMLSerializer().serializeToString(el); }, off : function () { $(this.scope).off('.joyride'); $(window).off('.joyride'); $('.joyride-close-tip, .joyride-next-tip, .joyride-modal-bg').off('.joyride'); $('.joyride-tip-guide, .joyride-modal-bg').remove(); clearTimeout(this.settings.automate); this.settings = {}; }, reflow : function () {} }; }(Foundation.zj, this, this.document)); automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.js000066400000000000000000000271721217034403100262220ustar00rootroot00000000000000/* * Foundation Responsive Library * http://foundation.zurb.com * Copyright 2013, ZURB * Free to use under the MIT license. * http://www.opensource.org/licenses/mit-license.php */ /*jslint unparam: true, browser: true, indent: 2 */ // Accommodate running jQuery or Zepto in noConflict() mode by // using an anonymous function to redefine the $ shorthand name. // See http://docs.jquery.com/Using_jQuery_with_Other_Libraries // and http://zeptojs.com/ var libFuncName = null; if (typeof jQuery === "undefined" && typeof Zepto === "undefined" && typeof $ === "function") { libFuncName = $; } else if (typeof jQuery === "function") { libFuncName = jQuery; } else if (typeof Zepto === "function") { libFuncName = Zepto; } else { throw new TypeError(); } (function ($, window, document, undefined) { 'use strict'; /* matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license https://github.com/paulirish/matchMedia.js */ window.matchMedia = window.matchMedia || (function( doc, undefined ) { "use strict"; var bool, docElem = doc.documentElement, refNode = docElem.firstElementChild || docElem.firstChild, // fakeBody required for fakeBody = doc.createElement( "body" ), div = doc.createElement( "div" ); div.id = "mq-test-1"; div.style.cssText = "position:absolute;top:-100em"; fakeBody.style.background = "none"; fakeBody.appendChild(div); return function(q){ div.innerHTML = "­"; docElem.insertBefore( fakeBody, refNode ); bool = div.offsetWidth === 42; docElem.removeChild( fakeBody ); return { matches: bool, media: q }; }; }( document )); // add dusty browser stuff if (!Array.prototype.filter) { Array.prototype.filter = function(fun /*, thisp */) { "use strict"; if (this == null) { throw new TypeError(); } var t = Object(this), len = t.length >>> 0; if (typeof fun !== "function") { return; } var res = [], thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in t) { var val = t[i]; // in case fun mutates this if (fun && fun.call(thisp, val, i, t)) { res.push(val); } } } return res; } } if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== "function") { // closest thing possible to the ECMAScript 5 internal IsCallable function throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () {}, fBound = function () { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }; } if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { "use strict"; if (this == null) { throw new TypeError(); } var t = Object(this); var len = t.length >>> 0; if (len === 0) { return -1; } var n = 0; if (arguments.length > 1) { n = Number(arguments[1]); if (n != n) { // shortcut for verifying if it's NaN n = 0; } else if (n != 0 && n != Infinity && n != -Infinity) { n = (n > 0 || -1) * Math.floor(Math.abs(n)); } } if (n >= len) { return -1; } var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); for (; k < len; k++) { if (k in t && t[k] === searchElement) { return k; } } return -1; } } // fake stop() for zepto. $.fn.stop = $.fn.stop || function() { return this; }; window.Foundation = { name : 'Foundation', version : '4.2.3', cache : {}, init : function (scope, libraries, method, options, response, /* internal */ nc) { var library_arr, args = [scope, method, options, response], responses = [], nc = nc || false; // disable library error catching, // used for development only if (nc) this.nc = nc; // check RTL this.rtl = /rtl/i.test($('html').attr('dir')); // set foundation global scope this.scope = scope || this.scope; if (libraries && typeof libraries === 'string' && !/reflow/i.test(libraries)) { if (/off/i.test(libraries)) return this.off(); library_arr = libraries.split(' '); if (library_arr.length > 0) { for (var i = library_arr.length - 1; i >= 0; i--) { responses.push(this.init_lib(library_arr[i], args)); } } } else { if (/reflow/i.test(libraries)) args[1] = 'reflow'; for (var lib in this.libs) { responses.push(this.init_lib(lib, args)); } } // if first argument is callback, add to args if (typeof libraries === 'function') { args.unshift(libraries); } return this.response_obj(responses, args); }, response_obj : function (response_arr, args) { for (var i = 0, len = args.length; i < len; i++) { if (typeof args[i] === 'function') { return args[i]({ errors: response_arr.filter(function (s) { if (typeof s === 'string') return s; }) }); } } return response_arr; }, init_lib : function (lib, args) { return this.trap(function () { if (this.libs.hasOwnProperty(lib)) { this.patch(this.libs[lib]); return this.libs[lib].init.apply(this.libs[lib], args); } else { return function () {}; } }.bind(this), lib); }, trap : function (fun, lib) { if (!this.nc) { try { return fun(); } catch (e) { return this.error({name: lib, message: 'could not be initialized', more: e.name + ' ' + e.message}); } } return fun(); }, patch : function (lib) { this.fix_outer(lib); lib.scope = this.scope; lib.rtl = this.rtl; }, inherit : function (scope, methods) { var methods_arr = methods.split(' '); for (var i = methods_arr.length - 1; i >= 0; i--) { if (this.lib_methods.hasOwnProperty(methods_arr[i])) { this.libs[scope.name][methods_arr[i]] = this.lib_methods[methods_arr[i]]; } } }, random_str : function (length) { var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); if (!length) { length = Math.floor(Math.random() * chars.length); } var str = ''; for (var i = 0; i < length; i++) { str += chars[Math.floor(Math.random() * chars.length)]; } return str; }, libs : {}, // methods that can be inherited in libraries lib_methods : { set_data : function (node, data) { // this.name references the name of the library calling this method var id = [this.name,+new Date(),Foundation.random_str(5)].join('-'); Foundation.cache[id] = data; node.attr('data-' + this.name + '-id', id); return data; }, get_data : function (node) { return Foundation.cache[node.attr('data-' + this.name + '-id')]; }, remove_data : function (node) { if (node) { delete Foundation.cache[node.attr('data-' + this.name + '-id')]; node.attr('data-' + this.name + '-id', ''); } else { $('[data-' + this.name + '-id]').each(function () { delete Foundation.cache[$(this).attr('data-' + this.name + '-id')]; $(this).attr('data-' + this.name + '-id', ''); }); } }, throttle : function(fun, delay) { var timer = null; return function () { var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function () { fun.apply(context, args); }, delay); }; }, // parses data-options attribute on nodes and turns // them into an object data_options : function (el) { var opts = {}, ii, p, opts_arr = (el.attr('data-options') || ':').split(';'), opts_len = opts_arr.length; function isNumber (o) { return ! isNaN (o-0) && o !== null && o !== "" && o !== false && o !== true; } function trim(str) { if (typeof str === 'string') return $.trim(str); return str; } // parse options for (ii = opts_len - 1; ii >= 0; ii--) { p = opts_arr[ii].split(':'); if (/true/i.test(p[1])) p[1] = true; if (/false/i.test(p[1])) p[1] = false; if (isNumber(p[1])) p[1] = parseInt(p[1], 10); if (p.length === 2 && p[0].length > 0) { opts[trim(p[0])] = trim(p[1]); } } return opts; }, delay : function (fun, delay) { return setTimeout(fun, delay); }, // animated scrolling scrollTo : function (el, to, duration) { if (duration < 0) return; var difference = to - $(window).scrollTop(); var perTick = difference / duration * 10; this.scrollToTimerCache = setTimeout(function() { if (!isNaN(parseInt(perTick, 10))) { window.scrollTo(0, $(window).scrollTop() + perTick); this.scrollTo(el, to, duration - 10); } }.bind(this), 10); }, // not supported in core Zepto scrollLeft : function (el) { if (!el.length) return; return ('scrollLeft' in el[0]) ? el[0].scrollLeft : el[0].pageXOffset; }, // test for empty object or array empty : function (obj) { if (obj.length && obj.length > 0) return false; if (obj.length && obj.length === 0) return true; for (var key in obj) { if (hasOwnProperty.call(obj, key)) return false; } return true; } }, fix_outer : function (lib) { lib.outerHeight = function (el, bool) { if (typeof Zepto === 'function') { return el.height(); } if (typeof bool !== 'undefined') { return el.outerHeight(bool); } return el.outerHeight(); }; lib.outerWidth = function (el) { if (typeof Zepto === 'function') { return el.width(); } if (typeof bool !== 'undefined') { return el.outerWidth(bool); } return el.outerWidth(); }; }, error : function (error) { return error.name + ' ' + error.message + '; ' + error.more; }, // remove all foundation events. off: function () { $(this.scope).off('.fndtn'); $(window).off('.fndtn'); return true; }, zj : function () { if (typeof Zepto !== 'undefined') { return Zepto; } else { return jQuery; } }() }; $.fn.foundation = function () { var args = Array.prototype.slice.call(arguments, 0); return this.each(function () { Foundation.init.apply(Foundation, [this].concat(args)); return this; }); }; }(libFuncName, this, this.document)); automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.magellan.js000066400000000000000000000113511217034403100277710ustar00rootroot00000000000000/*jslint unparam: true, browser: true, indent: 2 */ ;(function ($, window, document, undefined) { 'use strict'; Foundation.libs.magellan = { name : 'magellan', version : '4.2.2', settings : { activeClass: 'active' }, init : function (scope, method, options) { this.scope = scope || this.scope; Foundation.inherit(this, 'data_options'); if (typeof method === 'object') { $.extend(true, this.settings, method); } if (typeof method !== 'string') { if (!this.settings.init) { this.fixed_magellan = $("[data-magellan-expedition]"); this.set_threshold(); this.last_destination = $('[data-magellan-destination]').last(); this.events(); } return this.settings.init; } else { return this[method].call(this, options); } }, events : function () { var self = this; $(this.scope).on('arrival.fndtn.magellan', '[data-magellan-arrival]', function (e) { var $destination = $(this), $expedition = $destination.closest('[data-magellan-expedition]'), activeClass = $expedition.attr('data-magellan-active-class') || self.settings.activeClass; $destination .closest('[data-magellan-expedition]') .find('[data-magellan-arrival]') .not($destination) .removeClass(activeClass); $destination.addClass(activeClass); }); this.fixed_magellan .on('update-position.fndtn.magellan', function(){ var $el = $(this); // $el.data("magellan-fixed-position",""); // $el.data("magellan-top-offset", ""); }) .trigger('update-position'); $(window) .on('resize.fndtn.magellan', function() { this.fixed_magellan.trigger('update-position'); }.bind(this)) .on('scroll.fndtn.magellan', function() { var windowScrollTop = $(window).scrollTop(); self.fixed_magellan.each(function() { var $expedition = $(this); if (typeof $expedition.data('magellan-top-offset') === 'undefined') { $expedition.data('magellan-top-offset', $expedition.offset().top); } if (typeof $expedition.data('magellan-fixed-position') === 'undefined') { $expedition.data('magellan-fixed-position', false) } var fixed_position = (windowScrollTop + self.settings.threshold) > $expedition.data("magellan-top-offset"); var attr = $expedition.attr('data-magellan-top-offset'); if ($expedition.data("magellan-fixed-position") != fixed_position) { $expedition.data("magellan-fixed-position", fixed_position); if (fixed_position) { $expedition.addClass('fixed'); $expedition.css({position:"fixed", top:0}); } else { $expedition.removeClass('fixed'); $expedition.css({position:"", top:""}); } if (fixed_position && typeof attr != 'undefined' && attr != false) { $expedition.css({position:"fixed", top:attr + "px"}); } } }); }); if (this.last_destination.length > 0) { $(window).on('scroll.fndtn.magellan', function (e) { var windowScrollTop = $(window).scrollTop(), scrolltopPlusHeight = windowScrollTop + $(window).height(), lastDestinationTop = Math.ceil(self.last_destination.offset().top); $('[data-magellan-destination]').each(function () { var $destination = $(this), destination_name = $destination.attr('data-magellan-destination'), topOffset = $destination.offset().top - windowScrollTop; if (topOffset <= self.settings.threshold) { $("[data-magellan-arrival='" + destination_name + "']").trigger('arrival'); } // 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 if (scrolltopPlusHeight >= $(self.scope).height() && lastDestinationTop > windowScrollTop && lastDestinationTop < scrolltopPlusHeight) { $('[data-magellan-arrival]').last().trigger('arrival'); } }); }); } this.settings.init = true; }, set_threshold : function () { if (!this.settings.threshold) { this.settings.threshold = (this.fixed_magellan.length > 0) ? this.outerHeight(this.fixed_magellan, true) : 0; } }, off : function () { $(this.scope).off('.fndtn.magellan'); }, reflow : function () {} }; }(Foundation.zj, this, this.document));automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.orbit.js000066400000000000000000000361361217034403100273400ustar00rootroot00000000000000;(function ($, window, document, undefined) { 'use strict'; Foundation.libs = Foundation.libs || {}; Foundation.libs.orbit = { name: 'orbit', version: '4.2.0', settings: { timer_speed: 10000, pause_on_hover: true, resume_on_mouseout: false, animation_speed: 500, bullets: true, stack_on_small: true, navigation_arrows: true, slide_number: true, container_class: 'orbit-container', stack_on_small_class: 'orbit-stack-on-small', next_class: 'orbit-next', prev_class: 'orbit-prev', timer_container_class: 'orbit-timer', timer_paused_class: 'paused', timer_progress_class: 'orbit-progress', slides_container_class: 'orbit-slides-container', bullets_container_class: 'orbit-bullets', bullets_active_class: 'active', slide_number_class: 'orbit-slide-number', caption_class: 'orbit-caption', active_slide_class: 'active', orbit_transition_class: 'orbit-transitioning' }, init: function (scope, method, options) { var self = this; Foundation.inherit(self, 'data_options'); if (typeof method === 'object') { $.extend(true, self.settings, method); } if ($(scope).is('[data-orbit]')) { var scoped_self = $.extend(true, {}, self); scoped_self._init(idx, el); } $('[data-orbit]', scope).each(function(idx, el) { var scoped_self = $.extend(true, {}, self); scoped_self._init(idx, el); }); }, _container_html: function() { var self = this; return '
    '; }, _bullets_container_html: function($slides) { var self = this, $list = $('
      '); $slides.each(function(idx, slide) { var $item = $('
    1. '); if (idx === 0) { $item.addClass(self.settings.bullets_active_class); } $list.append($item); }); return $list; }, _slide_number_html: function(slide_number, total_slides) { var self = this, $container = $('
      '); $container.append('' + slide_number + ' of ' + total_slides + ''); return $container; }, _timer_html: function() { var self = this; if (typeof self.settings.timer_speed === 'number' && self.settings.timer_speed > 0) { return '
      '; } else { return ''; } }, _next_html: function() { var self = this; return 'Next '; }, _prev_html: function() { var self = this; return 'Prev '; }, _init: function (idx, slider) { var self = this, $slides_container = $(slider), $container = $slides_container.wrap(self._container_html()).parent(), $slides = $slides_container.children(); $.extend(true, self.settings, self.data_options($slides_container)); if (self.settings.navigation_arrows) { $container.append(self._prev_html()); $container.append(self._next_html()); } $slides_container.addClass(self.settings.slides_container_class); if (self.settings.stack_on_small) { $container.addClass(self.settings.stack_on_small_class); } if (self.settings.slide_number) { $container.append(self._slide_number_html(1, $slides.length)); } $container.append(self._timer_html()); if (self.settings.bullets) { $container.after(self._bullets_container_html($slides)); } // To better support the "sliding" effect it's easier // if we just clone the first and last slides $slides_container.append($slides.first().clone().attr('data-orbit-slide','')); $slides_container.prepend($slides.last().clone().attr('data-orbit-slide','')); // Make the first "real" slide active $slides_container.css(Foundation.rtl ? 'marginRight' : 'marginLeft', '-100%'); $slides.first().addClass(self.settings.active_slide_class); self._init_events($slides_container); self._init_dimensions($slides_container); self._start_timer($slides_container); }, _init_events: function ($slides_container) { var self = this, $container = $slides_container.parent(); $(window) .on('load.fndtn.orbit', function() { $slides_container.height(''); $slides_container.height($slides_container.height($container.height())); $slides_container.trigger('orbit:ready'); }) .on('resize.fndtn.orbit', function() { $slides_container.height(''); $slides_container.height($slides_container.height($container.height())); }); $(document).on('click.fndtn.orbit', '[data-orbit-link]', function(e) { e.preventDefault(); var id = $(e.currentTarget).attr('data-orbit-link'), $slide = $slides_container.find('[data-orbit-slide=' + id + ']').first(); if ($slide.length === 1) { self._reset_timer($slides_container, true); self._goto($slides_container, $slide.index(), function() {}); } }); $container.siblings('.' + self.settings.bullets_container_class) .on('click.fndtn.orbit', '[data-orbit-slide-number]', function(e) { e.preventDefault(); self._reset_timer($slides_container, true); self._goto($slides_container, $(e.currentTarget).data('orbit-slide-number'),function() {}); }); $container .on('mouseenter.fndtn.orbit', function(e) { if (self.settings.pause_on_hover) { self._stop_timer($slides_container); } }) .on('mouseleave.fndtn.orbit', function(e) { if (self.settings.resume_on_mouseout) { self._start_timer($slides_container); } }) .on('orbit:after-slide-change.fndtn.orbit', function(e, orbit) { var $slide_number = $container.find('.' + self.settings.slide_number_class); if ($slide_number.length === 1) { $slide_number.replaceWith(self._slide_number_html(orbit.slide_number, orbit.total_slides)); } }) .on('orbit:next-slide.fndtn.orbit click.fndtn.orbit', '.' + self.settings.next_class.split(" ").join("."), function(e) { e.preventDefault(); self._reset_timer($slides_container, true); self._goto($slides_container, 'next', function() {}); }) .on('orbit:prev-slide.fndtn.orbit click.fndtn.orbit', '.' + self.settings.prev_class.split(" ").join("."), function(e) { e.preventDefault(); self._reset_timer($slides_container, true); self._goto($slides_container, 'prev', function() {}); }) .on('orbit:toggle-play-pause.fndtn.orbit click.fndtn.orbit touchstart.fndtn.orbit', '.' + self.settings.timer_container_class, function(e) { e.preventDefault(); var $timer = $(e.currentTarget).toggleClass(self.settings.timer_paused_class), $slides_container = $timer.closest('.' + self.settings.container_class) .find('.' + self.settings.slides_container_class); if ($timer.hasClass(self.settings.timer_paused_class)) { self._stop_timer($slides_container); } else { self._start_timer($slides_container); } }) .on('touchstart.fndtn.orbit', function(e) { if (!e.touches) { e = e.originalEvent; } var data = { start_page_x: e.touches[0].pageX, start_page_y: e.touches[0].pageY, start_time: (new Date()).getTime(), delta_x: 0, is_scrolling: undefined }; $container.data('swipe-transition', data); e.stopPropagation(); }) .on('touchmove.fndtn.orbit', function(e) { if (!e.touches) { e = e.originalEvent; } // Ignore pinch/zoom events if(e.touches.length > 1 || e.scale && e.scale !== 1) return; var data = $container.data('swipe-transition'); if (typeof data === 'undefined') { data = {}; } data.delta_x = e.touches[0].pageX - data.start_page_x; if ( typeof data.is_scrolling === 'undefined') { data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) ); } if (!data.is_scrolling && !data.active) { e.preventDefault(); self._stop_timer($slides_container); var direction = (data.delta_x < 0) ? 'next' : 'prev'; data.active = true; self._goto($slides_container, direction, function() {}); } }) .on('touchend.fndtn.orbit', function(e) { $container.data('swipe-transition', {}); e.stopPropagation(); }); }, _init_dimensions: function ($slides_container) { var $container = $slides_container.parent(), $slides = $slides_container.children(); $slides_container.css('width', $slides.length * 100 + '%'); $slides.css('width', 100 / $slides.length + '%'); $slides_container.height($container.height()); $slides_container.css('width', $slides.length * 100 + '%'); }, _start_timer: function ($slides_container) { var self = this, $container = $slides_container.parent(); var callback = function() { self._reset_timer($slides_container, false); self._goto($slides_container, 'next', function() { self._start_timer($slides_container); }); }; var $timer = $container.find('.' + self.settings.timer_container_class), $progress = $timer.find('.' + self.settings.timer_progress_class), progress_pct = ($progress.width() / $timer.width()), delay = self.settings.timer_speed - (progress_pct * self.settings.timer_speed); $progress.animate({'width': '100%'}, delay, 'linear', callback); $slides_container.trigger('orbit:timer-started'); }, _stop_timer: function ($slides_container) { var self = this, $container = $slides_container.parent(), $timer = $container.find('.' + self.settings.timer_container_class), $progress = $timer.find('.' + self.settings.timer_progress_class), progress_pct = $progress.width() / $timer.width(); self._rebuild_timer($container, progress_pct * 100 + '%'); // $progress.stop(); $slides_container.trigger('orbit:timer-stopped'); $timer = $container.find('.' + self.settings.timer_container_class); $timer.addClass(self.settings.timer_paused_class); }, _reset_timer: function($slides_container, is_paused) { var self = this, $container = $slides_container.parent(); self._rebuild_timer($container, '0%'); if (typeof is_paused === 'boolean' && is_paused) { var $timer = $container.find('.' + self.settings.timer_container_class); $timer.addClass(self.settings.timer_paused_class); } }, _rebuild_timer: function ($container, width_pct) { // Zepto is unable to stop animations since they // are css-based. This is a workaround for that // limitation, which rebuilds the dom element // thus stopping the animation var self = this, $timer = $container.find('.' + self.settings.timer_container_class), $new_timer = $(self._timer_html()), $new_timer_progress = $new_timer.find('.' + self.settings.timer_progress_class); if (typeof Zepto === 'function') { $timer.remove(); $container.append($new_timer); $new_timer_progress.css('width', width_pct); } else if (typeof jQuery === 'function') { var $progress = $timer.find('.' + self.settings.timer_progress_class); $progress.css('width', width_pct); $progress.stop(); } }, _goto: function($slides_container, index_or_direction, callback) { var self = this, $container = $slides_container.parent(), $slides = $slides_container.children(), $active_slide = $slides_container.find('.' + self.settings.active_slide_class), active_index = $active_slide.index(), margin_position = Foundation.rtl ? 'marginRight' : 'marginLeft'; if ($container.hasClass(self.settings.orbit_transition_class)) { return false; } if (index_or_direction === 'prev') { if (active_index === 0) { active_index = $slides.length - 1; } else { active_index--; } } else if (index_or_direction === 'next') { active_index = (active_index+1) % $slides.length; } else if (typeof index_or_direction === 'number') { active_index = (index_or_direction % $slides.length); } if (active_index === ($slides.length - 1) && index_or_direction === 'next') { $slides_container.css(margin_position, '0%'); active_index = 1; } else if (active_index === 0 && index_or_direction === 'prev') { $slides_container.css(margin_position, '-' + ($slides.length - 1) * 100 + '%'); active_index = $slides.length - 2; } // Start transition, make next slide active $container.addClass(self.settings.orbit_transition_class); $active_slide.removeClass(self.settings.active_slide_class); $($slides[active_index]).addClass(self.settings.active_slide_class); // Make next bullet active var $bullets = $container.siblings('.' + self.settings.bullets_container_class); if ($bullets.length === 1) { $bullets.children().removeClass(self.settings.bullets_active_class); $($bullets.children()[active_index-1]).addClass(self.settings.bullets_active_class); } var new_margin_left = '-' + (active_index * 100) + '%'; // Check to see if animation will occur, otherwise perform // callbacks manually $slides_container.trigger('orbit:before-slide-change'); if ($slides_container.css(margin_position) === new_margin_left) { $container.removeClass(self.settings.orbit_transition_class); $slides_container.trigger('orbit:after-slide-change', [{slide_number: active_index, total_slides: $slides_container.children().length - 2}]); callback(); } else { var properties = {}; properties[margin_position] = new_margin_left; $slides_container.animate(properties, self.settings.animation_speed, 'linear', function() { $container.removeClass(self.settings.orbit_transition_class); $slides_container.trigger('orbit:after-slide-change', [{slide_number: active_index, total_slides: $slides_container.children().length - 2}]); callback(); }); } } }; }(Foundation.zj, this, this.document)); automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.placeholder.js000066400000000000000000000116001217034403100304700ustar00rootroot00000000000000/*! http://mths.be/placeholder v2.0.7 by @mathias Modified to work with Zepto.js by ZURB */ ;(function(window, document, $) { var isInputSupported = 'placeholder' in document.createElement('input'), isTextareaSupported = 'placeholder' in document.createElement('textarea'), prototype = $.fn, valHooks = $.valHooks, hooks, placeholder; if (isInputSupported && isTextareaSupported) { placeholder = prototype.placeholder = function() { return this; }; placeholder.input = placeholder.textarea = true; } else { placeholder = prototype.placeholder = function() { var $this = this; $this .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]') .not('.placeholder') .bind({ 'focus.placeholder': clearPlaceholder, 'blur.placeholder': setPlaceholder }) .data('placeholder-enabled', true) .trigger('blur.placeholder'); return $this; }; placeholder.input = isInputSupported; placeholder.textarea = isTextareaSupported; hooks = { 'get': function(element) { var $element = $(element); return $element.data('placeholder-enabled') && $element.hasClass('placeholder') ? '' : element.value; }, 'set': function(element, value) { var $element = $(element); if (!$element.data('placeholder-enabled')) { return element.value = value; } if (value == '') { element.value = value; // Issue #56: Setting the placeholder causes problems if the element continues to have focus. if (element != document.activeElement) { // We can't use `triggerHandler` here because of dummy text/password inputs :( setPlaceholder.call(element); } } else if ($element.hasClass('placeholder')) { clearPlaceholder.call(element, true, value) || (element.value = value); } else { element.value = value; } // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363 return $element; } }; isInputSupported || (valHooks.input = hooks); isTextareaSupported || (valHooks.textarea = hooks); $(function() { // Look for forms $(document).delegate('form', 'submit.placeholder', function() { // Clear the placeholder values so they don't get submitted var $inputs = $('.placeholder', this).each(clearPlaceholder); setTimeout(function() { $inputs.each(setPlaceholder); }, 10); }); }); // Clear placeholder values upon page reload $(window).bind('beforeunload.placeholder', function() { $('.placeholder').each(function() { this.value = ''; }); }); } function args(elem) { // Return an object of element attributes var newAttrs = {}, rinlinejQuery = /^jQuery\d+$/; $.each(elem.attributes, function(i, attr) { if (attr.specified && !rinlinejQuery.test(attr.name)) { newAttrs[attr.name] = attr.value; } }); return newAttrs; } function clearPlaceholder(event, value) { var input = this, $input = $(input); if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) { if ($input.data('placeholder-password')) { $input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id')); // If `clearPlaceholder` was called from `$.valHooks.input.set` if (event === true) { return $input[0].value = value; } $input.focus(); } else { input.value = ''; $input.removeClass('placeholder'); input == document.activeElement && input.select(); } } } function setPlaceholder() { var $replacement, input = this, $input = $(input), $origInput = $input, id = this.id; if (input.value == '') { if (input.type == 'password') { if (!$input.data('placeholder-textinput')) { try { $replacement = $input.clone().attr({ 'type': 'text' }); } catch(e) { $replacement = $('').attr($.extend(args(this), { 'type': 'text' })); } $replacement .removeAttr('name') .data({ 'placeholder-password': true, 'placeholder-id': id }) .bind('focus.placeholder', clearPlaceholder); $input .data({ 'placeholder-textinput': $replacement, 'placeholder-id': id }) .before($replacement); } $input = $input.removeAttr('id').hide().prev().attr('id', id).show(); // Note: `$input[0] != input` now! } $input.addClass('placeholder'); $input[0].value = $input.attr('placeholder'); } else { $input.removeClass('placeholder'); } } }(this, document, Foundation.zj)); ;(function ($, window, document, undefined) { 'use strict'; Foundation.libs.placeholder = { name : 'placeholder', version : '4.2.2', init : function (scope, method, options) { this.scope = scope || this.scope; if (typeof method !== 'string') { window.onload = function () { $('input, textarea').placeholder(); } } } }; }(Foundation.zj, this, this.document));automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.reveal.js000066400000000000000000000226451217034403100274770ustar00rootroot00000000000000/*jslint unparam: true, browser: true, indent: 2 */ ;(function ($, window, document, undefined) { 'use strict'; Foundation.libs.reveal = { name: 'reveal', version : '4.2.2', locked : false, settings : { animation: 'fadeAndPop', animationSpeed: 250, closeOnBackgroundClick: true, closeOnEsc: true, dismissModalClass: 'close-reveal-modal', bgClass: 'reveal-modal-bg', open: function(){}, opened: function(){}, close: function(){}, closed: function(){}, bg : $('.reveal-modal-bg'), css : { open : { 'opacity': 0, 'visibility': 'visible', 'display' : 'block' }, close : { 'opacity': 1, 'visibility': 'hidden', 'display': 'none' } } }, init : function (scope, method, options) { Foundation.inherit(this, 'data_options delay'); if (typeof method === 'object') { $.extend(true, this.settings, method); } else if (typeof options !== 'undefined') { $.extend(true, this.settings, options); } if (typeof method !== 'string') { this.events(); return this.settings.init; } else { return this[method].call(this, options); } }, events : function () { var self = this; $(this.scope) .off('.fndtn.reveal') .on('click.fndtn.reveal', '[data-reveal-id]', function (e) { e.preventDefault(); if (!self.locked) { var element = $(this), ajax = element.data('reveal-ajax'); self.locked = true; if (typeof ajax === 'undefined') { self.open.call(self, element); } else { var url = ajax === true ? element.attr('href') : ajax; self.open.call(self, element, {url: url}); } } }) .on('click.fndtn.reveal', this.close_targets(), function (e) { e.preventDefault(); if (!self.locked) { var settings = $.extend({}, self.settings, self.data_options($('.reveal-modal.open'))); if ($(e.target)[0] === $('.' + settings.bgClass)[0] && !settings.closeOnBackgroundClick) { return; } self.locked = true; self.close.call(self, $(this).closest('.reveal-modal')); } }) .on('open.fndtn.reveal', '.reveal-modal', this.settings.open) .on('opened.fndtn.reveal', '.reveal-modal', this.settings.opened) .on('opened.fndtn.reveal', '.reveal-modal', this.open_video) .on('close.fndtn.reveal', '.reveal-modal', this.settings.close) .on('closed.fndtn.reveal', '.reveal-modal', this.settings.closed) .on('closed.fndtn.reveal', '.reveal-modal', this.close_video); $( 'body' ).bind( 'keyup.reveal', function ( event ) { var open_modal = $('.reveal-modal.open'), settings = $.extend({}, self.settings, self.data_options(open_modal)); if ( event.which === 27 && settings.closeOnEsc) { // 27 is the keycode for the Escape key open_modal.foundation('reveal', 'close'); } }); return true; }, open : function (target, ajax_settings) { if (target) { if (typeof target.selector !== 'undefined') { var modal = $('#' + target.data('reveal-id')); } else { var modal = $(this.scope); ajax_settings = target; } } else { var modal = $(this.scope); } if (!modal.hasClass('open')) { var open_modal = $('.reveal-modal.open'); if (typeof modal.data('css-top') === 'undefined') { modal.data('css-top', parseInt(modal.css('top'), 10)) .data('offset', this.cache_offset(modal)); } modal.trigger('open'); if (open_modal.length < 1) { this.toggle_bg(modal); } if (typeof ajax_settings === 'undefined' || !ajax_settings.url) { this.hide(open_modal, this.settings.css.close); this.show(modal, this.settings.css.open); } else { var self = this, old_success = typeof ajax_settings.success !== 'undefined' ? ajax_settings.success : null; $.extend(ajax_settings, { success: function (data, textStatus, jqXHR) { if ( $.isFunction(old_success) ) { old_success(data, textStatus, jqXHR); } modal.html(data); $(modal).foundation('section', 'reflow'); self.hide(open_modal, self.settings.css.close); self.show(modal, self.settings.css.open); } }); $.ajax(ajax_settings); } } }, close : function (modal) { var modal = modal && modal.length ? modal : $(this.scope), open_modals = $('.reveal-modal.open'); if (open_modals.length > 0) { this.locked = true; modal.trigger('close'); this.toggle_bg(modal); this.hide(open_modals, this.settings.css.close); } }, close_targets : function () { var base = '.' + this.settings.dismissModalClass; if (this.settings.closeOnBackgroundClick) { return base + ', .' + this.settings.bgClass; } return base; }, toggle_bg : function (modal) { if ($('.reveal-modal-bg').length === 0) { this.settings.bg = $('
      ', {'class': this.settings.bgClass}) .appendTo('body'); } if (this.settings.bg.filter(':visible').length > 0) { this.hide(this.settings.bg); } else { this.show(this.settings.bg); } }, show : function (el, css) { // is modal if (css) { if (/pop/i.test(this.settings.animation)) { css.top = $(window).scrollTop() - el.data('offset') + 'px'; var end_css = { top: $(window).scrollTop() + el.data('css-top') + 'px', opacity: 1 }; return this.delay(function () { return el .css(css) .animate(end_css, this.settings.animationSpeed, 'linear', function () { this.locked = false; el.trigger('opened'); }.bind(this)) .addClass('open'); }.bind(this), this.settings.animationSpeed / 2); } if (/fade/i.test(this.settings.animation)) { var end_css = {opacity: 1}; return this.delay(function () { return el .css(css) .animate(end_css, this.settings.animationSpeed, 'linear', function () { this.locked = false; el.trigger('opened'); }.bind(this)) .addClass('open'); }.bind(this), this.settings.animationSpeed / 2); } return el.css(css).show().css({opacity: 1}).addClass('open').trigger('opened'); } // should we animate the background? if (/fade/i.test(this.settings.animation)) { return el.fadeIn(this.settings.animationSpeed / 2); } return el.show(); }, hide : function (el, css) { // is modal if (css) { if (/pop/i.test(this.settings.animation)) { var end_css = { top: - $(window).scrollTop() - el.data('offset') + 'px', opacity: 0 }; return this.delay(function () { return el .animate(end_css, this.settings.animationSpeed, 'linear', function () { this.locked = false; el.css(css).trigger('closed'); }.bind(this)) .removeClass('open'); }.bind(this), this.settings.animationSpeed / 2); } if (/fade/i.test(this.settings.animation)) { var end_css = {opacity: 0}; return this.delay(function () { return el .animate(end_css, this.settings.animationSpeed, 'linear', function () { this.locked = false; el.css(css).trigger('closed'); }.bind(this)) .removeClass('open'); }.bind(this), this.settings.animationSpeed / 2); } return el.hide().css(css).removeClass('open').trigger('closed'); } // should we animate the background? if (/fade/i.test(this.settings.animation)) { return el.fadeOut(this.settings.animationSpeed / 2); } return el.hide(); }, close_video : function (e) { var video = $(this).find('.flex-video'), iframe = video.find('iframe'); if (iframe.length > 0) { iframe.attr('data-src', iframe[0].src); iframe.attr('src', 'about:blank'); video.hide(); } }, open_video : function (e) { var video = $(this).find('.flex-video'), iframe = video.find('iframe'); if (iframe.length > 0) { var data_src = iframe.attr('data-src'); if (typeof data_src === 'string') { iframe[0].src = iframe.attr('data-src'); } else { var src = iframe[0].src; iframe[0].src = undefined; iframe[0].src = src; } video.show(); } }, cache_offset : function (modal) { var offset = modal.show().height() + parseInt(modal.css('top'), 10); modal.hide(); return offset; }, off : function () { $(this.scope).off('.fndtn.reveal'); }, reflow : function () {} }; }(Foundation.zj, this, this.document));automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.section.js000066400000000000000000000321421217034403100276560ustar00rootroot00000000000000/*jslint unparam: true, browser: true, indent: 2 */ ;(function ($, window, document, undefined) { 'use strict'; Foundation.libs.section = { name: 'section', version : '4.2.3', settings : { deep_linking: false, small_breakpoint: 768, one_up: true, section_selector : '[data-section]', region_selector : 'section, .section, [data-section-region]', title_selector : '.title, [data-section-title]', active_region_selector : 'section.active, .section.active, .active[data-section-region]', content_selector : '.content, [data-section-content]', nav_selector : '[data-section="vertical-nav"], [data-section="horizontal-nav"]', callback: function (){} }, init : function (scope, method, options) { var self = this; Foundation.inherit(this, 'throttle data_options position_right offset_right'); if (typeof method === 'object') { $.extend(true, self.settings, method); } if (typeof method !== 'string') { this.set_active_from_hash(); this.events(); return true; } else { return this[method].call(this, options); } }, events : function () { var self = this; $(this.scope) .on('click.fndtn.section', '[data-section] .title, [data-section] [data-section-title]', function (e) { var $this = $(this), section = $this.closest(self.settings.region_selector); if (section.children(self.settings.content_selector).length > 0) { self.toggle_active.call(this, e, self); self.reflow(); } }); $(window) .on('resize.fndtn.section', self.throttle(function () { self.resize.call(this); }, 30)) .on('hashchange', function () { if (!self.settings.toggled){ self.set_active_from_hash(); $(this).trigger('resize'); } }).trigger('resize'); $(document) .on('click.fndtn.section', function (e) { if ($(e.target).closest(self.settings.title_selector).length < 1) { $(self.settings.nav_selector) .children(self.settings.region_selector) .removeClass('active') .attr('style', ''); } }); }, toggle_active : function (e, self) { var $this = $(this), self = Foundation.libs.section, region = $this.closest(self.settings.region_selector), content = $this.siblings(self.settings.content_selector), parent = region.parent(), settings = $.extend({}, self.settings, self.data_options(parent)), prev_active_section = parent .children(self.settings.active_region_selector); self.settings.toggled = true; if (!settings.deep_linking && content.length > 0) { e.preventDefault(); } if (region.hasClass('active')) { // this is causing the style flash. if (self.small(parent) || self.is_vertical_nav(parent) || self.is_horizontal_nav(parent) || self.is_accordion(parent)) { if (prev_active_section[0] !== region[0] || (prev_active_section[0] === region[0] && !settings.one_up)) { region .removeClass('active') .attr('style', ''); } } } else { var prev_active_section = parent .children(self.settings.active_region_selector), title_height = self.outerHeight(region .children(self.settings.title_selector)); if (self.small(parent) || settings.one_up) { if (self.small(parent)) { prev_active_section.attr('style', ''); } else { prev_active_section.attr('style', 'visibility: hidden; padding-top: '+title_height+'px;'); } } if (self.small(parent)) { region.attr('style', ''); } else { region.css('padding-top', title_height); } region.addClass('active'); if (prev_active_section.length > 0) { prev_active_section .removeClass('active') .attr('style', ''); } // Toggle the content display attribute. This is done to // ensure accurate outerWidth measurements that account for // the scrollbar. if (self.is_vertical_tabs(parent)) { content.css('display', 'block'); if (prev_active_section !== null) { prev_active_section .children(self.settings.content_selector) .css('display', 'none'); } } } setTimeout(function () { self.settings.toggled = false; }, 300); settings.callback(); }, resize : function () { var self = Foundation.libs.section, sections = $(self.settings.section_selector); sections.each(function() { var $this = $(this), active_section = $this .children(self.settings.active_region_selector), settings = $.extend({}, self.settings, self.data_options($this)); if (active_section.length > 1) { active_section .not(':first') .removeClass('active') .attr('style', ''); } else if (active_section.length < 1 && !self.is_vertical_nav($this) && !self.is_horizontal_nav($this) && !self.is_accordion($this)) { var first = $this.children(self.settings.region_selector).first(); if (settings.one_up || !self.small($this)) { first.addClass('active'); } if (self.small($this)) { first.attr('style', ''); } else { first.css('padding-top', self.outerHeight(first .children(self.settings.title_selector))); } } if (self.small($this)) { active_section.attr('style', ''); } else { active_section.css('padding-top', self.outerHeight(active_section .children(self.settings.title_selector))); } self.position_titles($this); if ( (self.is_horizontal_nav($this) && !self.small($this)) || self.is_vertical_tabs($this) && !self.small($this)) { self.position_content($this); } else { self.position_content($this, false); } }); }, is_vertical_nav : function (el) { return /vertical-nav/i.test(el.data('section')); }, is_horizontal_nav : function (el) { return /horizontal-nav/i.test(el.data('section')); }, is_accordion : function (el) { return /accordion/i.test(el.data('section')); }, is_horizontal_tabs : function (el) { return /^tabs$/i.test(el.data('section')); }, is_vertical_tabs : function (el) { return /vertical-tabs/i.test(el.data('section')); }, set_active_from_hash : function () { var hash = window.location.hash.substring(1), sections = $('[data-section]'), self = this; sections.each(function () { var section = $(this), settings = $.extend({}, self.settings, self.data_options(section)); if (hash.length > 0 && settings.deep_linking) { var regions = section .children(self.settings.region_selector) .attr('style', '') .removeClass('active'); var hash_regions = regions.map(function () { var content = $(self.settings.content_selector, this), content_slug = content.data('slug'); if (new RegExp(content_slug, 'i').test(hash)) return content; }); var count = hash_regions.length; for (var i = count - 1; i >= 0; i--) { $(hash_regions[i]).parent().addClass('active'); } } }); }, position_titles : function (section, off) { var self = this, titles = section .children(this.settings.region_selector) .map(function () { return $(this).children(self.settings.title_selector); }), previous_width = 0, previous_height = 0, self = this; if (typeof off === 'boolean') { titles.attr('style', ''); } else { titles.each(function () { if (self.is_vertical_tabs(section)) { $(this).css('top', previous_height); previous_height += self.outerHeight($(this)); } else { if (!self.rtl) { $(this).css('left', previous_width); } else { $(this).css('right', previous_width); } previous_width += self.outerWidth($(this)); } }); } }, position_content : function (section, off) { var self = this, regions = section.children(self.settings.region_selector), titles = regions .map(function () { return $(this).children(self.settings.title_selector); }), content = regions .map(function () { return $(this).children(self.settings.content_selector); }); if (typeof off === 'boolean') { content.attr('style', ''); section.attr('style', ''); // Reset the minHeight and maxWidth values (only applicable to // vertical tabs) content.css('minHeight', ''); content.css('maxWidth', ''); } else { if (self.is_vertical_tabs(section) && !self.small(section)) { var content_min_height = 0, content_min_width = Number.MAX_VALUE, title_width = null; regions.each(function () { var region = $(this), title = region.children(self.settings.title_selector), content = region.children(self.settings.content_selector), content_width = 0; title_width = self.outerWidth(title); content_width = self.outerWidth(section) - title_width; if (content_width < content_min_width) { content_min_width = content_width; } // Increment the minimum height of the content region // to align with the height of the titles. content_min_height += self.outerHeight(title); // Set all of the inactive tabs to 'display: none' // The CSS sets all of the tabs as 'display: block' // in order to account for scrollbars when measuring the width // of the content regions. if (!$(this).hasClass('active')) { content.css('display', 'none'); } }); regions.each(function () { var content = $(this).children(self.settings.content_selector); content.css('minHeight', content_min_height); // Remove 2 pixels to account for the right-shift in the CSS content.css('maxWidth', content_min_width - 2); }); } else { regions.each(function () { var region = $(this), title = region.children(self.settings.title_selector), content = region.children(self.settings.content_selector); if (!self.rtl) { content .css({left: title.position().left - 1, top: self.outerHeight(title) - 2}); } else { content .css({right: self.position_right(title) + 1, top: self.outerHeight(title) - 2}); } }); // temporary work around for Zepto outerheight calculation issues. if (typeof Zepto === 'function') { section.height(this.outerHeight($(titles[0]))); } else { section.height(this.outerHeight($(titles[0])) - 2); } } } }, position_right : function (el) { var self = this, section = el.closest(this.settings.section_selector), regions = section.children(this.settings.region_selector), section_width = el.closest(this.settings.section_selector).width(), offset = regions .map(function () { return $(this).children(self.settings.title_selector); }).length; return (section_width - el.position().left - el.width() * (el.index() + 1) - offset); }, reflow : function (scope) { var scope = scope || document; $(this.settings.section_selector, scope).trigger('resize'); }, small : function (el) { var settings = $.extend({}, this.settings, this.data_options(el)); if (this.is_horizontal_tabs(el)) { return false; } if (el && this.is_accordion(el)) { return true; } if ($('html').hasClass('lt-ie9')) { return true; } if ($('html').hasClass('ie8compat')) { return true; } return $(this.scope).width() < settings.small_breakpoint; }, off : function () { $(this.scope).off('.fndtn.section'); $(window).off('.fndtn.section'); $(document).off('.fndtn.section') } }; }(Foundation.zj, this, this.document)); automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.tooltips.js000066400000000000000000000160141217034403100300670ustar00rootroot00000000000000/*jslint unparam: true, browser: true, indent: 2 */ ;(function ($, window, document, undefined) { 'use strict'; Foundation.libs.tooltips = { name: 'tooltips', version : '4.2.2', settings : { selector : '.has-tip', additionalInheritableClasses : [], tooltipClass : '.tooltip', appendTo: 'body', 'disable-for-touch': false, tipTemplate : function (selector, content) { return '' + content + ''; } }, cache : {}, init : function (scope, method, options) { Foundation.inherit(this, 'data_options'); var self = this; if (typeof method === 'object') { $.extend(true, this.settings, method); } else if (typeof options !== 'undefined') { $.extend(true, this.settings, options); } if (typeof method !== 'string') { if (Modernizr.touch) { $(this.scope) .on('click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip', '[data-tooltip]', function (e) { var settings = $.extend({}, self.settings, self.data_options($(this))); if (!settings['disable-for-touch']) { e.preventDefault(); $(settings.tooltipClass).hide(); self.showOrCreateTip($(this)); } }) .on('click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip', this.settings.tooltipClass, function (e) { e.preventDefault(); $(this).fadeOut(150); }); } else { $(this.scope) .on('mouseenter.fndtn.tooltip mouseleave.fndtn.tooltip', '[data-tooltip]', function (e) { var $this = $(this); if (/enter|over/i.test(e.type)) { self.showOrCreateTip($this); } else if (e.type === 'mouseout' || e.type === 'mouseleave') { self.hide($this); } }); } // $(this.scope).data('fndtn-tooltips', true); } else { return this[method].call(this, options); } }, showOrCreateTip : function ($target) { var $tip = this.getTip($target); if ($tip && $tip.length > 0) { return this.show($target); } return this.create($target); }, getTip : function ($target) { var selector = this.selector($target), tip = null; if (selector) { tip = $('span[data-selector="' + selector + '"]' + this.settings.tooltipClass); } return (typeof tip === 'object') ? tip : false; }, selector : function ($target) { var id = $target.attr('id'), dataSelector = $target.attr('data-tooltip') || $target.attr('data-selector'); if ((id && id.length < 1 || !id) && typeof dataSelector != 'string') { dataSelector = 'tooltip' + Math.random().toString(36).substring(7); $target.attr('data-selector', dataSelector); } return (id && id.length > 0) ? id : dataSelector; }, create : function ($target) { var $tip = $(this.settings.tipTemplate(this.selector($target), $('
      ').html($target.attr('title')).html())), classes = this.inheritable_classes($target); $tip.addClass(classes).appendTo(this.settings.appendTo); if (Modernizr.touch) { $tip.append('tap to close '); } $target.removeAttr('title').attr('title',''); this.show($target); }, reposition : function (target, tip, classes) { var width, nub, nubHeight, nubWidth, column, objPos; tip.css('visibility', 'hidden').show(); width = target.data('width'); nub = tip.children('.nub'); nubHeight = this.outerHeight(nub); nubWidth = this.outerHeight(nub); objPos = function (obj, top, right, bottom, left, width) { return obj.css({ 'top' : (top) ? top : 'auto', 'bottom' : (bottom) ? bottom : 'auto', 'left' : (left) ? left : 'auto', 'right' : (right) ? right : 'auto', 'width' : (width) ? width : 'auto' }).end(); }; objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', target.offset().left, width); if ($(window).width() < 767) { objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', 12.5, $(this.scope).width()); tip.addClass('tip-override'); objPos(nub, -nubHeight, 'auto', 'auto', target.offset().left); } else { var left = target.offset().left; if (Foundation.rtl) { left = target.offset().left + target.offset().width - this.outerWidth(tip); } objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', left, width); tip.removeClass('tip-override'); if (classes && classes.indexOf('tip-top') > -1) { objPos(tip, (target.offset().top - this.outerHeight(tip)), 'auto', 'auto', left, width) .removeClass('tip-override'); } else if (classes && classes.indexOf('tip-left') > -1) { objPos(tip, (target.offset().top + (this.outerHeight(target) / 2) - nubHeight*2.5), 'auto', 'auto', (target.offset().left - this.outerWidth(tip) - nubHeight), width) .removeClass('tip-override'); } else if (classes && classes.indexOf('tip-right') > -1) { objPos(tip, (target.offset().top + (this.outerHeight(target) / 2) - nubHeight*2.5), 'auto', 'auto', (target.offset().left + this.outerWidth(target) + nubHeight), width) .removeClass('tip-override'); } } tip.css('visibility', 'visible').hide(); }, inheritable_classes : function (target) { var inheritables = ['tip-top', 'tip-left', 'tip-bottom', 'tip-right', 'noradius'].concat(this.settings.additionalInheritableClasses), classes = target.attr('class'), filtered = classes ? $.map(classes.split(' '), function (el, i) { if ($.inArray(el, inheritables) !== -1) { return el; } }).join(' ') : ''; return $.trim(filtered); }, show : function ($target) { var $tip = this.getTip($target); this.reposition($target, $tip, $target.attr('class')); $tip.fadeIn(150); }, hide : function ($target) { var $tip = this.getTip($target); $tip.fadeOut(150); }, // deprecate reload reload : function () { var $self = $(this); return ($self.data('fndtn-tooltips')) ? $self.foundationTooltips('destroy').foundationTooltips('init') : $self.foundationTooltips('init'); }, off : function () { $(this.scope).off('.fndtn.tooltip'); $(this.settings.tooltipClass).each(function (i) { $('[data-tooltip]').get(i).attr('title', $(this).text()); }).remove(); }, reflow : function () {} }; }(Foundation.zj, this, this.document)); automx-0.10.0/src/foundation-scss/javascripts/foundation/foundation.topbar.js000066400000000000000000000232071217034403100275030ustar00rootroot00000000000000/*jslint unparam: true, browser: true, indent: 2 */ ;(function ($, window, document, undefined) { 'use strict'; Foundation.libs.topbar = { name : 'topbar', version : '4.2.3', settings : { index : 0, stickyClass : 'sticky', custom_back_text: true, back_text: 'Back', is_hover: true, scrolltop : true, // jump to top when sticky nav menu toggle is clicked init : false }, init : function (section, method, options) { Foundation.inherit(this, 'data_options'); var self = this; if (typeof method === 'object') { $.extend(true, this.settings, method); } else if (typeof options !== 'undefined') { $.extend(true, this.settings, options); } if (typeof method !== 'string') { $('.top-bar, [data-topbar]').each(function () { $.extend(true, self.settings, self.data_options($(this))); self.settings.$w = $(window); self.settings.$topbar = $(this); self.settings.$section = self.settings.$topbar.find('section'); self.settings.$titlebar = self.settings.$topbar.children('ul').first(); self.settings.$topbar.data('index', 0); var breakpoint = $("
      ").insertAfter(self.settings.$topbar); self.settings.breakPoint = breakpoint.width(); breakpoint.remove(); self.assemble(); if (self.settings.$topbar.parent().hasClass('fixed')) { $('body').css('padding-top', self.outerHeight(self.settings.$topbar)); } }); if (!self.settings.init) { this.events(); } return this.settings.init; } else { // fire method return this[method].call(this, options); } }, events : function () { var self = this; var offst = this.outerHeight($('.top-bar, [data-topbar]')); $(this.scope) .off('.fndtn.topbar') .on('click.fndtn.topbar', '.top-bar .toggle-topbar, [data-topbar] .toggle-topbar', function (e) { var topbar = $(this).closest('.top-bar, [data-topbar]'), section = topbar.find('section, .section'), titlebar = topbar.children('ul').first(); e.preventDefault(); if (self.breakpoint()) { if (!self.rtl) { section.css({left: '0%'}); section.find('>.name').css({left: '100%'}); } else { section.css({right: '0%'}); section.find('>.name').css({right: '100%'}); } section.find('li.moved').removeClass('moved'); topbar.data('index', 0); topbar .toggleClass('expanded') .css('height', ''); } if (!topbar.hasClass('expanded')) { if (topbar.hasClass('fixed')) { topbar.parent().addClass('fixed'); topbar.removeClass('fixed'); $('body').css('padding-top',offst); } } else if (topbar.parent().hasClass('fixed')) { topbar.parent().removeClass('fixed'); topbar.addClass('fixed'); $('body').css('padding-top','0'); if (self.settings.scrolltop) { window.scrollTo(0,0); } } }) .on('mouseenter mouseleave', '.top-bar li', function (e) { if (!self.settings.is_hover) return; if (/enter|over/i.test(e.type)) { $(this).addClass('hover'); } else { $(this).removeClass('hover'); } }) .on('click.fndtn.topbar', '.top-bar li.has-dropdown', function (e) { if (self.breakpoint()) return; var li = $(this), target = $(e.target), topbar = li.closest('[data-topbar], .top-bar'), is_hover = topbar.data('topbar'); if (self.settings.is_hover && !Modernizr.touch) return; e.stopImmediatePropagation(); if (target[0].nodeName === 'A' && target.parent().hasClass('has-dropdown')) { e.preventDefault(); } if (li.hasClass('hover')) { li .removeClass('hover') .find('li') .removeClass('hover'); } else { li.addClass('hover'); } }) .on('click.fndtn.topbar', '.top-bar .has-dropdown>a, [data-topbar] .has-dropdown>a', function (e) { if (self.breakpoint()) { e.preventDefault(); var $this = $(this), topbar = $this.closest('.top-bar, [data-topbar]'), section = topbar.find('section, .section'), titlebar = topbar.children('ul').first(), dropdownHeight = $this.next('.dropdown').outerHeight(), $selectedLi = $this.closest('li'); topbar.data('index', topbar.data('index') + 1); $selectedLi.addClass('moved'); if (!self.rtl) { section.css({left: -(100 * topbar.data('index')) + '%'}); section.find('>.name').css({left: 100 * topbar.data('index') + '%'}); } else { section.css({right: -(100 * topbar.data('index')) + '%'}); section.find('>.name').css({right: 100 * topbar.data('index') + '%'}); } topbar.css('height', self.outerHeight($this.siblings('ul'), true) + self.outerHeight(titlebar, true)); } }); $(window).on('resize.fndtn.topbar', function () { if (!self.breakpoint()) { $('.top-bar, [data-topbar]') .css('height', '') .removeClass('expanded') .find('li') .removeClass('hover'); } }.bind(this)); $('body').on('click.fndtn.topbar', function (e) { var parent = $(e.target).closest('[data-topbar], .top-bar'); if (parent.length > 0) { return; } $('.top-bar li, [data-topbar] li').removeClass('hover'); }); // Go up a level on Click $(this.scope).on('click.fndtn', '.top-bar .has-dropdown .back, [data-topbar] .has-dropdown .back', function (e) { e.preventDefault(); var $this = $(this), topbar = $this.closest('.top-bar, [data-topbar]'), titlebar = topbar.children('ul').first(), section = topbar.find('section, .section'), $movedLi = $this.closest('li.moved'), $previousLevelUl = $movedLi.parent(); topbar.data('index', topbar.data('index') - 1); if (!self.rtl) { section.css({left: -(100 * topbar.data('index')) + '%'}); section.find('>.name').css({left: 100 * topbar.data('index') + '%'}); } else { section.css({right: -(100 * topbar.data('index')) + '%'}); section.find('>.name').css({right: 100 * topbar.data('index') + '%'}); } if (topbar.data('index') === 0) { topbar.css('height', ''); } else { topbar.css('height', self.outerHeight($previousLevelUl, true) + self.outerHeight(titlebar, true)); } setTimeout(function () { $movedLi.removeClass('moved'); }, 300); }); }, breakpoint : function () { return $(document).width() <= this.settings.breakPoint || $('html').hasClass('lt-ie9'); }, assemble : function () { var self = this; // Pull element out of the DOM for manipulation this.settings.$section.detach(); this.settings.$section.find('.has-dropdown>a').each(function () { var $link = $(this), $dropdown = $link.siblings('.dropdown'), url = $link.attr('href'); if (url && url.length > 1) { var $titleLi = $('
    2. ' + $link.text() +'
    3. '); } else { var $titleLi = $('
    4. '); } // Copy link to subnav if (self.settings.custom_back_text == true) { $titleLi.find('h5>a').html('« ' + self.settings.back_text); } else { $titleLi.find('h5>a').html('« ' + $link.html()); } $dropdown.prepend($titleLi); }); // Put element back in the DOM this.settings.$section.appendTo(this.settings.$topbar); // check for sticky this.sticky(); }, height : function (ul) { var total = 0, self = this; ul.find('> li').each(function () { total += self.outerHeight($(this), true); }); return total; }, sticky : function () { var klass = '.' + this.settings.stickyClass; if ($(klass).length > 0) { var distance = $(klass).length ? $(klass).offset().top: 0, $window = $(window); var offst = this.outerHeight($('.top-bar')); //Whe resize elements of the page on windows resize. Must recalculate distance $(window).resize(function() { clearTimeout(t_top); t_top = setTimeout (function() { distance = $(klass).offset().top; },105); }); $window.scroll(function() { if ($window.scrollTop() > (distance)) { $(klass).addClass("fixed"); $('body').css('padding-top',offst); } else if ($window.scrollTop() <= distance) { $(klass).removeClass("fixed"); $('body').css('padding-top','0'); } }); } }, off : function () { $(this.scope).off('.fndtn.topbar'); $(window).off('.fndtn.topbar'); }, reflow : function () {} }; }(Foundation.zj, this, this.document)); automx-0.10.0/src/foundation-scss/javascripts/vendor/000077500000000000000000000000001217034403100226345ustar00rootroot00000000000000automx-0.10.0/src/foundation-scss/javascripts/vendor/custom.modernizr.js000066400000000000000000000221071217034403100265160ustar00rootroot00000000000000/* Modernizr 2.6.2 (Custom Build) | MIT & BSD * Build: http://modernizr.com/download/#-inlinesvg-svg-svgclippaths-touch-shiv-mq-cssclasses-teststyles-prefixes-ie8compat-load */ ;window.Modernizr=function(a,b,c){function y(a){j.cssText=a}function z(a,b){return y(m.join(a+";")+(b||""))}function A(a,b){return typeof a===b}function B(a,b){return!!~(""+a).indexOf(b)}function C(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:A(f,"function")?f.bind(d||b):f}return!1}var d="2.6.2",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n={svg:"http://www.w3.org/2000/svg"},o={},p={},q={},r=[],s=r.slice,t,u=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["­",'"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},v=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return u("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},w={}.hasOwnProperty,x;!A(w,"undefined")&&!A(w.call,"undefined")?x=function(a,b){return w.call(a,b)}:x=function(a,b){return b in a&&A(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=s.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(s.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(s.call(arguments)))};return e}),o.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:u(["@media (",m.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},o.svg=function(){return!!b.createElementNS&&!!b.createElementNS(n.svg,"svg").createSVGRect},o.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="",(a.firstChild&&a.firstChild.namespaceURI)==n.svg},o.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(l.call(b.createElementNS(n.svg,"clipPath")))};for(var D in o)x(o,D)&&(t=D.toLowerCase(),e[t]=o[D](),r.push((e[t]?"":"no-")+t));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)x(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},y(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=m,e.mq=v,e.testStyles=u,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+r.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f type pairs class2type = {}, // List of deleted data cache ids, so we can reuse them core_deletedIds = [], core_version = "1.10.1", // Save a reference to some core methods core_concat = core_deletedIds.concat, core_push = core_deletedIds.push, core_slice = core_deletedIds.slice, core_indexOf = core_deletedIds.indexOf, core_toString = class2type.toString, core_hasOwn = class2type.hasOwnProperty, core_trim = core_version.trim, // Define a local copy of jQuery jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context, rootjQuery ); }, // Used for matching numbers core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, // Used for splitting on whitespace core_rnotwhite = /\S+/g, // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, // A simple way to check for HTML strings // Prioritize #id over to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, // Match a standalone tag rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, // JSON RegExp rvalidchars = /^[\],:{}\s]*$/, rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, // Matches dashed string for camelizing rmsPrefix = /^-ms-/, rdashAlpha = /-([\da-z])/gi, // Used by jQuery.camelCase as callback to replace() fcamelCase = function( all, letter ) { return letter.toUpperCase(); }, // The ready event handler completed = function( event ) { // readyState === "complete" is good enough for us to call the dom ready in oldIE if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { detach(); jQuery.ready(); } }, // Clean-up method for dom ready events detach = function() { if ( document.addEventListener ) { document.removeEventListener( "DOMContentLoaded", completed, false ); window.removeEventListener( "load", completed, false ); } else { document.detachEvent( "onreadystatechange", completed ); window.detachEvent( "onload", completed ); } }; jQuery.fn = jQuery.prototype = { // The current version of jQuery being used jquery: core_version, constructor: jQuery, init: function( selector, context, rootjQuery ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } // Handle HTML strings if ( typeof selector === "string" ) { if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; // scripts is true for back-compat jQuery.merge( this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { // Properties of context are called as methods if possible if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById( match[2] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id !== match[2] ) { return rootjQuery.find( selector ); } // Otherwise, we inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; } // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return ( context || rootjQuery ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { this.context = this[0] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } if ( selector.selector !== undefined ) { this.selector = selector.selector; this.context = selector.context; } return jQuery.makeArray( selector, this ); }, // Start with an empty selector selector: "", // The default length of a jQuery object is 0 length: 0, toArray: function() { return core_slice.call( this ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { return num == null ? // Return a 'clean' array this.toArray() : // Return just the object ( num < 0 ? this[ this.length + num ] : this[ num ] ); }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems ) { // Build a new jQuery matched element set var ret = jQuery.merge( this.constructor(), elems ); // Add the old object onto the stack (as a reference) ret.prevObject = this; ret.context = this.context; // Return the newly-formed element set return ret; }, // Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function( callback, args ) { return jQuery.each( this, callback, args ); }, ready: function( fn ) { // Add the callback jQuery.ready.promise().done( fn ); return this; }, slice: function() { return this.pushStack( core_slice.apply( this, arguments ) ); }, first: function() { return this.eq( 0 ); }, last: function() { return this.eq( -1 ); }, eq: function( i ) { var len = this.length, j = +i + ( i < 0 ? len : 0 ); return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); }, map: function( callback ) { return this.pushStack( jQuery.map(this, function( elem, i ) { return callback.call( elem, i, elem ); })); }, end: function() { return this.prevObject || this.constructor(null); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: core_push, sort: [].sort, splice: [].splice }; // Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn; jQuery.extend = jQuery.fn.extend = function() { var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // extend jQuery itself if only one argument is passed if ( length === i ) { target = this; --i; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; jQuery.extend({ // Unique for each copy of jQuery on the page // Non-digits removed to match rinlinejQuery expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), noConflict: function( deep ) { if ( window.$ === jQuery ) { window.$ = _$; } if ( deep && window.jQuery === jQuery ) { window.jQuery = _jQuery; } return jQuery; }, // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, // Hold (or release) the ready event holdReady: function( hold ) { if ( hold ) { jQuery.readyWait++; } else { jQuery.ready( true ); } }, // Handle when the DOM is ready ready: function( wait ) { // Abort if there are pending holds or we're already ready if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { return; } // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if ( !document.body ) { return setTimeout( jQuery.ready ); } // Remember that the DOM is ready jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be if ( wait !== true && --jQuery.readyWait > 0 ) { return; } // If there are functions bound, to execute readyList.resolveWith( document, [ jQuery ] ); // Trigger any bound ready events if ( jQuery.fn.trigger ) { jQuery( document ).trigger("ready").off("ready"); } }, // See test/unit/core.js for details concerning isFunction. // Since version 1.3, DOM methods and functions like alert // aren't supported. They return false on IE (#2968). isFunction: function( obj ) { return jQuery.type(obj) === "function"; }, isArray: Array.isArray || function( obj ) { return jQuery.type(obj) === "array"; }, isWindow: function( obj ) { /* jshint eqeqeq: false */ return obj != null && obj == obj.window; }, isNumeric: function( obj ) { return !isNaN( parseFloat(obj) ) && isFinite( obj ); }, type: function( obj ) { if ( obj == null ) { return String( obj ); } return typeof obj === "object" || typeof obj === "function" ? class2type[ core_toString.call(obj) ] || "object" : typeof obj; }, isPlainObject: function( obj ) { var key; // Must be an Object. // Because of IE, we also have to check the presence of the constructor property. // Make sure that DOM nodes and window objects don't pass through, as well if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { return false; } try { // Not own constructor property must be Object if ( obj.constructor && !core_hasOwn.call(obj, "constructor") && !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { return false; } } catch ( e ) { // IE8,9 Will throw exceptions on certain host objects #9897 return false; } // Support: IE<9 // Handle iteration over inherited properties before own properties. if ( jQuery.support.ownLast ) { for ( key in obj ) { return core_hasOwn.call( obj, key ); } } // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. for ( key in obj ) {} return key === undefined || core_hasOwn.call( obj, key ); }, isEmptyObject: function( obj ) { var name; for ( name in obj ) { return false; } return true; }, error: function( msg ) { throw new Error( msg ); }, // data: string of html // context (optional): If specified, the fragment will be created in this context, defaults to document // keepScripts (optional): If true, will include scripts passed in the html string parseHTML: function( data, context, keepScripts ) { if ( !data || typeof data !== "string" ) { return null; } if ( typeof context === "boolean" ) { keepScripts = context; context = false; } context = context || document; var parsed = rsingleTag.exec( data ), scripts = !keepScripts && []; // Single tag if ( parsed ) { return [ context.createElement( parsed[1] ) ]; } parsed = jQuery.buildFragment( [ data ], context, scripts ); if ( scripts ) { jQuery( scripts ).remove(); } return jQuery.merge( [], parsed.childNodes ); }, parseJSON: function( data ) { // Attempt to parse using the native JSON parser first if ( window.JSON && window.JSON.parse ) { return window.JSON.parse( data ); } if ( data === null ) { return data; } if ( typeof data === "string" ) { // Make sure leading/trailing whitespace is removed (IE can't handle it) data = jQuery.trim( data ); if ( data ) { // Make sure the incoming data is actual JSON // Logic borrowed from http://json.org/json2.js if ( rvalidchars.test( data.replace( rvalidescape, "@" ) .replace( rvalidtokens, "]" ) .replace( rvalidbraces, "")) ) { return ( new Function( "return " + data ) )(); } } } jQuery.error( "Invalid JSON: " + data ); }, // Cross-browser xml parsing parseXML: function( data ) { var xml, tmp; if ( !data || typeof data !== "string" ) { return null; } try { if ( window.DOMParser ) { // Standard tmp = new DOMParser(); xml = tmp.parseFromString( data , "text/xml" ); } else { // IE xml = new ActiveXObject( "Microsoft.XMLDOM" ); xml.async = "false"; xml.loadXML( data ); } } catch( e ) { xml = undefined; } if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { jQuery.error( "Invalid XML: " + data ); } return xml; }, noop: function() {}, // Evaluates a script in a global context // Workarounds based on findings by Jim Driscoll // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context globalEval: function( data ) { if ( data && jQuery.trim( data ) ) { // We use execScript on Internet Explorer // We use an anonymous function so that context is window // rather than jQuery in Firefox ( window.execScript || function( data ) { window[ "eval" ].call( window, data ); } )( data ); } }, // Convert dashed to camelCase; used by the css and data modules // Microsoft forgot to hump their vendor prefix (#9572) camelCase: function( string ) { return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); }, nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }, // args is for internal usage only each: function( obj, callback, args ) { var value, i = 0, length = obj.length, isArray = isArraylike( obj ); if ( args ) { if ( isArray ) { for ( ; i < length; i++ ) { value = callback.apply( obj[ i ], args ); if ( value === false ) { break; } } } else { for ( i in obj ) { value = callback.apply( obj[ i ], args ); if ( value === false ) { break; } } } // A special, fast, case for the most common use of each } else { if ( isArray ) { for ( ; i < length; i++ ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) { break; } } } else { for ( i in obj ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) { break; } } } } return obj; }, // Use native String.trim function wherever possible trim: core_trim && !core_trim.call("\uFEFF\xA0") ? function( text ) { return text == null ? "" : core_trim.call( text ); } : // Otherwise use our own trimming functionality function( text ) { return text == null ? "" : ( text + "" ).replace( rtrim, "" ); }, // results is for internal usage only makeArray: function( arr, results ) { var ret = results || []; if ( arr != null ) { if ( isArraylike( Object(arr) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr ); } else { core_push.call( ret, arr ); } } return ret; }, inArray: function( elem, arr, i ) { var len; if ( arr ) { if ( core_indexOf ) { return core_indexOf.call( arr, elem, i ); } len = arr.length; i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; for ( ; i < len; i++ ) { // Skip accessing in sparse arrays if ( i in arr && arr[ i ] === elem ) { return i; } } } return -1; }, merge: function( first, second ) { var l = second.length, i = first.length, j = 0; if ( typeof l === "number" ) { for ( ; j < l; j++ ) { first[ i++ ] = second[ j ]; } } else { while ( second[j] !== undefined ) { first[ i++ ] = second[ j++ ]; } } first.length = i; return first; }, grep: function( elems, callback, inv ) { var retVal, ret = [], i = 0, length = elems.length; inv = !!inv; // Go through the array, only saving the items // that pass the validator function for ( ; i < length; i++ ) { retVal = !!callback( elems[ i ], i ); if ( inv !== retVal ) { ret.push( elems[ i ] ); } } return ret; }, // arg is for internal usage only map: function( elems, callback, arg ) { var value, i = 0, length = elems.length, isArray = isArraylike( elems ), ret = []; // Go through the array, translating each of the items to their if ( isArray ) { for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } } // Go through every key on the object, } else { for ( i in elems ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } } } // Flatten any nested arrays return core_concat.apply( [], ret ); }, // A global GUID counter for objects guid: 1, // Bind a function to a context, optionally partially applying any // arguments. proxy: function( fn, context ) { var args, proxy, tmp; if ( typeof context === "string" ) { tmp = fn[ context ]; context = fn; fn = tmp; } // Quick check to determine if target is callable, in the spec // this throws a TypeError, but we will just return undefined. if ( !jQuery.isFunction( fn ) ) { return undefined; } // Simulated bind args = core_slice.call( arguments, 2 ); proxy = function() { return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); }; // Set the guid of unique handler to the same of original handler, so it can be removed proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy; }, // Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function access: function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, length = elems.length, bulk = key == null; // Sets many values if ( jQuery.type( key ) === "object" ) { chainable = true; for ( i in key ) { jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); } // Sets one value } else if ( value !== undefined ) { chainable = true; if ( !jQuery.isFunction( value ) ) { raw = true; } if ( bulk ) { // Bulk operations run against the entire set if ( raw ) { fn.call( elems, value ); fn = null; // ...except when executing function values } else { bulk = fn; fn = function( elem, key, value ) { return bulk.call( jQuery( elem ), value ); }; } } if ( fn ) { for ( ; i < length; i++ ) { fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); } } } return chainable ? elems : // Gets bulk ? fn.call( elems ) : length ? fn( elems[0], key ) : emptyGet; }, now: function() { return ( new Date() ).getTime(); }, // A method for quickly swapping in/out CSS properties to get correct calculations. // Note: this method belongs to the css module but it's needed here for the support module. // If support gets modularized, this method should be moved back to the css module. swap: function( elem, options, callback, args ) { var ret, name, old = {}; // Remember the old values, and insert the new ones for ( name in options ) { old[ name ] = elem.style[ name ]; elem.style[ name ] = options[ name ]; } ret = callback.apply( elem, args || [] ); // Revert the old values for ( name in options ) { elem.style[ name ] = old[ name ]; } return ret; } }); jQuery.ready.promise = function( obj ) { if ( !readyList ) { readyList = jQuery.Deferred(); // Catch cases where $(document).ready() is called after the browser event has already occurred. // we once tried to use readyState "interactive" here, but it caused issues like the one // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready setTimeout( jQuery.ready ); // Standards-based browsers support DOMContentLoaded } else if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded", completed, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", completed, false ); // If IE event model is used } else { // Ensure firing before onload, maybe late but safe also for iframes document.attachEvent( "onreadystatechange", completed ); // A fallback to window.onload, that will always work window.attachEvent( "onload", completed ); // If IE and not a frame // continually check to see if the document is ready var top = false; try { top = window.frameElement == null && document.documentElement; } catch(e) {} if ( top && top.doScroll ) { (function doScrollCheck() { if ( !jQuery.isReady ) { try { // Use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); // and execute any waiting functions jQuery.ready(); } })(); } } } return readyList.promise( obj ); }; // Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); function isArraylike( obj ) { var length = obj.length, type = jQuery.type( obj ); if ( jQuery.isWindow( obj ) ) { return false; } if ( obj.nodeType === 1 && length ) { return true; } return type === "array" || type !== "function" && ( length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj ); } // All jQuery objects should point back to these rootjQuery = jQuery(document); /*! * Sizzle CSS Selector Engine v1.9.4-pre * http://sizzlejs.com/ * * Copyright 2013 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2013-05-27 */ (function( window, undefined ) { var i, support, cachedruns, Expr, getText, isXML, compile, outermostContext, sortInput, // Local document vars setDocument, document, docElem, documentIsHTML, rbuggyQSA, rbuggyMatches, matches, contains, // Instance-specific data expando = "sizzle" + -(new Date()), preferredDoc = window.document, dirruns = 0, done = 0, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), hasDuplicate = false, sortOrder = function() { return 0; }, // General-purpose constants strundefined = typeof undefined, MAX_NEGATIVE = 1 << 31, // Instance methods hasOwn = ({}).hasOwnProperty, arr = [], pop = arr.pop, push_native = arr.push, push = arr.push, slice = arr.slice, // Use a stripped-down indexOf if we can't use a native one indexOf = arr.indexOf || function( elem ) { var i = 0, len = this.length; for ( ; i < len; i++ ) { if ( this[i] === elem ) { return i; } } return -1; }, booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", // Regular expressions // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace whitespace = "[\\x20\\t\\r\\n\\f]", // http://www.w3.org/TR/css3-syntax/#characters characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", // Loosely modeled on CSS identifier characters // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier identifier = characterEncoding.replace( "w", "w#" ), // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", // Prefer arguments quoted, // then not containing pseudos/brackets, // then attribute selectors/non-parenthetical expressions, // then anything else // These preferences are here to reduce the number of selectors // needing tokenize in the PSEUDO preFilter pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), rsibling = new RegExp( whitespace + "*[+~]" ), rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), rpseudo = new RegExp( pseudos ), ridentifier = new RegExp( "^" + identifier + "$" ), matchExpr = { "ID": new RegExp( "^#(" + characterEncoding + ")" ), "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), "ATTR": new RegExp( "^" + attributes ), "PSEUDO": new RegExp( "^" + pseudos ), "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), // For use in libraries implementing .is() // We use this for POS matching in `select` "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) }, rnative = /^[^{]+\{\s*\[native \w/, // Easily-parseable/retrievable ID or TAG or CLASS selectors rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rinputs = /^(?:input|select|textarea|button)$/i, rheader = /^h\d$/i, rescape = /'|\\/g, // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), funescape = function( _, escaped, escapedWhitespace ) { var high = "0x" + escaped - 0x10000; // NaN means non-codepoint // Support: Firefox // Workaround erroneous numeric interpretation of +"0x" return high !== high || escapedWhitespace ? escaped : // BMP codepoint high < 0 ? String.fromCharCode( high + 0x10000 ) : // Supplemental Plane codepoint (surrogate pair) String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); }; // Optimize for push.apply( _, NodeList ) try { push.apply( (arr = slice.call( preferredDoc.childNodes )), preferredDoc.childNodes ); // Support: Android<4.0 // Detect silently failing push.apply arr[ preferredDoc.childNodes.length ].nodeType; } catch ( e ) { push = { apply: arr.length ? // Leverage slice if possible function( target, els ) { push_native.apply( target, slice.call(els) ); } : // Support: IE<9 // Otherwise append directly function( target, els ) { var j = target.length, i = 0; // Can't trust NodeList.length while ( (target[j++] = els[i++]) ) {} target.length = j - 1; } }; } function Sizzle( selector, context, results, seed ) { var match, elem, m, nodeType, // QSA vars i, groups, old, nid, newContext, newSelector; if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { setDocument( context ); } context = context || document; results = results || []; if ( !selector || typeof selector !== "string" ) { return results; } if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { return []; } if ( documentIsHTML && !seed ) { // Shortcuts if ( (match = rquickExpr.exec( selector )) ) { // Speed-up: Sizzle("#ID") if ( (m = match[1]) ) { if ( nodeType === 9 ) { elem = context.getElementById( m ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE, Opera, and Webkit return items // by name instead of ID if ( elem.id === m ) { results.push( elem ); return results; } } else { return results; } } else { // Context is not a document if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && contains( context, elem ) && elem.id === m ) { results.push( elem ); return results; } } // Speed-up: Sizzle("TAG") } else if ( match[2] ) { push.apply( results, context.getElementsByTagName( selector ) ); return results; // Speed-up: Sizzle(".CLASS") } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { push.apply( results, context.getElementsByClassName( m ) ); return results; } } // QSA path if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { nid = old = expando; newContext = context; newSelector = nodeType === 9 && selector; // qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { groups = tokenize( selector ); if ( (old = context.getAttribute("id")) ) { nid = old.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); } nid = "[id='" + nid + "'] "; i = groups.length; while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); } newContext = rsibling.test( selector ) && context.parentNode || context; newSelector = groups.join(","); } if ( newSelector ) { try { push.apply( results, newContext.querySelectorAll( newSelector ) ); return results; } catch(qsaError) { } finally { if ( !old ) { context.removeAttribute("id"); } } } } } // All others return select( selector.replace( rtrim, "$1" ), context, results, seed ); } /** * For feature detection * @param {Function} fn The function to test for native support */ function isNative( fn ) { return rnative.test( fn + "" ); } /** * Create key-value caches of limited size * @returns {Function(string, Object)} Returns the Object data after storing it on itself with * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) * deleting the oldest entry */ function createCache() { var keys = []; function cache( key, value ) { // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) if ( keys.push( key += " " ) > Expr.cacheLength ) { // Only keep the most recent entries delete cache[ keys.shift() ]; } return (cache[ key ] = value); } return cache; } /** * Mark a function for special use by Sizzle * @param {Function} fn The function to mark */ function markFunction( fn ) { fn[ expando ] = true; return fn; } /** * Support testing using an element * @param {Function} fn Passed the created div and expects a boolean result */ function assert( fn ) { var div = document.createElement("div"); try { return !!fn( div ); } catch (e) { return false; } finally { // Remove from its parent by default if ( div.parentNode ) { div.parentNode.removeChild( div ); } // release memory in IE div = null; } } /** * Adds the same handler for all of the specified attrs * @param {String} attrs Pipe-separated list of attributes * @param {Function} handler The method that will be applied if the test fails * @param {Boolean} test The result of a test. If true, null will be set as the handler in leiu of the specified handler */ function addHandle( attrs, handler, test ) { attrs = attrs.split("|"); var current, i = attrs.length, setHandle = test ? null : handler; while ( i-- ) { // Don't override a user's handler if ( !(current = Expr.attrHandle[ attrs[i] ]) || current === handler ) { Expr.attrHandle[ attrs[i] ] = setHandle; } } } /** * Fetches boolean attributes by node * @param {Element} elem * @param {String} name */ function boolHandler( elem, name ) { // XML does not need to be checked as this will not be assigned for XML documents var val = elem.getAttributeNode( name ); return val && val.specified ? val.value : elem[ name ] === true ? name.toLowerCase() : null; } /** * Fetches attributes without interpolation * http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx * @param {Element} elem * @param {String} name */ function interpolationHandler( elem, name ) { // XML does not need to be checked as this will not be assigned for XML documents return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); } /** * Uses defaultValue to retrieve value in IE6/7 * @param {Element} elem * @param {String} name */ function valueHandler( elem ) { // Ignore the value *property* on inputs by using defaultValue // Fallback to Sizzle.attr by returning undefined where appropriate // XML does not need to be checked as this will not be assigned for XML documents if ( elem.nodeName.toLowerCase() === "input" ) { return elem.defaultValue; } } /** * Checks document order of two siblings * @param {Element} a * @param {Element} b * @returns Returns -1 if a precedes b, 1 if a follows b */ function siblingCheck( a, b ) { var cur = b && a, diff = cur && a.nodeType === 1 && b.nodeType === 1 && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE ); // Use IE sourceIndex if available on both nodes if ( diff ) { return diff; } // Check if b follows a if ( cur ) { while ( (cur = cur.nextSibling) ) { if ( cur === b ) { return -1; } } } return a ? 1 : -1; } /** * Returns a function to use in pseudos for input types * @param {String} type */ function createInputPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === type; }; } /** * Returns a function to use in pseudos for buttons * @param {String} type */ function createButtonPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return (name === "input" || name === "button") && elem.type === type; }; } /** * Returns a function to use in pseudos for positionals * @param {Function} fn */ function createPositionalPseudo( fn ) { return markFunction(function( argument ) { argument = +argument; return markFunction(function( seed, matches ) { var j, matchIndexes = fn( [], seed.length, argument ), i = matchIndexes.length; // Match elements found at the specified indexes while ( i-- ) { if ( seed[ (j = matchIndexes[i]) ] ) { seed[j] = !(matches[j] = seed[j]); } } }); }); } /** * Detect xml * @param {Element|Object} elem An element or a document */ isXML = Sizzle.isXML = function( elem ) { // documentElement is verified for cases where it doesn't yet exist // (such as loading iframes in IE - #4833) var documentElement = elem && (elem.ownerDocument || elem).documentElement; return documentElement ? documentElement.nodeName !== "HTML" : false; }; // Expose support vars for convenience support = Sizzle.support = {}; /** * Sets document-related variables once based on the current document * @param {Element|Object} [doc] An element or document object to use to set the document * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { var doc = node ? node.ownerDocument || node : preferredDoc, parent = doc.parentWindow; // If no document and documentElement is available, return if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { return document; } // Set our document document = doc; docElem = doc.documentElement; // Support tests documentIsHTML = !isXML( doc ); // Support: IE>8 // If iframe document is assigned to "document" variable and if iframe has been reloaded, // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 if ( parent && parent.frameElement ) { parent.attachEvent( "onbeforeunload", function() { setDocument(); }); } /* Attributes ---------------------------------------------------------------------- */ // Support: IE<8 // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) support.attributes = assert(function( div ) { // Support: IE<8 // Prevent attribute/property "interpolation" div.innerHTML = ""; addHandle( "type|href|height|width", interpolationHandler, div.firstChild.getAttribute("href") === "#" ); // Support: IE<9 // Use getAttributeNode to fetch booleans when getAttribute lies addHandle( booleans, boolHandler, div.getAttribute("disabled") == null ); div.className = "i"; return !div.getAttribute("className"); }); // Support: IE<9 // Retrieving value should defer to defaultValue support.input = assert(function( div ) { div.innerHTML = ""; div.firstChild.setAttribute( "value", "" ); return div.firstChild.getAttribute( "value" ) === ""; }); // IE6/7 still return empty string for value, // but are actually retrieving the property addHandle( "value", valueHandler, support.attributes && support.input ); /* getElement(s)By* ---------------------------------------------------------------------- */ // Check if getElementsByTagName("*") returns only elements support.getElementsByTagName = assert(function( div ) { div.appendChild( doc.createComment("") ); return !div.getElementsByTagName("*").length; }); // Check if getElementsByClassName can be trusted support.getElementsByClassName = assert(function( div ) { div.innerHTML = "
      "; // Support: Safari<4 // Catch class over-caching div.firstChild.className = "i"; // Support: Opera<10 // Catch gEBCN failure to find non-leading classes return div.getElementsByClassName("i").length === 2; }); // Support: IE<10 // Check if getElementById returns elements by name // The broken getElementById methods don't pick up programatically-set names, // so use a roundabout getElementsByName test support.getById = assert(function( div ) { docElem.appendChild( div ).id = expando; return !doc.getElementsByName || !doc.getElementsByName( expando ).length; }); // ID find and filter if ( support.getById ) { Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== strundefined && documentIsHTML ) { var m = context.getElementById( id ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 return m && m.parentNode ? [m] : []; } }; Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { return elem.getAttribute("id") === attrId; }; }; } else { // Support: IE6/7 // getElementById is not reliable as a find shortcut delete Expr.find["ID"]; Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); return node && node.value === attrId; }; }; } // Tag Expr.find["TAG"] = support.getElementsByTagName ? function( tag, context ) { if ( typeof context.getElementsByTagName !== strundefined ) { return context.getElementsByTagName( tag ); } } : function( tag, context ) { var elem, tmp = [], i = 0, results = context.getElementsByTagName( tag ); // Filter out possible comments if ( tag === "*" ) { while ( (elem = results[i++]) ) { if ( elem.nodeType === 1 ) { tmp.push( elem ); } } return tmp; } return results; }; // Class Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { return context.getElementsByClassName( className ); } }; /* QSA/matchesSelector ---------------------------------------------------------------------- */ // QSA and matchesSelector support // matchesSelector(:active) reports false when true (IE9/Opera 11.5) rbuggyMatches = []; // qSa(:focus) reports false when true (Chrome 21) // We allow this because of a bug in IE8/9 that throws an error // whenever `document.activeElement` is accessed on an iframe // So, we allow :focus to pass through QSA all the time to avoid the IE error // See http://bugs.jquery.com/ticket/13378 rbuggyQSA = []; if ( (support.qsa = isNative(doc.querySelectorAll)) ) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function( div ) { // Select is set to empty string on purpose // This is to test IE's treatment of not explicitly // setting a boolean content attribute, // since its presence should be enough // http://bugs.jquery.com/ticket/12359 div.innerHTML = ""; // Support: IE8 // Boolean attributes and "value" are not treated correctly if ( !div.querySelectorAll("[selected]").length ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests if ( !div.querySelectorAll(":checked").length ) { rbuggyQSA.push(":checked"); } }); assert(function( div ) { // Support: Opera 10-12/IE8 // ^= $= *= and empty values // Should not select anything // Support: Windows 8 Native Apps // The type attribute is restricted during .innerHTML assignment var input = doc.createElement("input"); input.setAttribute( "type", "hidden" ); div.appendChild( input ).setAttribute( "t", "" ); if ( div.querySelectorAll("[t^='']").length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) // IE8 throws error here and will not see later tests if ( !div.querySelectorAll(":enabled").length ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Opera 10-11 does not throw on post-comma invalid pseudos div.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } if ( (support.matchesSelector = isNative( (matches = docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector) )) ) { assert(function( div ) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9) support.disconnectedMatch = matches.call( div, "div" ); // This should fail with an exception // Gecko does not error, returns false instead matches.call( div, "[s!='']:x" ); rbuggyMatches.push( "!=", pseudos ); }); } rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); /* Contains ---------------------------------------------------------------------- */ // Element contains another // Purposefully does not implement inclusive descendent // As in, an element does not contain itself contains = isNative(docElem.contains) || docElem.compareDocumentPosition ? function( a, b ) { var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; return a === bup || !!( bup && bup.nodeType === 1 && ( adown.contains ? adown.contains( bup ) : a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 )); } : function( a, b ) { if ( b ) { while ( (b = b.parentNode) ) { if ( b === a ) { return true; } } } return false; }; /* Sorting ---------------------------------------------------------------------- */ // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) // Detached nodes confoundingly follow *each other* support.sortDetached = assert(function( div1 ) { // Should return 1, but returns 4 (following) return div1.compareDocumentPosition( doc.createElement("div") ) & 1; }); // Document order sorting sortOrder = docElem.compareDocumentPosition ? function( a, b ) { // Flag for duplicate removal if ( a === b ) { hasDuplicate = true; return 0; } var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); if ( compare ) { // Disconnected nodes if ( compare & 1 || (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { // Choose the first element that is related to our preferred document if ( a === doc || contains(preferredDoc, a) ) { return -1; } if ( b === doc || contains(preferredDoc, b) ) { return 1; } // Maintain original order return sortInput ? ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : 0; } return compare & 4 ? -1 : 1; } // Not directly comparable, sort on existence of method return a.compareDocumentPosition ? -1 : 1; } : function( a, b ) { var cur, i = 0, aup = a.parentNode, bup = b.parentNode, ap = [ a ], bp = [ b ]; // Exit early if the nodes are identical if ( a === b ) { hasDuplicate = true; return 0; // Parentless nodes are either documents or disconnected } else if ( !aup || !bup ) { return a === doc ? -1 : b === doc ? 1 : aup ? -1 : bup ? 1 : sortInput ? ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : 0; // If the nodes are siblings, we can do a quick check } else if ( aup === bup ) { return siblingCheck( a, b ); } // Otherwise we need full lists of their ancestors for comparison cur = a; while ( (cur = cur.parentNode) ) { ap.unshift( cur ); } cur = b; while ( (cur = cur.parentNode) ) { bp.unshift( cur ); } // Walk down the tree looking for a discrepancy while ( ap[i] === bp[i] ) { i++; } return i ? // Do a sibling check if the nodes have a common ancestor siblingCheck( ap[i], bp[i] ) : // Otherwise nodes in our document sort first ap[i] === preferredDoc ? -1 : bp[i] === preferredDoc ? 1 : 0; }; return doc; }; Sizzle.matches = function( expr, elements ) { return Sizzle( expr, null, null, elements ); }; Sizzle.matchesSelector = function( elem, expr ) { // Set document vars if needed if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } // Make sure that attribute selectors are quoted expr = expr.replace( rattributeQuotes, "='$1']" ); if ( support.matchesSelector && documentIsHTML && ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { try { var ret = matches.call( elem, expr ); // IE 9's matchesSelector returns false on disconnected nodes if ( ret || support.disconnectedMatch || // As well, disconnected nodes are said to be in a document // fragment in IE 9 elem.document && elem.document.nodeType !== 11 ) { return ret; } } catch(e) {} } return Sizzle( expr, document, null, [elem] ).length > 0; }; Sizzle.contains = function( context, elem ) { // Set document vars if needed if ( ( context.ownerDocument || context ) !== document ) { setDocument( context ); } return contains( context, elem ); }; Sizzle.attr = function( elem, name ) { // Set document vars if needed if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } var fn = Expr.attrHandle[ name.toLowerCase() ], // Don't get fooled by Object.prototype properties (jQuery #13807) val = ( fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? fn( elem, name, !documentIsHTML ) : undefined ); return val === undefined ? support.attributes || !documentIsHTML ? elem.getAttribute( name ) : (val = elem.getAttributeNode(name)) && val.specified ? val.value : null : val; }; Sizzle.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; /** * Document sorting and removing duplicates * @param {ArrayLike} results */ Sizzle.uniqueSort = function( results ) { var elem, duplicates = [], j = 0, i = 0; // Unless we *know* we can detect duplicates, assume their presence hasDuplicate = !support.detectDuplicates; sortInput = !support.sortStable && results.slice( 0 ); results.sort( sortOrder ); if ( hasDuplicate ) { while ( (elem = results[i++]) ) { if ( elem === results[ i ] ) { j = duplicates.push( i ); } } while ( j-- ) { results.splice( duplicates[ j ], 1 ); } } return results; }; /** * Utility function for retrieving the text value of an array of DOM nodes * @param {Array|Element} elem */ getText = Sizzle.getText = function( elem ) { var node, ret = "", i = 0, nodeType = elem.nodeType; if ( !nodeType ) { // If no nodeType, this is expected to be an array for ( ; (node = elem[i]); i++ ) { // Do not traverse comment nodes ret += getText( node ); } } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent for elements // innerText usage removed for consistency of new lines (see #11153) if ( typeof elem.textContent === "string" ) { return elem.textContent; } else { // Traverse its children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { ret += getText( elem ); } } } else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } // Do not include comment or processing instruction nodes return ret; }; Expr = Sizzle.selectors = { // Can be adjusted by the user cacheLength: 50, createPseudo: markFunction, match: matchExpr, attrHandle: {}, find: {}, relative: { ">": { dir: "parentNode", first: true }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: true }, "~": { dir: "previousSibling" } }, preFilter: { "ATTR": function( match ) { match[1] = match[1].replace( runescape, funescape ); // Move the given value to match[3] whether quoted or unquoted match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); if ( match[2] === "~=" ) { match[3] = " " + match[3] + " "; } return match.slice( 0, 4 ); }, "CHILD": function( match ) { /* matches from matchExpr["CHILD"] 1 type (only|nth|...) 2 what (child|of-type) 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 4 xn-component of xn+y argument ([+-]?\d*n|) 5 sign of xn-component 6 x of xn-component 7 sign of y-component 8 y of y-component */ match[1] = match[1].toLowerCase(); if ( match[1].slice( 0, 3 ) === "nth" ) { // nth-* requires argument if ( !match[3] ) { Sizzle.error( match[0] ); } // numeric x and y parameters for Expr.filter.CHILD // remember that false/true cast respectively to 0/1 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); // other types prohibit arguments } else if ( match[3] ) { Sizzle.error( match[0] ); } return match; }, "PSEUDO": function( match ) { var excess, unquoted = !match[5] && match[2]; if ( matchExpr["CHILD"].test( match[0] ) ) { return null; } // Accept quoted arguments as-is if ( match[3] && match[4] !== undefined ) { match[2] = match[4]; // Strip excess characters from unquoted arguments } else if ( unquoted && rpseudo.test( unquoted ) && // Get excess from tokenize (recursively) (excess = tokenize( unquoted, true )) && // advance to the next closing parenthesis (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { // excess is a negative index match[0] = match[0].slice( 0, excess ); match[2] = unquoted.slice( 0, excess ); } // Return only captures needed by the pseudo filter method (type and argument) return match.slice( 0, 3 ); } }, filter: { "TAG": function( nodeNameSelector ) { var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function( elem ) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; }, "CLASS": function( className ) { var pattern = classCache[ className + " " ]; return pattern || (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && classCache( className, function( elem ) { return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); }); }, "ATTR": function( name, operator, check ) { return function( elem ) { var result = Sizzle.attr( elem, name ); if ( result == null ) { return operator === "!="; } if ( !operator ) { return true; } result += ""; return operator === "=" ? result === check : operator === "!=" ? result !== check : operator === "^=" ? check && result.indexOf( check ) === 0 : operator === "*=" ? check && result.indexOf( check ) > -1 : operator === "$=" ? check && result.slice( -check.length ) === check : operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : false; }; }, "CHILD": function( type, what, argument, first, last ) { var simple = type.slice( 0, 3 ) !== "nth", forward = type.slice( -4 ) !== "last", ofType = what === "of-type"; return first === 1 && last === 0 ? // Shortcut for :nth-*(n) function( elem ) { return !!elem.parentNode; } : function( elem, context, xml ) { var cache, outerCache, node, diff, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), useCache = !xml && !ofType; if ( parent ) { // :(first|last|only)-(child|of-type) if ( simple ) { while ( dir ) { node = elem; while ( (node = node[ dir ]) ) { if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { return false; } } // Reverse direction for :only-* (if we haven't yet done so) start = dir = type === "only" && !start && "nextSibling"; } return true; } start = [ forward ? parent.firstChild : parent.lastChild ]; // non-xml :nth-child(...) stores cache data on `parent` if ( forward && useCache ) { // Seek `elem` from a previously-cached index outerCache = parent[ expando ] || (parent[ expando ] = {}); cache = outerCache[ type ] || []; nodeIndex = cache[0] === dirruns && cache[1]; diff = cache[0] === dirruns && cache[2]; node = nodeIndex && parent.childNodes[ nodeIndex ]; while ( (node = ++nodeIndex && node && node[ dir ] || // Fallback to seeking `elem` from the start (diff = nodeIndex = 0) || start.pop()) ) { // When found, cache indexes on `parent` and break if ( node.nodeType === 1 && ++diff && node === elem ) { outerCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } // Use previously-cached element index if available } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { diff = cache[1]; // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) } else { // Use the same loop as above to seek `elem` from the start while ( (node = ++nodeIndex && node && node[ dir ] || (diff = nodeIndex = 0) || start.pop()) ) { if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { // Cache the index of each encountered element if ( useCache ) { (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; } if ( node === elem ) { break; } } } } // Incorporate the offset, then check against cycle size diff -= last; return diff === first || ( diff % first === 0 && diff / first >= 0 ); } }; }, "PSEUDO": function( pseudo, argument ) { // pseudo-class names are case-insensitive // http://www.w3.org/TR/selectors/#pseudo-classes // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters // Remember that setFilters inherits from pseudos var args, fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || Sizzle.error( "unsupported pseudo: " + pseudo ); // The user may use createPseudo to indicate that // arguments are needed to create the filter function // just as Sizzle does if ( fn[ expando ] ) { return fn( argument ); } // But maintain support for old signatures if ( fn.length > 1 ) { args = [ pseudo, pseudo, "", argument ]; return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? markFunction(function( seed, matches ) { var idx, matched = fn( seed, argument ), i = matched.length; while ( i-- ) { idx = indexOf.call( seed, matched[i] ); seed[ idx ] = !( matches[ idx ] = matched[i] ); } }) : function( elem ) { return fn( elem, 0, args ); }; } return fn; } }, pseudos: { // Potentially complex pseudos "not": markFunction(function( selector ) { // Trim the selector passed to compile // to avoid treating leading and trailing // spaces as combinators var input = [], results = [], matcher = compile( selector.replace( rtrim, "$1" ) ); return matcher[ expando ] ? markFunction(function( seed, matches, context, xml ) { var elem, unmatched = matcher( seed, null, xml, [] ), i = seed.length; // Match elements unmatched by `matcher` while ( i-- ) { if ( (elem = unmatched[i]) ) { seed[i] = !(matches[i] = elem); } } }) : function( elem, context, xml ) { input[0] = elem; matcher( input, null, xml, results ); return !results.pop(); }; }), "has": markFunction(function( selector ) { return function( elem ) { return Sizzle( selector, elem ).length > 0; }; }), "contains": markFunction(function( text ) { return function( elem ) { return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; }; }), // "Whether an element is represented by a :lang() selector // is based solely on the element's language value // being equal to the identifier C, // or beginning with the identifier C immediately followed by "-". // The matching of C against the element's language value is performed case-insensitively. // The identifier C does not have to be a valid language name." // http://www.w3.org/TR/selectors/#lang-pseudo "lang": markFunction( function( lang ) { // lang value must be a valid identifier if ( !ridentifier.test(lang || "") ) { Sizzle.error( "unsupported lang: " + lang ); } lang = lang.replace( runescape, funescape ).toLowerCase(); return function( elem ) { var elemLang; do { if ( (elemLang = documentIsHTML ? elem.lang : elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { elemLang = elemLang.toLowerCase(); return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; } } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); return false; }; }), // Miscellaneous "target": function( elem ) { var hash = window.location && window.location.hash; return hash && hash.slice( 1 ) === elem.id; }, "root": function( elem ) { return elem === docElem; }, "focus": function( elem ) { return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); }, // Boolean properties "enabled": function( elem ) { return elem.disabled === false; }, "disabled": function( elem ) { return elem.disabled === true; }, "checked": function( elem ) { // In CSS3, :checked should return both checked and selected elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked var nodeName = elem.nodeName.toLowerCase(); return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); }, "selected": function( elem ) { // Accessing this property makes selected-by-default // options in Safari work properly if ( elem.parentNode ) { elem.parentNode.selectedIndex; } return elem.selected === true; }, // Contents "empty": function( elem ) { // http://www.w3.org/TR/selectors/#empty-pseudo // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), // not comment, processing instructions, or others // Thanks to Diego Perini for the nodeName shortcut // Greater than "@" means alpha characters (specifically not starting with "#" or "?") for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { return false; } } return true; }, "parent": function( elem ) { return !Expr.pseudos["empty"]( elem ); }, // Element/input types "header": function( elem ) { return rheader.test( elem.nodeName ); }, "input": function( elem ) { return rinputs.test( elem.nodeName ); }, "button": function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === "button" || name === "button"; }, "text": function( elem ) { var attr; // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) // use getAttribute instead to test this case return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); }, // Position-in-collection "first": createPositionalPseudo(function() { return [ 0 ]; }), "last": createPositionalPseudo(function( matchIndexes, length ) { return [ length - 1 ]; }), "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { return [ argument < 0 ? argument + length : argument ]; }), "even": createPositionalPseudo(function( matchIndexes, length ) { var i = 0; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "odd": createPositionalPseudo(function( matchIndexes, length ) { var i = 1; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; --i >= 0; ) { matchIndexes.push( i ); } return matchIndexes; }), "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; ++i < length; ) { matchIndexes.push( i ); } return matchIndexes; }) } }; // Add button/input type pseudos for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { Expr.pseudos[ i ] = createInputPseudo( i ); } for ( i in { submit: true, reset: true } ) { Expr.pseudos[ i ] = createButtonPseudo( i ); } function tokenize( selector, parseOnly ) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; if ( cached ) { return parseOnly ? 0 : cached.slice( 0 ); } soFar = selector; groups = []; preFilters = Expr.preFilter; while ( soFar ) { // Comma and first run if ( !matched || (match = rcomma.exec( soFar )) ) { if ( match ) { // Don't consume trailing commas as valid soFar = soFar.slice( match[0].length ) || soFar; } groups.push( tokens = [] ); } matched = false; // Combinators if ( (match = rcombinators.exec( soFar )) ) { matched = match.shift(); tokens.push({ value: matched, // Cast descendant combinators to space type: match[0].replace( rtrim, " " ) }); soFar = soFar.slice( matched.length ); } // Filters for ( type in Expr.filter ) { if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || (match = preFilters[ type ]( match ))) ) { matched = match.shift(); tokens.push({ value: matched, type: type, matches: match }); soFar = soFar.slice( matched.length ); } } if ( !matched ) { break; } } // Return the length of the invalid excess // if we're just parsing // Otherwise, throw an error or return tokens return parseOnly ? soFar.length : soFar ? Sizzle.error( selector ) : // Cache the tokens tokenCache( selector, groups ).slice( 0 ); } function toSelector( tokens ) { var i = 0, len = tokens.length, selector = ""; for ( ; i < len; i++ ) { selector += tokens[i].value; } return selector; } function addCombinator( matcher, combinator, base ) { var dir = combinator.dir, checkNonElements = base && dir === "parentNode", doneName = done++; return combinator.first ? // Check against closest ancestor/preceding element function( elem, context, xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { return matcher( elem, context, xml ); } } } : // Check against all ancestor/preceding elements function( elem, context, xml ) { var data, cache, outerCache, dirkey = dirruns + " " + doneName; // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching if ( xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { if ( matcher( elem, context, xml ) ) { return true; } } } } else { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || (elem[ expando ] = {}); if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { if ( (data = cache[1]) === true || data === cachedruns ) { return data === true; } } else { cache = outerCache[ dir ] = [ dirkey ]; cache[1] = matcher( elem, context, xml ) || cachedruns; if ( cache[1] === true ) { return true; } } } } } }; } function elementMatcher( matchers ) { return matchers.length > 1 ? function( elem, context, xml ) { var i = matchers.length; while ( i-- ) { if ( !matchers[i]( elem, context, xml ) ) { return false; } } return true; } : matchers[0]; } function condense( unmatched, map, filter, context, xml ) { var elem, newUnmatched = [], i = 0, len = unmatched.length, mapped = map != null; for ( ; i < len; i++ ) { if ( (elem = unmatched[i]) ) { if ( !filter || filter( elem, context, xml ) ) { newUnmatched.push( elem ); if ( mapped ) { map.push( i ); } } } } return newUnmatched; } function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { if ( postFilter && !postFilter[ expando ] ) { postFilter = setMatcher( postFilter ); } if ( postFinder && !postFinder[ expando ] ) { postFinder = setMatcher( postFinder, postSelector ); } return markFunction(function( seed, results, context, xml ) { var temp, i, elem, preMap = [], postMap = [], preexisting = results.length, // Get initial elements from seed or context elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), // Prefilter to get matcher input, preserving a map for seed-results synchronization matcherIn = preFilter && ( seed || !selector ) ? condense( elems, preMap, preFilter, context, xml ) : elems, matcherOut = matcher ? // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, postFinder || ( seed ? preFilter : preexisting || postFilter ) ? // ...intermediate processing is necessary [] : // ...otherwise use results directly results : matcherIn; // Find primary matches if ( matcher ) { matcher( matcherIn, matcherOut, context, xml ); } // Apply postFilter if ( postFilter ) { temp = condense( matcherOut, postMap ); postFilter( temp, [], context, xml ); // Un-match failing elements by moving them back to matcherIn i = temp.length; while ( i-- ) { if ( (elem = temp[i]) ) { matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); } } } if ( seed ) { if ( postFinder || preFilter ) { if ( postFinder ) { // Get the final matcherOut by condensing this intermediate into postFinder contexts temp = []; i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) ) { // Restore matcherIn since elem is not yet a final match temp.push( (matcherIn[i] = elem) ); } } postFinder( null, (matcherOut = []), temp, xml ); } // Move matched elements from seed to results to keep them synchronized i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) && (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { seed[temp] = !(results[temp] = elem); } } } // Add elements to results, through postFinder if defined } else { matcherOut = condense( matcherOut === results ? matcherOut.splice( preexisting, matcherOut.length ) : matcherOut ); if ( postFinder ) { postFinder( null, results, matcherOut, xml ); } else { push.apply( results, matcherOut ); } } }); } function matcherFromTokens( tokens ) { var checkContext, matcher, j, len = tokens.length, leadingRelative = Expr.relative[ tokens[0].type ], implicitRelative = leadingRelative || Expr.relative[" "], i = leadingRelative ? 1 : 0, // The foundational matcher ensures that elements are reachable from top-level context(s) matchContext = addCombinator( function( elem ) { return elem === checkContext; }, implicitRelative, true ), matchAnyContext = addCombinator( function( elem ) { return indexOf.call( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( (checkContext = context).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); } ]; for ( ; i < len; i++ ) { if ( (matcher = Expr.relative[ tokens[i].type ]) ) { matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; } else { matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); // Return special upon seeing a positional matcher if ( matcher[ expando ] ) { // Find the next relative operator (if any) for proper handling j = ++i; for ( ; j < len; j++ ) { if ( Expr.relative[ tokens[j].type ] ) { break; } } return setMatcher( i > 1 && elementMatcher( matchers ), i > 1 && toSelector( // If the preceding token was a descendant combinator, insert an implicit any-element `*` tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) ).replace( rtrim, "$1" ), matcher, i < j && matcherFromTokens( tokens.slice( i, j ) ), j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), j < len && toSelector( tokens ) ); } matchers.push( matcher ); } } return elementMatcher( matchers ); } function matcherFromGroupMatchers( elementMatchers, setMatchers ) { // A counter to specify which element is currently being matched var matcherCachedRuns = 0, bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, superMatcher = function( seed, context, xml, results, expandContext ) { var elem, j, matcher, setMatched = [], matchedCount = 0, i = "0", unmatched = seed && [], outermost = expandContext != null, contextBackup = outermostContext, // We must always have either seed elements or context elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), // Use integer dirruns iff this is the outermost matcher dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); if ( outermost ) { outermostContext = context !== document && context; cachedruns = matcherCachedRuns; } // Add elements passing elementMatchers directly to results // Keep `i` a string if there are no elements so `matchedCount` will be "00" below for ( ; (elem = elems[i]) != null; i++ ) { if ( byElement && elem ) { j = 0; while ( (matcher = elementMatchers[j++]) ) { if ( matcher( elem, context, xml ) ) { results.push( elem ); break; } } if ( outermost ) { dirruns = dirrunsUnique; cachedruns = ++matcherCachedRuns; } } // Track unmatched elements for set filters if ( bySet ) { // They will have gone through all possible matchers if ( (elem = !matcher && elem) ) { matchedCount--; } // Lengthen the array for every element, matched or not if ( seed ) { unmatched.push( elem ); } } } // Apply set filters to unmatched elements matchedCount += i; if ( bySet && i !== matchedCount ) { j = 0; while ( (matcher = setMatchers[j++]) ) { matcher( unmatched, setMatched, context, xml ); } if ( seed ) { // Reintegrate element matches to eliminate the need for sorting if ( matchedCount > 0 ) { while ( i-- ) { if ( !(unmatched[i] || setMatched[i]) ) { setMatched[i] = pop.call( results ); } } } // Discard index placeholder values to get only actual matches setMatched = condense( setMatched ); } // Add matches to results push.apply( results, setMatched ); // Seedless set matches succeeding multiple successful matchers stipulate sorting if ( outermost && !seed && setMatched.length > 0 && ( matchedCount + setMatchers.length ) > 1 ) { Sizzle.uniqueSort( results ); } } // Override manipulation of globals by nested matchers if ( outermost ) { dirruns = dirrunsUnique; outermostContext = contextBackup; } return unmatched; }; return bySet ? markFunction( superMatcher ) : superMatcher; } compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { var i, setMatchers = [], elementMatchers = [], cached = compilerCache[ selector + " " ]; if ( !cached ) { // Generate a function of recursive functions that can be used to check each element if ( !group ) { group = tokenize( selector ); } i = group.length; while ( i-- ) { cached = matcherFromTokens( group[i] ); if ( cached[ expando ] ) { setMatchers.push( cached ); } else { elementMatchers.push( cached ); } } // Cache the compiled function cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); } return cached; }; function multipleContexts( selector, contexts, results ) { var i = 0, len = contexts.length; for ( ; i < len; i++ ) { Sizzle( selector, contexts[i], results ); } return results; } function select( selector, context, results, seed ) { var i, tokens, token, type, find, match = tokenize( selector ); if ( !seed ) { // Try to minimize operations if there is only one group if ( match.length === 1 ) { // Take a shortcut and set the context if the root selector is an ID tokens = match[0] = match[0].slice( 0 ); if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && support.getById && context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; if ( !context ) { return results; } selector = selector.slice( tokens.shift().value.length ); } // Fetch a seed set for right-to-left matching i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; while ( i-- ) { token = tokens[i]; // Abort if we hit a combinator if ( Expr.relative[ (type = token.type) ] ) { break; } if ( (find = Expr.find[ type ]) ) { // Search, expanding context for leading sibling combinators if ( (seed = find( token.matches[0].replace( runescape, funescape ), rsibling.test( tokens[0].type ) && context.parentNode || context )) ) { // If seed is empty or no tokens remain, we can return early tokens.splice( i, 1 ); selector = seed.length && toSelector( tokens ); if ( !selector ) { push.apply( results, seed ); return results; } break; } } } } } // Compile and execute a filtering function // Provide `match` to avoid retokenization if we modified the selector above compile( selector, match )( seed, context, !documentIsHTML, results, rsibling.test( selector ) ); return results; } // Deprecated Expr.pseudos["nth"] = Expr.pseudos["eq"]; // Easy API for creating new setFilters function setFilters() {} setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); // One-time assignments // Sort stability support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; // Initialize against the default document setDocument(); // Support: Chrome<<14 // Always assume duplicates if they aren't passed to the comparison function [0, 0].sort( sortOrder ); support.detectDuplicates = hasDuplicate; jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; jQuery.expr[":"] = jQuery.expr.pseudos; jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; })( window ); // String to Object options format cache var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache function createOptions( options ) { var object = optionsCache[ options ] = {}; jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; }); return object; } /* * Create a callback list using the following parameters: * * options: an optional list of space-separated options that will change how * the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * * once: will ensure the callback list can only be fired once (like a Deferred) * * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * * unique: will ensure a callback can only be added once (no duplicate in the list) * * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); var // Flag to know if list is currently firing firing, // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // First callback to fire (used internally by add and fireWith) firingStart, // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = !options.once && [], // Fire callbacks fire = function( data ) { memory = options.memory && data; fired = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { memory = false; // To prevent further calls using add break; } } firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { list = []; } else { self.disable(); } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // First, we save the current length var start = list.length; (function add( args ) { jQuery.each( args, function( _, arg ) { var type = jQuery.type( arg ); if ( type === "function" ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) { // Inspect recursively add( arg ); } }); })( arguments ); // Do we need to add the callbacks to the // current firing batch? if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) { firingStart = start; fire( memory ); } } return this; }, // Remove a callback from the list remove: function() { if ( list ) { jQuery.each( arguments, function( _, arg ) { var index; while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; }, // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); }, // Remove all callbacks from the list empty: function() { list = []; firingLength = 0; return this; }, // Have the list do nothing anymore disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function() { return !list; }, // Lock the list in its current state lock: function() { stack = undefined; if ( !memory ) { self.disable(); } return this; }, // Is it locked? locked: function() { return !stack; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( list && ( !fired || stack ) ) { if ( firing ) { stack.push( args ); } else { fire( args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!fired; } }; return self; }; jQuery.extend({ Deferred: function( func ) { var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], state = "pending", promise = { state: function() { return state; }, always: function() { deferred.done( arguments ).fail( arguments ); return this; }, then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var action = tuple[ 0 ], fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer deferred[ tuple[1] ](function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } else { newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); fns = null; }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Keep pipe for back-compat promise.pipe = promise.then; // Add list-specific methods jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add; // Handle state if ( stateString ) { list.add(function() { // state = [ resolved | rejected ] state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ] deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; }; deferred[ tuple[0] + "With" ] = list.fireWith; }); // Make the deferred a promise promise.promise( deferred ); // Call given func if any if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; }, // Deferred helper when: function( subordinate /* , ..., subordinateN */ ) { var i = 0, resolveValues = core_slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that. deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values updateFunc = function( i, contexts, values ) { return function( value ) { contexts[ i ] = this; values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; if( values === progressValues ) { deferred.notifyWith( contexts, values ); } else if ( !( --remaining ) ) { deferred.resolveWith( contexts, values ); } }; }, progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolved if ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array( length ); for ( ; i < length; i++ ) { if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { resolveValues[ i ].promise() .done( updateFunc( i, resolveContexts, resolveValues ) ) .fail( deferred.reject ) .progress( updateFunc( i, progressContexts, progressValues ) ); } else { --remaining; } } } // if we're not waiting on anything, resolve the master if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise(); } }); jQuery.support = (function( support ) { var all, a, input, select, fragment, opt, eventName, isSupported, i, div = document.createElement("div"); // Setup div.setAttribute( "className", "t" ); div.innerHTML = "
      a"; // Finish early in limited (non-browser) environments all = div.getElementsByTagName("*") || []; a = div.getElementsByTagName("a")[ 0 ]; if ( !a || !a.style || !all.length ) { return support; } // First batch of tests select = document.createElement("select"); opt = select.appendChild( document.createElement("option") ); input = div.getElementsByTagName("input")[ 0 ]; a.style.cssText = "top:1px;float:left;opacity:.5"; // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) support.getSetAttribute = div.className !== "t"; // IE strips leading whitespace when .innerHTML is used support.leadingWhitespace = div.firstChild.nodeType === 3; // Make sure that tbody elements aren't automatically inserted // IE will insert them into empty tables support.tbody = !div.getElementsByTagName("tbody").length; // Make sure that link elements get serialized correctly by innerHTML // This requires a wrapper element in IE support.htmlSerialize = !!div.getElementsByTagName("link").length; // Get the style information from getAttribute // (IE uses .cssText instead) support.style = /top/.test( a.getAttribute("style") ); // Make sure that URLs aren't manipulated // (IE normalizes it by default) support.hrefNormalized = a.getAttribute("href") === "/a"; // Make sure that element opacity exists // (IE uses filter instead) // Use a regex to work around a WebKit issue. See #5145 support.opacity = /^0.5/.test( a.style.opacity ); // Verify style float existence // (IE uses styleFloat instead of cssFloat) support.cssFloat = !!a.style.cssFloat; // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) support.checkOn = !!input.value; // Make sure that a selected-by-default option has a working selected property. // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) support.optSelected = opt.selected; // Tests for enctype support on a form (#6743) support.enctype = !!document.createElement("form").enctype; // Makes sure cloning an html5 element does not cause problems // Where outerHTML is undefined, this still works support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>"; // Will be defined later support.inlineBlockNeedsLayout = false; support.shrinkWrapBlocks = false; support.pixelPosition = false; support.deleteExpando = true; support.noCloneEvent = true; support.reliableMarginRight = true; support.boxSizingReliable = true; // Make sure checked status is properly cloned input.checked = true; support.noCloneChecked = input.cloneNode( true ).checked; // Make sure that the options inside disabled selects aren't marked as disabled // (WebKit marks them as disabled) select.disabled = true; support.optDisabled = !opt.disabled; // Support: IE<9 try { delete div.test; } catch( e ) { support.deleteExpando = false; } // Check if we can trust getAttribute("value") input = document.createElement("input"); input.setAttribute( "value", "" ); support.input = input.getAttribute( "value" ) === ""; // Check if an input maintains its value after becoming a radio input.value = "t"; input.setAttribute( "type", "radio" ); support.radioValue = input.value === "t"; // #11217 - WebKit loses check when the name is after the checked attribute input.setAttribute( "checked", "t" ); input.setAttribute( "name", "t" ); fragment = document.createDocumentFragment(); fragment.appendChild( input ); // Check if a disconnected checkbox will retain its checked // value of true after appended to the DOM (IE6/7) support.appendChecked = input.checked; // WebKit doesn't clone checked state correctly in fragments support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; // Support: IE<9 // Opera does not clone events (and typeof div.attachEvent === undefined). // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() if ( div.attachEvent ) { div.attachEvent( "onclick", function() { support.noCloneEvent = false; }); div.cloneNode( true ).click(); } // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) for ( i in { submit: true, change: true, focusin: true }) { div.setAttribute( eventName = "on" + i, "t" ); support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; } div.style.backgroundClip = "content-box"; div.cloneNode( true ).style.backgroundClip = ""; support.clearCloneStyle = div.style.backgroundClip === "content-box"; // Support: IE<9 // Iteration over object's inherited properties before its own. for ( i in jQuery( support ) ) { break; } support.ownLast = i !== "0"; // Run tests that need a body at doc ready jQuery(function() { var container, marginDiv, tds, divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", body = document.getElementsByTagName("body")[0]; if ( !body ) { // Return for frameset docs that don't have a body return; } container = document.createElement("div"); container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; body.appendChild( container ).appendChild( div ); // Support: IE8 // Check if table cells still have offsetWidth/Height when they are set // to display:none and there are still other visible table cells in a // table row; if so, offsetWidth/Height are not reliable for use when // determining if an element has been hidden directly using // display:none (it is still safe to use offsets if a parent element is // hidden; don safety goggles and see bug #4512 for more information). div.innerHTML = "
      t
      "; tds = div.getElementsByTagName("td"); tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; isSupported = ( tds[ 0 ].offsetHeight === 0 ); tds[ 0 ].style.display = ""; tds[ 1 ].style.display = "none"; // Support: IE8 // Check if empty table cells still have offsetWidth/Height support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); // Check box-sizing and margin behavior. div.innerHTML = ""; div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; // Workaround failing boxSizing test due to offsetWidth returning wrong value // with some non-1 values of body zoom, ticket #13543 jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() { support.boxSizing = div.offsetWidth === 4; }); // Use window.getComputedStyle because jsdom on node.js will break without it. if ( window.getComputedStyle ) { support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; // Check if div with explicit width and no margin-right incorrectly // gets computed margin-right based on width of container. (#3333) // Fails in WebKit before Feb 2011 nightlies // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right marginDiv = div.appendChild( document.createElement("div") ); marginDiv.style.cssText = div.style.cssText = divReset; marginDiv.style.marginRight = marginDiv.style.width = "0"; div.style.width = "1px"; support.reliableMarginRight = !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); } if ( typeof div.style.zoom !== core_strundefined ) { // Support: IE<8 // Check if natively block-level elements act like inline-block // elements when setting their display to 'inline' and giving // them layout div.innerHTML = ""; div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); // Support: IE6 // Check if elements with layout shrink-wrap their children div.style.display = "block"; div.innerHTML = "
      "; div.firstChild.style.width = "5px"; support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); if ( support.inlineBlockNeedsLayout ) { // Prevent IE 6 from affecting layout for positioned elements #11048 // Prevent IE from shrinking the body in IE 7 mode #12869 // Support: IE<8 body.style.zoom = 1; } } body.removeChild( container ); // Null elements to avoid leaks in IE container = div = tds = marginDiv = null; }); // Null elements to avoid leaks in IE all = select = fragment = opt = a = input = null; return support; })({}); var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, rmultiDash = /([A-Z])/g; function internalData( elem, name, data, pvt /* Internal Use Only */ ){ if ( !jQuery.acceptData( elem ) ) { return; } var ret, thisCache, internalKey = jQuery.expando, // We have to handle DOM nodes and JS objects differently because IE6-7 // can't GC object references properly across the DOM-JS boundary isNode = elem.nodeType, // Only DOM nodes need the global jQuery cache; JS object data is // attached directly to the object so GC can occur automatically cache = isNode ? jQuery.cache : elem, // Only defining an ID for JS objects if its cache already exists allows // the code to shortcut on the same path as a DOM node with no cache id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; // Avoid doing any more work than we need to when trying to get data on an // object that has no data at all if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { return; } if ( !id ) { // Only DOM nodes need a new unique ID for each element since their data // ends up in the global cache if ( isNode ) { id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++; } else { id = internalKey; } } if ( !cache[ id ] ) { // Avoid exposing jQuery metadata on plain JS objects when the object // is serialized using JSON.stringify cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; } // An object can be passed to jQuery.data instead of a key/value pair; this gets // shallow copied over onto the existing cache if ( typeof name === "object" || typeof name === "function" ) { if ( pvt ) { cache[ id ] = jQuery.extend( cache[ id ], name ); } else { cache[ id ].data = jQuery.extend( cache[ id ].data, name ); } } thisCache = cache[ id ]; // jQuery data() is stored in a separate object inside the object's internal data // cache in order to avoid key collisions between internal data and user-defined // data. if ( !pvt ) { if ( !thisCache.data ) { thisCache.data = {}; } thisCache = thisCache.data; } if ( data !== undefined ) { thisCache[ jQuery.camelCase( name ) ] = data; } // Check for both converted-to-camel and non-converted data property names // If a data property was specified if ( typeof name === "string" ) { // First Try to find as-is property data ret = thisCache[ name ]; // Test for null|undefined property data if ( ret == null ) { // Try to find the camelCased property ret = thisCache[ jQuery.camelCase( name ) ]; } } else { ret = thisCache; } return ret; } function internalRemoveData( elem, name, pvt ) { if ( !jQuery.acceptData( elem ) ) { return; } var thisCache, i, isNode = elem.nodeType, // See jQuery.data for more information cache = isNode ? jQuery.cache : elem, id = isNode ? elem[ jQuery.expando ] : jQuery.expando; // If there is already no cache entry for this object, there is no // purpose in continuing if ( !cache[ id ] ) { return; } if ( name ) { thisCache = pvt ? cache[ id ] : cache[ id ].data; if ( thisCache ) { // Support array or space separated string names for data keys if ( !jQuery.isArray( name ) ) { // try the string as a key before any manipulation if ( name in thisCache ) { name = [ name ]; } else { // split the camel cased version by spaces unless a key with the spaces exists name = jQuery.camelCase( name ); if ( name in thisCache ) { name = [ name ]; } else { name = name.split(" "); } } } else { // If "name" is an array of keys... // When data is initially created, via ("key", "val") signature, // keys will be converted to camelCase. // Since there is no way to tell _how_ a key was added, remove // both plain key and camelCase key. #12786 // This will only penalize the array argument path. name = name.concat( jQuery.map( name, jQuery.camelCase ) ); } i = name.length; while ( i-- ) { delete thisCache[ name[i] ]; } // If there is no data left in the cache, we want to continue // and let the cache object itself get destroyed if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { return; } } } // See jQuery.data for more information if ( !pvt ) { delete cache[ id ].data; // Don't destroy the parent cache unless the internal data object // had been the only thing left in it if ( !isEmptyDataObject( cache[ id ] ) ) { return; } } // Destroy the cache if ( isNode ) { jQuery.cleanData( [ elem ], true ); // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) /* jshint eqeqeq: false */ } else if ( jQuery.support.deleteExpando || cache != cache.window ) { /* jshint eqeqeq: true */ delete cache[ id ]; // When all else fails, null } else { cache[ id ] = null; } } jQuery.extend({ cache: {}, // The following elements throw uncatchable exceptions if you // attempt to add expando properties to them. noData: { "applet": true, "embed": true, // Ban all objects except for Flash (which handle expandos) "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" }, hasData: function( elem ) { elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; return !!elem && !isEmptyDataObject( elem ); }, data: function( elem, name, data ) { return internalData( elem, name, data ); }, removeData: function( elem, name ) { return internalRemoveData( elem, name ); }, // For internal use only. _data: function( elem, name, data ) { return internalData( elem, name, data, true ); }, _removeData: function( elem, name ) { return internalRemoveData( elem, name, true ); }, // A method for determining if a DOM node can handle the data expando acceptData: function( elem ) { // Do not set data on non-element because it will not be cleared (#8335). if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { return false; } var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; // nodes accept data unless otherwise specified; rejection can be conditional return !noData || noData !== true && elem.getAttribute("classid") === noData; } }); jQuery.fn.extend({ data: function( key, value ) { var attrs, name, data = null, i = 0, elem = this[0]; // Special expections of .data basically thwart jQuery.access, // so implement the relevant behavior ourselves // Gets all values if ( key === undefined ) { if ( this.length ) { data = jQuery.data( elem ); if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { attrs = elem.attributes; for ( ; i < attrs.length; i++ ) { name = attrs[i].name; if ( name.indexOf("data-") === 0 ) { name = jQuery.camelCase( name.slice(5) ); dataAttr( elem, name, data[ name ] ); } } jQuery._data( elem, "parsedAttrs", true ); } } return data; } // Sets multiple values if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key ); }); } return arguments.length > 1 ? // Sets one value this.each(function() { jQuery.data( this, key, value ); }) : // Gets one value // Try to fetch any internally stored data first elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; }, removeData: function( key ) { return this.each(function() { jQuery.removeData( this, key ); }); } }); function dataAttr( elem, key, data ) { // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : // Only convert to a number if it doesn't change the string +data + "" === data ? +data : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; } catch( e ) {} // Make sure we set the data so it isn't changed later jQuery.data( elem, key, data ); } else { data = undefined; } } return data; } // checks a cache object for emptiness function isEmptyDataObject( obj ) { var name; for ( name in obj ) { // if the public data object is empty, the private is still empty if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { continue; } if ( name !== "toJSON" ) { return false; } } return true; } jQuery.extend({ queue: function( elem, type, data ) { var queue; if ( elem ) { type = ( type || "fx" ) + "queue"; queue = jQuery._data( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { if ( !queue || jQuery.isArray(data) ) { queue = jQuery._data( elem, type, jQuery.makeArray(data) ); } else { queue.push( data ); } } return queue || []; } }, dequeue: function( elem, type ) { type = type || "fx"; var queue = jQuery.queue( elem, type ), startLength = queue.length, fn = queue.shift(), hooks = jQuery._queueHooks( elem, type ), next = function() { jQuery.dequeue( elem, type ); }; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { fn = queue.shift(); startLength--; } if ( fn ) { // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if ( type === "fx" ) { queue.unshift( "inprogress" ); } // clear up the last queue stop function delete hooks.stop; fn.call( elem, next, hooks ); } if ( !startLength && hooks ) { hooks.empty.fire(); } }, // not intended for public consumption - generates a queueHooks object, or returns the current one _queueHooks: function( elem, type ) { var key = type + "queueHooks"; return jQuery._data( elem, key ) || jQuery._data( elem, key, { empty: jQuery.Callbacks("once memory").add(function() { jQuery._removeData( elem, type + "queue" ); jQuery._removeData( elem, key ); }) }); } }); jQuery.fn.extend({ queue: function( type, data ) { var setter = 2; if ( typeof type !== "string" ) { data = type; type = "fx"; setter--; } if ( arguments.length < setter ) { return jQuery.queue( this[0], type ); } return data === undefined ? this : this.each(function() { var queue = jQuery.queue( this, type, data ); // ensure a hooks for this queue jQuery._queueHooks( this, type ); if ( type === "fx" && queue[0] !== "inprogress" ) { jQuery.dequeue( this, type ); } }); }, dequeue: function( type ) { return this.each(function() { jQuery.dequeue( this, type ); }); }, // Based off of the plugin by Clint Helfers, with permission. // http://blindsignals.com/index.php/2009/07/jquery-delay/ delay: function( time, type ) { time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; type = type || "fx"; return this.queue( type, function( next, hooks ) { var timeout = setTimeout( next, time ); hooks.stop = function() { clearTimeout( timeout ); }; }); }, clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function( type, obj ) { var tmp, count = 1, defer = jQuery.Deferred(), elements = this, i = this.length, resolve = function() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); } }; if ( typeof type !== "string" ) { obj = type; type = undefined; } type = type || "fx"; while( i-- ) { tmp = jQuery._data( elements[ i ], type + "queueHooks" ); if ( tmp && tmp.empty ) { count++; tmp.empty.add( resolve ); } } resolve(); return defer.promise( obj ); } }); var nodeHook, boolHook, rclass = /[\t\r\n\f]/g, rreturn = /\r/g, rfocusable = /^(?:input|select|textarea|button|object)$/i, rclickable = /^(?:a|area)$/i, ruseDefault = /^(?:checked|selected)$/i, getSetAttribute = jQuery.support.getSetAttribute, getSetInput = jQuery.support.input; jQuery.fn.extend({ attr: function( name, value ) { return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); }, removeAttr: function( name ) { return this.each(function() { jQuery.removeAttr( this, name ); }); }, prop: function( name, value ) { return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); }, removeProp: function( name ) { name = jQuery.propFix[ name ] || name; return this.each(function() { // try/catch handles cases where IE balks (such as removing a property on window) try { this[ name ] = undefined; delete this[ name ]; } catch( e ) {} }); }, addClass: function( value ) { var classes, elem, cur, clazz, j, i = 0, len = this.length, proceed = typeof value === "string" && value; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { jQuery( this ).addClass( value.call( this, j, this.className ) ); }); } if ( proceed ) { // The disjunction here is for better compressibility (see removeClass) classes = ( value || "" ).match( core_rnotwhite ) || []; for ( ; i < len; i++ ) { elem = this[ i ]; cur = elem.nodeType === 1 && ( elem.className ? ( " " + elem.className + " " ).replace( rclass, " " ) : " " ); if ( cur ) { j = 0; while ( (clazz = classes[j++]) ) { if ( cur.indexOf( " " + clazz + " " ) < 0 ) { cur += clazz + " "; } } elem.className = jQuery.trim( cur ); } } } return this; }, removeClass: function( value ) { var classes, elem, cur, clazz, j, i = 0, len = this.length, proceed = arguments.length === 0 || typeof value === "string" && value; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { jQuery( this ).removeClass( value.call( this, j, this.className ) ); }); } if ( proceed ) { classes = ( value || "" ).match( core_rnotwhite ) || []; for ( ; i < len; i++ ) { elem = this[ i ]; // This expression is here for better compressibility (see addClass) cur = elem.nodeType === 1 && ( elem.className ? ( " " + elem.className + " " ).replace( rclass, " " ) : "" ); if ( cur ) { j = 0; while ( (clazz = classes[j++]) ) { // Remove *all* instances while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { cur = cur.replace( " " + clazz + " ", " " ); } } elem.className = value ? jQuery.trim( cur ) : ""; } } } return this; }, toggleClass: function( value, stateVal ) { var type = typeof value, isBool = typeof stateVal === "boolean"; if ( jQuery.isFunction( value ) ) { return this.each(function( i ) { jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); }); } return this.each(function() { if ( type === "string" ) { // toggle individual class names var className, i = 0, self = jQuery( this ), state = stateVal, classNames = value.match( core_rnotwhite ) || []; while ( (className = classNames[ i++ ]) ) { // check each className given, space separated list state = isBool ? state : !self.hasClass( className ); self[ state ? "addClass" : "removeClass" ]( className ); } // Toggle whole class name } else if ( type === core_strundefined || type === "boolean" ) { if ( this.className ) { // store className if set jQuery._data( this, "__className__", this.className ); } // If the element has a class name or if we're passed "false", // then remove the whole classname (if there was one, the above saved it). // Otherwise bring back whatever was previously saved (if anything), // falling back to the empty string if nothing was stored. this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; } }); }, hasClass: function( selector ) { var className = " " + selector + " ", i = 0, l = this.length; for ( ; i < l; i++ ) { if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { return true; } } return false; }, val: function( value ) { var ret, hooks, isFunction, elem = this[0]; if ( !arguments.length ) { if ( elem ) { hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { return ret; } ret = elem.value; return typeof ret === "string" ? // handle most common string cases ret.replace(rreturn, "") : // handle cases where value is null/undef or number ret == null ? "" : ret; } return; } isFunction = jQuery.isFunction( value ); return this.each(function( i ) { var val; if ( this.nodeType !== 1 ) { return; } if ( isFunction ) { val = value.call( this, i, jQuery( this ).val() ); } else { val = value; } // Treat null/undefined as ""; convert numbers to string if ( val == null ) { val = ""; } else if ( typeof val === "number" ) { val += ""; } else if ( jQuery.isArray( val ) ) { val = jQuery.map(val, function ( value ) { return value == null ? "" : value + ""; }); } hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; // If set returns undefined, fall back to normal setting if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { this.value = val; } }); } }); jQuery.extend({ valHooks: { option: { get: function( elem ) { // Use proper attribute retrieval(#6932, #12072) var val = jQuery.find.attr( elem, "value" ); return val != null ? val : elem.text; } }, select: { get: function( elem ) { var value, option, options = elem.options, index = elem.selectedIndex, one = elem.type === "select-one" || index < 0, values = one ? null : [], max = one ? index + 1 : options.length, i = index < 0 ? max : one ? index : 0; // Loop through all the selected options for ( ; i < max; i++ ) { option = options[ i ]; // oldIE doesn't update selected after form reset (#2551) if ( ( option.selected || i === index ) && // Don't return options that are disabled or in a disabled optgroup ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { // Get the specific value for the option value = jQuery( option ).val(); // We don't need an array for one selects if ( one ) { return value; } // Multi-Selects return an array values.push( value ); } } return values; }, set: function( elem, value ) { var optionSet, option, options = elem.options, values = jQuery.makeArray( value ), i = options.length; while ( i-- ) { option = options[ i ]; if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { optionSet = true; } } // force browsers to behave consistently when non-matching value is set if ( !optionSet ) { elem.selectedIndex = -1; } return values; } } }, attr: function( elem, name, value ) { var hooks, ret, nType = elem.nodeType; // don't get/set attributes on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } // Fallback to prop when attributes are not supported if ( typeof elem.getAttribute === core_strundefined ) { return jQuery.prop( elem, name, value ); } // All attributes are lowercase // Grab necessary hook if one is defined if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { name = name.toLowerCase(); hooks = jQuery.attrHooks[ name ] || ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); } if ( value !== undefined ) { if ( value === null ) { jQuery.removeAttr( elem, name ); } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { return ret; } else { elem.setAttribute( name, value + "" ); return value; } } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { return ret; } else { ret = jQuery.find.attr( elem, name ); // Non-existent attributes return null, we normalize to undefined return ret == null ? undefined : ret; } }, removeAttr: function( elem, value ) { var name, propName, i = 0, attrNames = value && value.match( core_rnotwhite ); if ( attrNames && elem.nodeType === 1 ) { while ( (name = attrNames[i++]) ) { propName = jQuery.propFix[ name ] || name; // Boolean attributes get special treatment (#10870) if ( jQuery.expr.match.bool.test( name ) ) { // Set corresponding property to false if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { elem[ propName ] = false; // Support: IE<9 // Also clear defaultChecked/defaultSelected (if appropriate) } else { elem[ jQuery.camelCase( "default-" + name ) ] = elem[ propName ] = false; } // See #9699 for explanation of this approach (setting first, then removal) } else { jQuery.attr( elem, name, "" ); } elem.removeAttribute( getSetAttribute ? name : propName ); } } }, attrHooks: { type: { set: function( elem, value ) { if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { // Setting the type on a radio button after the value resets the value in IE6-9 // Reset value to default in case type is set after value during creation var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } } }, propFix: { "for": "htmlFor", "class": "className" }, prop: function( elem, name, value ) { var ret, hooks, notxml, nType = elem.nodeType; // don't get/set properties on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); if ( notxml ) { // Fix name and attach hooks name = jQuery.propFix[ name ] || name; hooks = jQuery.propHooks[ name ]; } if ( value !== undefined ) { return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? ret : ( elem[ name ] = value ); } else { return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? ret : elem[ name ]; } }, propHooks: { tabIndex: { get: function( elem ) { // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ // Use proper attribute retrieval(#12072) var tabindex = jQuery.find.attr( elem, "tabindex" ); return tabindex ? parseInt( tabindex, 10 ) : rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? 0 : -1; } } } }); // Hooks for boolean attributes boolHook = { set: function( elem, value, name ) { if ( value === false ) { // Remove boolean attributes when set to false jQuery.removeAttr( elem, name ); } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { // IE<8 needs the *property* name elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); // Use defaultChecked and defaultSelected for oldIE } else { elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; } return name; } }; jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ? function( elem, name, isXML ) { var fn = jQuery.expr.attrHandle[ name ], ret = isXML ? undefined : /* jshint eqeqeq: false */ (jQuery.expr.attrHandle[ name ] = undefined) != getter( elem, name, isXML ) ? name.toLowerCase() : null; jQuery.expr.attrHandle[ name ] = fn; return ret; } : function( elem, name, isXML ) { return isXML ? undefined : elem[ jQuery.camelCase( "default-" + name ) ] ? name.toLowerCase() : null; }; }); // fix oldIE attroperties if ( !getSetInput || !getSetAttribute ) { jQuery.attrHooks.value = { set: function( elem, value, name ) { if ( jQuery.nodeName( elem, "input" ) ) { // Does not return so that setAttribute is also used elem.defaultValue = value; } else { // Use nodeHook if defined (#1954); otherwise setAttribute is fine return nodeHook && nodeHook.set( elem, value, name ); } } }; } // IE6/7 do not support getting/setting some attributes with get/setAttribute if ( !getSetAttribute ) { // Use this for any attribute in IE6/7 // This fixes almost every IE6/7 issue nodeHook = { set: function( elem, value, name ) { // Set the existing or create a new attribute node var ret = elem.getAttributeNode( name ); if ( !ret ) { elem.setAttributeNode( (ret = elem.ownerDocument.createAttribute( name )) ); } ret.value = value += ""; // Break association with cloned elements by also using setAttribute (#9646) return name === "value" || value === elem.getAttribute( name ) ? value : undefined; } }; jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords = // Some attributes are constructed with empty-string values when not defined function( elem, name, isXML ) { var ret; return isXML ? undefined : (ret = elem.getAttributeNode( name )) && ret.value !== "" ? ret.value : null; }; jQuery.valHooks.button = { get: function( elem, name ) { var ret = elem.getAttributeNode( name ); return ret && ret.specified ? ret.value : undefined; }, set: nodeHook.set }; // Set contenteditable to false on removals(#10429) // Setting to empty string throws an error as an invalid value jQuery.attrHooks.contenteditable = { set: function( elem, value, name ) { nodeHook.set( elem, value === "" ? false : value, name ); } }; // Set width and height to auto instead of 0 on empty string( Bug #8150 ) // This is for removals jQuery.each([ "width", "height" ], function( i, name ) { jQuery.attrHooks[ name ] = { set: function( elem, value ) { if ( value === "" ) { elem.setAttribute( name, "auto" ); return value; } } }; }); } // Some attributes require a special call on IE // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx if ( !jQuery.support.hrefNormalized ) { // href/src property should get the full normalized URL (#10299/#12915) jQuery.each([ "href", "src" ], function( i, name ) { jQuery.propHooks[ name ] = { get: function( elem ) { return elem.getAttribute( name, 4 ); } }; }); } if ( !jQuery.support.style ) { jQuery.attrHooks.style = { get: function( elem ) { // Return undefined in the case of empty string // Note: IE uppercases css property names, but if we were to .toLowerCase() // .cssText, that would destroy case senstitivity in URL's, like in "background" return elem.style.cssText || undefined; }, set: function( elem, value ) { return ( elem.style.cssText = value + "" ); } }; } // Safari mis-reports the default selected property of an option // Accessing the parent's selectedIndex property fixes it if ( !jQuery.support.optSelected ) { jQuery.propHooks.selected = { get: function( elem ) { var parent = elem.parentNode; if ( parent ) { parent.selectedIndex; // Make sure that it also works with optgroups, see #5701 if ( parent.parentNode ) { parent.parentNode.selectedIndex; } } return null; } }; } jQuery.each([ "tabIndex", "readOnly", "maxLength", "cellSpacing", "cellPadding", "rowSpan", "colSpan", "useMap", "frameBorder", "contentEditable" ], function() { jQuery.propFix[ this.toLowerCase() ] = this; }); // IE6/7 call enctype encoding if ( !jQuery.support.enctype ) { jQuery.propFix.enctype = "encoding"; } // Radios and checkboxes getter/setter jQuery.each([ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = { set: function( elem, value ) { if ( jQuery.isArray( value ) ) { return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); } } }; if ( !jQuery.support.checkOn ) { jQuery.valHooks[ this ].get = function( elem ) { // Support: Webkit // "" is returned instead of "on" if a value isn't specified return elem.getAttribute("value") === null ? "on" : elem.value; }; } }); var rformElems = /^(?:input|select|textarea)$/i, rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|contextmenu)|click/, rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; function returnTrue() { return true; } function returnFalse() { return false; } function safeActiveElement() { try { return document.activeElement; } catch ( err ) { } } /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { global: {}, add: function( elem, types, handler, data, selector ) { var tmp, events, t, handleObjIn, special, eventHandle, handleObj, handlers, type, namespaces, origType, elemData = jQuery._data( elem ); // Don't attach events to noData or text/comment nodes (but allow plain objects) if ( !elemData ) { return; } // Caller can pass in an object of custom data in lieu of the handler if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; } // Init the element's event structure and main handler, if this is the first if ( !(events = elemData.events) ) { events = elemData.events = {}; } if ( !(eventHandle = elemData.handle) ) { eventHandle = elemData.handle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : undefined; }; // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events eventHandle.elem = elem; } // Handle multiple events separated by a space types = ( types || "" ).match( core_rnotwhite ) || [""]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[t] ) || []; type = origType = tmp[1]; namespaces = ( tmp[2] || "" ).split( "." ).sort(); // There *must* be a type, no attaching namespace-only handlers if ( !type ) { continue; } // If event changes its type, use the special event handlers for the changed type special = jQuery.event.special[ type ] || {}; // If selector defined, determine special event api type, otherwise given type type = ( selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers handleObj = jQuery.extend({ type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join(".") }, handleObjIn ); // Init the event handler queue if we're the first if ( !(handlers = events[ type ]) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener/attachEvent if the special events handler returns false if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { // Bind the global event handler to the element if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); } else if ( elem.attachEvent ) { elem.attachEvent( "on" + type, eventHandle ); } } } if ( special.add ) { special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } // Add to the element's handler list, delegates in front if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } // Keep track of which events have ever been used, for event optimization jQuery.event.global[ type ] = true; } // Nullify elem to prevent memory leaks in IE elem = null; }, // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { var j, handleObj, tmp, origCount, t, events, special, handlers, type, namespaces, origType, elemData = jQuery.hasData( elem ) && jQuery._data( elem ); if ( !elemData || !(events = elemData.events) ) { return; } // Once for each type.namespace in types; type may be omitted types = ( types || "" ).match( core_rnotwhite ) || [""]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[t] ) || []; type = origType = tmp[1]; namespaces = ( tmp[2] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element if ( !type ) { for ( type in events ) { jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); } continue; } special = jQuery.event.special[ type ] || {}; type = ( selector ? special.delegateType : special.bindType ) || type; handlers = events[ type ] || []; tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); // Remove matching events origCount = j = handlers.length; while ( j-- ) { handleObj = handlers[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && ( !tmp || tmp.test( handleObj.namespace ) ) && ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { handlers.splice( j, 1 ); if ( handleObj.selector ) { handlers.delegateCount--; } if ( special.remove ) { special.remove.call( elem, handleObj ); } } } // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( origCount && !handlers.length ) { if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { jQuery.removeEvent( elem, type, elemData.handle ); } delete events[ type ]; } } // Remove the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { delete elemData.handle; // removeData also checks for emptiness and clears the expando if empty // so use it instead of delete jQuery._removeData( elem, "events" ); } }, trigger: function( event, data, elem, onlyHandlers ) { var handle, ontype, cur, bubbleType, special, tmp, i, eventPath = [ elem || document ], type = core_hasOwn.call( event, "type" ) ? event.type : event, namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; cur = tmp = elem = elem || document; // Don't do events on text and comment nodes if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return; } // focus/blur morphs to focusin/out; ensure we're not firing them right now if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } if ( type.indexOf(".") >= 0 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split("."); type = namespaces.shift(); namespaces.sort(); } ontype = type.indexOf(":") < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string event = event[ jQuery.expando ] ? event : new jQuery.Event( type, typeof event === "object" && event ); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) event.isTrigger = onlyHandlers ? 2 : 3; event.namespace = namespaces.join("."); event.namespace_re = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : null; // Clean up the event in case it is being reused event.result = undefined; if ( !event.target ) { event.target = elem; } // Clone any incoming data and prepend the event, creating the handler arg list data = data == null ? [ event ] : jQuery.makeArray( data, [ event ] ); // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; } // Determine event propagation path in advance, per W3C events spec (#9951) // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type; if ( !rfocusMorph.test( bubbleType + type ) ) { cur = cur.parentNode; } for ( ; cur; cur = cur.parentNode ) { eventPath.push( cur ); tmp = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( tmp === (elem.ownerDocument || document) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } // Fire handlers on the event path i = 0; while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { event.type = i > 1 ? bubbleType : special.bindType || type; // jQuery handler handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } // Native handler handle = ontype && cur[ ontype ]; if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { event.preventDefault(); } } event.type = type; // If nobody prevented the default action, do it now if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && jQuery.acceptData( elem ) ) { // Call a native DOM method on the target with the same name name as the event. // Can't use an .isFunction() check here because IE6/7 fails that test. // Don't do default actions on window, that's where global variables be (#6170) if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { // Don't re-trigger an onFOO event when we call its FOO() method tmp = elem[ ontype ]; if ( tmp ) { elem[ ontype ] = null; } // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; try { elem[ type ](); } catch ( e ) { // IE<9 dies on focus/blur to hidden element (#1486,#12518) // only reproducible on winXP IE8 native, not IE9 in IE8 mode } jQuery.event.triggered = undefined; if ( tmp ) { elem[ ontype ] = tmp; } } } } return event.result; }, dispatch: function( event ) { // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event ); var i, ret, handleObj, matched, j, handlerQueue = [], args = core_slice.call( arguments ), handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[0] = event; event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { return; } // Determine handlers handlerQueue = jQuery.event.handlers.call( this, event, handlers ); // Run delegates first; they may want to stop propagation beneath us i = 0; while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { event.currentTarget = matched.elem; j = 0; while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { // Triggered event must either 1) have no namespace, or // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { event.handleObj = handleObj; event.data = handleObj.data; ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) .apply( matched.elem, args ); if ( ret !== undefined ) { if ( (event.result = ret) === false ) { event.preventDefault(); event.stopPropagation(); } } } } } // Call the postDispatch hook for the mapped type if ( special.postDispatch ) { special.postDispatch.call( this, event ); } return event.result; }, handlers: function( event, handlers ) { var sel, handleObj, matches, i, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target; // Find delegate handlers // Black-hole SVG instance trees (#13180) // Avoid non-left-click bubbling in Firefox (#3861) if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { /* jshint eqeqeq: false */ for ( ; cur != this; cur = cur.parentNode || this ) { /* jshint eqeqeq: true */ // Don't check non-elements (#13208) // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { matches = []; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203) sel = handleObj.selector + " "; if ( matches[ sel ] === undefined ) { matches[ sel ] = handleObj.needsContext ? jQuery( sel, this ).index( cur ) >= 0 : jQuery.find( sel, this, null, [ cur ] ).length; } if ( matches[ sel ] ) { matches.push( handleObj ); } } if ( matches.length ) { handlerQueue.push({ elem: cur, handlers: matches }); } } } } // Add the remaining (directly-bound) handlers if ( delegateCount < handlers.length ) { handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); } return handlerQueue; }, fix: function( event ) { if ( event[ jQuery.expando ] ) { return event; } // Create a writable copy of the event object and normalize some properties var i, prop, copy, type = event.type, originalEvent = event, fixHook = this.fixHooks[ type ]; if ( !fixHook ) { this.fixHooks[ type ] = fixHook = rmouseEvent.test( type ) ? this.mouseHooks : rkeyEvent.test( type ) ? this.keyHooks : {}; } copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; event = new jQuery.Event( originalEvent ); i = copy.length; while ( i-- ) { prop = copy[ i ]; event[ prop ] = originalEvent[ prop ]; } // Support: IE<9 // Fix target property (#1925) if ( !event.target ) { event.target = originalEvent.srcElement || document; } // Support: Chrome 23+, Safari? // Target should not be a text node (#504, #13143) if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; } // Support: IE<9 // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) event.metaKey = !!event.metaKey; return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; }, // Includes some event props shared by KeyEvent and MouseEvent props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), fixHooks: {}, keyHooks: { props: "char charCode key keyCode".split(" "), filter: function( event, original ) { // Add which for key events if ( event.which == null ) { event.which = original.charCode != null ? original.charCode : original.keyCode; } return event; } }, mouseHooks: { props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), filter: function( event, original ) { var body, eventDoc, doc, button = original.button, fromElement = original.fromElement; // Calculate pageX/Y if missing and clientX/Y available if ( event.pageX == null && original.clientX != null ) { eventDoc = event.target.ownerDocument || document; doc = eventDoc.documentElement; body = eventDoc.body; event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); } // Add relatedTarget, if necessary if ( !event.relatedTarget && fromElement ) { event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; } // Add which for click: 1 === left; 2 === middle; 3 === right // Note: button is not normalized, so don't use it if ( !event.which && button !== undefined ) { event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); } return event; } }, special: { load: { // Prevent triggered image.load events from bubbling to window.load noBubble: true }, focus: { // Fire native event if possible so blur/focus sequence is correct trigger: function() { if ( this !== safeActiveElement() && this.focus ) { try { this.focus(); return false; } catch ( e ) { // Support: IE<9 // If we error on focus to hidden element (#1486, #12518), // let .trigger() run the handlers } } }, delegateType: "focusin" }, blur: { trigger: function() { if ( this === safeActiveElement() && this.blur ) { this.blur(); return false; } }, delegateType: "focusout" }, click: { // For checkbox, fire native event so checked state will be right trigger: function() { if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { this.click(); return false; } }, // For cross-browser consistency, don't fire native .click() on links _default: function( event ) { return jQuery.nodeName( event.target, "a" ); } }, beforeunload: { postDispatch: function( event ) { // Even when returnValue equals to undefined Firefox will still show alert if ( event.result !== undefined ) { event.originalEvent.returnValue = event.result; } } } }, simulate: function( type, elem, event, bubble ) { // Piggyback on a donor event to simulate a different one. // Fake originalEvent to avoid donor's stopPropagation, but if the // simulated event prevents default then we do the same on the donor. var e = jQuery.extend( new jQuery.Event(), event, { type: type, isSimulated: true, originalEvent: {} } ); if ( bubble ) { jQuery.event.trigger( e, null, elem ); } else { jQuery.event.dispatch.call( elem, e ); } if ( e.isDefaultPrevented() ) { event.preventDefault(); } } }; jQuery.removeEvent = document.removeEventListener ? function( elem, type, handle ) { if ( elem.removeEventListener ) { elem.removeEventListener( type, handle, false ); } } : function( elem, type, handle ) { var name = "on" + type; if ( elem.detachEvent ) { // #8545, #7054, preventing memory leaks for custom events in IE6-8 // detachEvent needed property on element, by name of that event, to properly expose it to GC if ( typeof elem[ name ] === core_strundefined ) { elem[ name ] = null; } elem.detachEvent( name, handle ); } }; jQuery.Event = function( src, props ) { // Allow instantiation without the 'new' keyword if ( !(this instanceof jQuery.Event) ) { return new jQuery.Event( src, props ); } // Event object if ( src && src.type ) { this.originalEvent = src; this.type = src.type; // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; // Event type } else { this.type = src; } // Put explicitly provided properties onto the event object if ( props ) { jQuery.extend( this, props ); } // Create a timestamp if incoming event doesn't have one this.timeStamp = src && src.timeStamp || jQuery.now(); // Mark it as fixed this[ jQuery.expando ] = true; }; // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, preventDefault: function() { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; if ( !e ) { return; } // If preventDefault exists, run it on the original event if ( e.preventDefault ) { e.preventDefault(); // Support: IE // Otherwise set the returnValue property of the original event to false } else { e.returnValue = false; } }, stopPropagation: function() { var e = this.originalEvent; this.isPropagationStopped = returnTrue; if ( !e ) { return; } // If stopPropagation exists, run it on the original event if ( e.stopPropagation ) { e.stopPropagation(); } // Support: IE // Set the cancelBubble property of the original event to true e.cancelBubble = true; }, stopImmediatePropagation: function() { this.isImmediatePropagationStopped = returnTrue; this.stopPropagation(); } }; // Create mouseenter/leave events using mouseover/out and event-time checks jQuery.each({ mouseenter: "mouseover", mouseleave: "mouseout" }, function( orig, fix ) { jQuery.event.special[ orig ] = { delegateType: fix, bindType: fix, handle: function( event ) { var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj; // For mousenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if ( !related || (related !== target && !jQuery.contains( target, related )) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; } return ret; } }; }); // IE submit delegation if ( !jQuery.support.submitBubbles ) { jQuery.event.special.submit = { setup: function() { // Only need this for delegated form submit events if ( jQuery.nodeName( this, "form" ) ) { return false; } // Lazy-add a submit handler when a descendant form may potentially be submitted jQuery.event.add( this, "click._submit keypress._submit", function( e ) { // Node name check avoids a VML-related crash in IE (#9807) var elem = e.target, form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; if ( form && !jQuery._data( form, "submitBubbles" ) ) { jQuery.event.add( form, "submit._submit", function( event ) { event._submit_bubble = true; }); jQuery._data( form, "submitBubbles", true ); } }); // return undefined since we don't need an event listener }, postDispatch: function( event ) { // If form was submitted by the user, bubble the event up the tree if ( event._submit_bubble ) { delete event._submit_bubble; if ( this.parentNode && !event.isTrigger ) { jQuery.event.simulate( "submit", this.parentNode, event, true ); } } }, teardown: function() { // Only need this for delegated form submit events if ( jQuery.nodeName( this, "form" ) ) { return false; } // Remove delegated handlers; cleanData eventually reaps submit handlers attached above jQuery.event.remove( this, "._submit" ); } }; } // IE change delegation and checkbox/radio fix if ( !jQuery.support.changeBubbles ) { jQuery.event.special.change = { setup: function() { if ( rformElems.test( this.nodeName ) ) { // IE doesn't fire change on a check/radio until blur; trigger it on click // after a propertychange. Eat the blur-change in special.change.handle. // This still fires onchange a second time for check/radio after blur. if ( this.type === "checkbox" || this.type === "radio" ) { jQuery.event.add( this, "propertychange._change", function( event ) { if ( event.originalEvent.propertyName === "checked" ) { this._just_changed = true; } }); jQuery.event.add( this, "click._change", function( event ) { if ( this._just_changed && !event.isTrigger ) { this._just_changed = false; } // Allow triggered, simulated change events (#11500) jQuery.event.simulate( "change", this, event, true ); }); } return false; } // Delegated event; lazy-add a change handler on descendant inputs jQuery.event.add( this, "beforeactivate._change", function( e ) { var elem = e.target; if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { jQuery.event.add( elem, "change._change", function( event ) { if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { jQuery.event.simulate( "change", this.parentNode, event, true ); } }); jQuery._data( elem, "changeBubbles", true ); } }); }, handle: function( event ) { var elem = event.target; // Swallow native change events from checkbox/radio, we already triggered them above if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { return event.handleObj.handler.apply( this, arguments ); } }, teardown: function() { jQuery.event.remove( this, "._change" ); return !rformElems.test( this.nodeName ); } }; } // Create "bubbling" focus and blur events if ( !jQuery.support.focusinBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler while someone wants focusin/focusout var attaches = 0, handler = function( event ) { jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); }; jQuery.event.special[ fix ] = { setup: function() { if ( attaches++ === 0 ) { document.addEventListener( orig, handler, true ); } }, teardown: function() { if ( --attaches === 0 ) { document.removeEventListener( orig, handler, true ); } } }; }); } jQuery.fn.extend({ on: function( types, selector, data, fn, /*INTERNAL*/ one ) { var type, origFn; // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) if ( typeof selector !== "string" ) { // ( types-Object, data ) data = data || selector; selector = undefined; } for ( type in types ) { this.on( type, selector, data, types[ type ], one ); } return this; } if ( data == null && fn == null ) { // ( types, fn ) fn = selector; data = selector = undefined; } else if ( fn == null ) { if ( typeof selector === "string" ) { // ( types, selector, fn ) fn = data; data = undefined; } else { // ( types, data, fn ) fn = data; data = selector; selector = undefined; } } if ( fn === false ) { fn = returnFalse; } else if ( !fn ) { return this; } if ( one === 1 ) { origFn = fn; fn = function( event ) { // Can use an empty set, since event contains the info jQuery().off( event ); return origFn.apply( this, arguments ); }; // Use same guid so caller can remove using origFn fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } return this.each( function() { jQuery.event.add( this, types, fn, data, selector ); }); }, one: function( types, selector, data, fn ) { return this.on( types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { var handleObj, type; if ( types && types.preventDefault && types.handleObj ) { // ( event ) dispatched jQuery.Event handleObj = types.handleObj; jQuery( types.delegateTarget ).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) { // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); } return this; } if ( selector === false || typeof selector === "function" ) { // ( types [, fn] ) fn = selector; selector = undefined; } if ( fn === false ) { fn = returnFalse; } return this.each(function() { jQuery.event.remove( this, types, fn, selector ); }); }, trigger: function( type, data ) { return this.each(function() { jQuery.event.trigger( type, data, this ); }); }, triggerHandler: function( type, data ) { var elem = this[0]; if ( elem ) { return jQuery.event.trigger( type, data, elem, true ); } } }); var isSimple = /^.[^:#\[\.,]*$/, rparentsprev = /^(?:parents|prev(?:Until|All))/, rneedsContext = jQuery.expr.match.needsContext, // methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, next: true, prev: true }; jQuery.fn.extend({ find: function( selector ) { var i, ret = [], self = this, len = self.length; if ( typeof selector !== "string" ) { return this.pushStack( jQuery( selector ).filter(function() { for ( i = 0; i < len; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } } }) ); } for ( i = 0; i < len; i++ ) { jQuery.find( selector, self[ i ], ret ); } // Needed because $( selector, context ) becomes $( context ).find( selector ) ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); ret.selector = this.selector ? this.selector + " " + selector : selector; return ret; }, has: function( target ) { var i, targets = jQuery( target, this ), len = targets.length; return this.filter(function() { for ( i = 0; i < len; i++ ) { if ( jQuery.contains( this, targets[i] ) ) { return true; } } }); }, not: function( selector ) { return this.pushStack( winnow(this, selector || [], true) ); }, filter: function( selector ) { return this.pushStack( winnow(this, selector || [], false) ); }, is: function( selector ) { return !!winnow( this, // If this is a positional/relative selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". typeof selector === "string" && rneedsContext.test( selector ) ? jQuery( selector ) : selector || [], false ).length; }, closest: function( selectors, context ) { var cur, i = 0, l = this.length, ret = [], pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? jQuery( selectors, context || this.context ) : 0; for ( ; i < l; i++ ) { for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { // Always skip document fragments if ( cur.nodeType < 11 && (pos ? pos.index(cur) > -1 : // Don't pass non-elements to Sizzle cur.nodeType === 1 && jQuery.find.matchesSelector(cur, selectors)) ) { cur = ret.push( cur ); break; } } } return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); }, // Determine the position of an element within // the matched set of elements index: function( elem ) { // No argument, return index in parent if ( !elem ) { return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; } // index in selector if ( typeof elem === "string" ) { return jQuery.inArray( this[0], jQuery( elem ) ); } // Locate the position of the desired element return jQuery.inArray( // If it receives a jQuery object, the first element is used elem.jquery ? elem[0] : elem, this ); }, add: function( selector, context ) { var set = typeof selector === "string" ? jQuery( selector, context ) : jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), all = jQuery.merge( this.get(), set ); return this.pushStack( jQuery.unique(all) ); }, addBack: function( selector ) { return this.add( selector == null ? this.prevObject : this.prevObject.filter(selector) ); } }); function sibling( cur, dir ) { do { cur = cur[ dir ]; } while ( cur && cur.nodeType !== 1 ); return cur; } jQuery.each({ parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function( elem ) { return jQuery.dir( elem, "parentNode" ); }, parentsUntil: function( elem, i, until ) { return jQuery.dir( elem, "parentNode", until ); }, next: function( elem ) { return sibling( elem, "nextSibling" ); }, prev: function( elem ) { return sibling( elem, "previousSibling" ); }, nextAll: function( elem ) { return jQuery.dir( elem, "nextSibling" ); }, prevAll: function( elem ) { return jQuery.dir( elem, "previousSibling" ); }, nextUntil: function( elem, i, until ) { return jQuery.dir( elem, "nextSibling", until ); }, prevUntil: function( elem, i, until ) { return jQuery.dir( elem, "previousSibling", until ); }, siblings: function( elem ) { return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); }, children: function( elem ) { return jQuery.sibling( elem.firstChild ); }, contents: function( elem ) { return jQuery.nodeName( elem, "iframe" ) ? elem.contentDocument || elem.contentWindow.document : jQuery.merge( [], elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { var ret = jQuery.map( this, fn, until ); if ( name.slice( -5 ) !== "Until" ) { selector = until; } if ( selector && typeof selector === "string" ) { ret = jQuery.filter( selector, ret ); } if ( this.length > 1 ) { // Remove duplicates if ( !guaranteedUnique[ name ] ) { ret = jQuery.unique( ret ); } // Reverse order for parents* and prev-derivatives if ( rparentsprev.test( name ) ) { ret = ret.reverse(); } } return this.pushStack( ret ); }; }); jQuery.extend({ filter: function( expr, elems, not ) { var elem = elems[ 0 ]; if ( not ) { expr = ":not(" + expr + ")"; } return elems.length === 1 && elem.nodeType === 1 ? jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { return elem.nodeType === 1; })); }, dir: function( elem, dir, until ) { var matched = [], cur = elem[ dir ]; while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { if ( cur.nodeType === 1 ) { matched.push( cur ); } cur = cur[dir]; } return matched; }, sibling: function( n, elem ) { var r = []; for ( ; n; n = n.nextSibling ) { if ( n.nodeType === 1 && n !== elem ) { r.push( n ); } } return r; } }); // Implement the identical functionality for filter and not function winnow( elements, qualifier, not ) { if ( jQuery.isFunction( qualifier ) ) { return jQuery.grep( elements, function( elem, i ) { /* jshint -W018 */ return !!qualifier.call( elem, i, elem ) !== not; }); } if ( qualifier.nodeType ) { return jQuery.grep( elements, function( elem ) { return ( elem === qualifier ) !== not; }); } if ( typeof qualifier === "string" ) { if ( isSimple.test( qualifier ) ) { return jQuery.filter( qualifier, elements, not ); } qualifier = jQuery.filter( qualifier, elements ); } return jQuery.grep( elements, function( elem ) { return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; }); } function createSafeFragment( document ) { var list = nodeNames.split( "|" ), safeFrag = document.createDocumentFragment(); if ( safeFrag.createElement ) { while ( list.length ) { safeFrag.createElement( list.pop() ); } } return safeFrag; } var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), rleadingWhitespace = /^\s+/, rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, rtagName = /<([\w:]+)/, rtbody = /\s*$/g, // We have to close these tags to support XHTML (#13200) wrapMap = { option: [ 1, "" ], legend: [ 1, "
      ", "
      " ], area: [ 1, "", "" ], param: [ 1, "", "" ], thead: [ 1, "", "
      " ], tr: [ 2, "", "
      " ], col: [ 2, "", "
      " ], td: [ 3, "", "
      " ], // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, // unless wrapped in a div with non-breaking characters in front of it. _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
      ", "
      " ] }, safeFragment = createSafeFragment( document ), fragmentDiv = safeFragment.appendChild( document.createElement("div") ); wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; jQuery.fn.extend({ text: function( value ) { return jQuery.access( this, function( value ) { return value === undefined ? jQuery.text( this ) : this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); }, null, value, arguments.length ); }, append: function() { return this.domManip( arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.appendChild( elem ); } }); }, prepend: function() { return this.domManip( arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.insertBefore( elem, target.firstChild ); } }); }, before: function() { return this.domManip( arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this ); } }); }, after: function() { return this.domManip( arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this.nextSibling ); } }); }, // keepData is for internal use only--do not document remove: function( selector, keepData ) { var elem, elems = selector ? jQuery.filter( selector, this ) : this, i = 0; for ( ; (elem = elems[i]) != null; i++ ) { if ( !keepData && elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem ) ); } if ( elem.parentNode ) { if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { setGlobalEval( getAll( elem, "script" ) ); } elem.parentNode.removeChild( elem ); } } return this; }, empty: function() { var elem, i = 0; for ( ; (elem = this[i]) != null; i++ ) { // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); } // Remove any remaining nodes while ( elem.firstChild ) { elem.removeChild( elem.firstChild ); } // If this is a select, ensure that it displays empty (#12336) // Support: IE<9 if ( elem.options && jQuery.nodeName( elem, "select" ) ) { elem.options.length = 0; } } return this; }, clone: function( dataAndEvents, deepDataAndEvents ) { dataAndEvents = dataAndEvents == null ? false : dataAndEvents; deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; return this.map( function () { return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); }); }, html: function( value ) { return jQuery.access( this, function( value ) { var elem = this[0] || {}, i = 0, l = this.length; if ( value === undefined ) { return elem.nodeType === 1 ? elem.innerHTML.replace( rinlinejQuery, "" ) : undefined; } // See if we can take a shortcut and just use innerHTML if ( typeof value === "string" && !rnoInnerhtml.test( value ) && ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { value = value.replace( rxhtmlTag, "<$1>" ); try { for (; i < l; i++ ) { // Remove element nodes and prevent memory leaks elem = this[i] || {}; if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; } } elem = 0; // If using innerHTML throws an exception, use the fallback method } catch(e) {} } if ( elem ) { this.empty().append( value ); } }, null, value, arguments.length ); }, replaceWith: function() { var // Snapshot the DOM in case .domManip sweeps something relevant into its fragment args = jQuery.map( this, function( elem ) { return [ elem.nextSibling, elem.parentNode ]; }), i = 0; // Make the changes, replacing each context element with the new content this.domManip( arguments, function( elem ) { var next = args[ i++ ], parent = args[ i++ ]; if ( parent ) { // Don't use the snapshot next if it has moved (#13810) if ( next && next.parentNode !== parent ) { next = this.nextSibling; } jQuery( this ).remove(); parent.insertBefore( elem, next ); } // Allow new content to include elements from the context set }, true ); // Force removal if there was no new content (e.g., from empty arguments) return i ? this : this.remove(); }, detach: function( selector ) { return this.remove( selector, true ); }, domManip: function( args, callback, allowIntersection ) { // Flatten any nested arrays args = core_concat.apply( [], args ); var first, node, hasScripts, scripts, doc, fragment, i = 0, l = this.length, set = this, iNoClone = l - 1, value = args[0], isFunction = jQuery.isFunction( value ); // We can't cloneNode fragments that contain checked, in WebKit if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { return this.each(function( index ) { var self = set.eq( index ); if ( isFunction ) { args[0] = value.call( this, index, self.html() ); } self.domManip( args, callback, allowIntersection ); }); } if ( l ) { fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this ); first = fragment.firstChild; if ( fragment.childNodes.length === 1 ) { fragment = first; } if ( first ) { scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); hasScripts = scripts.length; // Use the original fragment for the last item instead of the first because it can end up // being emptied incorrectly in certain situations (#8070). for ( ; i < l; i++ ) { node = fragment; if ( i !== iNoClone ) { node = jQuery.clone( node, true, true ); // Keep references to cloned scripts for later restoration if ( hasScripts ) { jQuery.merge( scripts, getAll( node, "script" ) ); } } callback.call( this[i], node, i ); } if ( hasScripts ) { doc = scripts[ scripts.length - 1 ].ownerDocument; // Reenable scripts jQuery.map( scripts, restoreScript ); // Evaluate executable scripts on first document insertion for ( i = 0; i < hasScripts; i++ ) { node = scripts[ i ]; if ( rscriptType.test( node.type || "" ) && !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { if ( node.src ) { // Hope ajax is available... jQuery._evalUrl( node.src ); } else { jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); } } } } // Fix #11809: Avoid leaking memory fragment = first = null; } } return this; } }); // Support: IE<8 // Manipulating tables requires a tbody function manipulationTarget( elem, content ) { return jQuery.nodeName( elem, "table" ) && jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ? elem.getElementsByTagName("tbody")[0] || elem.appendChild( elem.ownerDocument.createElement("tbody") ) : elem; } // Replace/restore the type attribute of script elements for safe DOM manipulation function disableScript( elem ) { elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; return elem; } function restoreScript( elem ) { var match = rscriptTypeMasked.exec( elem.type ); if ( match ) { elem.type = match[1]; } else { elem.removeAttribute("type"); } return elem; } // Mark scripts as having already been evaluated function setGlobalEval( elems, refElements ) { var elem, i = 0; for ( ; (elem = elems[i]) != null; i++ ) { jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); } } function cloneCopyEvent( src, dest ) { if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { return; } var type, i, l, oldData = jQuery._data( src ), curData = jQuery._data( dest, oldData ), events = oldData.events; if ( events ) { delete curData.handle; curData.events = {}; for ( type in events ) { for ( i = 0, l = events[ type ].length; i < l; i++ ) { jQuery.event.add( dest, type, events[ type ][ i ] ); } } } // make the cloned public data object a copy from the original if ( curData.data ) { curData.data = jQuery.extend( {}, curData.data ); } } function fixCloneNodeIssues( src, dest ) { var nodeName, e, data; // We do not need to do anything for non-Elements if ( dest.nodeType !== 1 ) { return; } nodeName = dest.nodeName.toLowerCase(); // IE6-8 copies events bound via attachEvent when using cloneNode. if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { data = jQuery._data( dest ); for ( e in data.events ) { jQuery.removeEvent( dest, e, data.handle ); } // Event data gets referenced instead of copied if the expando gets copied too dest.removeAttribute( jQuery.expando ); } // IE blanks contents when cloning scripts, and tries to evaluate newly-set text if ( nodeName === "script" && dest.text !== src.text ) { disableScript( dest ).text = src.text; restoreScript( dest ); // IE6-10 improperly clones children of object elements using classid. // IE10 throws NoModificationAllowedError if parent is null, #12132. } else if ( nodeName === "object" ) { if ( dest.parentNode ) { dest.outerHTML = src.outerHTML; } // This path appears unavoidable for IE9. When cloning an object // element in IE9, the outerHTML strategy above is not sufficient. // If the src has innerHTML and the destination does not, // copy the src.innerHTML into the dest.innerHTML. #10324 if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { dest.innerHTML = src.innerHTML; } } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { // IE6-8 fails to persist the checked state of a cloned checkbox // or radio button. Worse, IE6-7 fail to give the cloned element // a checked appearance if the defaultChecked value isn't also set dest.defaultChecked = dest.checked = src.checked; // IE6-7 get confused and end up setting the value of a cloned // checkbox/radio button to an empty string instead of "on" if ( dest.value !== src.value ) { dest.value = src.value; } // IE6-8 fails to return the selected option to the default selected // state when cloning options } else if ( nodeName === "option" ) { dest.defaultSelected = dest.selected = src.defaultSelected; // IE6-8 fails to set the defaultValue to the correct value when // cloning other types of input fields } else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; } } jQuery.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function( name, original ) { jQuery.fn[ name ] = function( selector ) { var elems, i = 0, ret = [], insert = jQuery( selector ), last = insert.length - 1; for ( ; i <= last; i++ ) { elems = i === last ? this : this.clone(true); jQuery( insert[i] )[ original ]( elems ); // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() core_push.apply( ret, elems.get() ); } return this.pushStack( ret ); }; }); function getAll( context, tag ) { var elems, elem, i = 0, found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : undefined; if ( !found ) { for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { if ( !tag || jQuery.nodeName( elem, tag ) ) { found.push( elem ); } else { jQuery.merge( found, getAll( elem, tag ) ); } } } return tag === undefined || tag && jQuery.nodeName( context, tag ) ? jQuery.merge( [ context ], found ) : found; } // Used in buildFragment, fixes the defaultChecked property function fixDefaultChecked( elem ) { if ( manipulation_rcheckableType.test( elem.type ) ) { elem.defaultChecked = elem.checked; } } jQuery.extend({ clone: function( elem, dataAndEvents, deepDataAndEvents ) { var destElements, node, clone, i, srcElements, inPage = jQuery.contains( elem.ownerDocument, elem ); if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { clone = elem.cloneNode( true ); // IE<=8 does not properly clone detached, unknown element nodes } else { fragmentDiv.innerHTML = elem.outerHTML; fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); } if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); // Fix all IE cloning issues for ( i = 0; (node = srcElements[i]) != null; ++i ) { // Ensure that the destination node is not null; Fixes #9587 if ( destElements[i] ) { fixCloneNodeIssues( node, destElements[i] ); } } } // Copy the events from the original to the clone if ( dataAndEvents ) { if ( deepDataAndEvents ) { srcElements = srcElements || getAll( elem ); destElements = destElements || getAll( clone ); for ( i = 0; (node = srcElements[i]) != null; i++ ) { cloneCopyEvent( node, destElements[i] ); } } else { cloneCopyEvent( elem, clone ); } } // Preserve script evaluation history destElements = getAll( clone, "script" ); if ( destElements.length > 0 ) { setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); } destElements = srcElements = node = null; // Return the cloned set return clone; }, buildFragment: function( elems, context, scripts, selection ) { var j, elem, contains, tmp, tag, tbody, wrap, l = elems.length, // Ensure a safe fragment safe = createSafeFragment( context ), nodes = [], i = 0; for ( ; i < l; i++ ) { elem = elems[ i ]; if ( elem || elem === 0 ) { // Add nodes directly if ( jQuery.type( elem ) === "object" ) { jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); // Convert non-html into a text node } else if ( !rhtml.test( elem ) ) { nodes.push( context.createTextNode( elem ) ); // Convert html into DOM nodes } else { tmp = tmp || safe.appendChild( context.createElement("div") ); // Deserialize a standard representation tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); wrap = wrapMap[ tag ] || wrapMap._default; tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; // Descend through wrappers to the right content j = wrap[0]; while ( j-- ) { tmp = tmp.lastChild; } // Manually add leading whitespace removed by IE if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); } // Remove IE's autoinserted from table fragments if ( !jQuery.support.tbody ) { // String was a , *may* have spurious elem = tag === "table" && !rtbody.test( elem ) ? tmp.firstChild : // String was a bare or wrap[1] === "
      " && !rtbody.test( elem ) ? tmp : 0; j = elem && elem.childNodes.length; while ( j-- ) { if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { elem.removeChild( tbody ); } } } jQuery.merge( nodes, tmp.childNodes ); // Fix #12392 for WebKit and IE > 9 tmp.textContent = ""; // Fix #12392 for oldIE while ( tmp.firstChild ) { tmp.removeChild( tmp.firstChild ); } // Remember the top-level container for proper cleanup tmp = safe.lastChild; } } } // Fix #11356: Clear elements from fragment if ( tmp ) { safe.removeChild( tmp ); } // Reset defaultChecked for any radios and checkboxes // about to be appended to the DOM in IE 6/7 (#8060) if ( !jQuery.support.appendChecked ) { jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); } i = 0; while ( (elem = nodes[ i++ ]) ) { // #4087 - If origin and destination elements are the same, and this is // that element, do not do anything if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { continue; } contains = jQuery.contains( elem.ownerDocument, elem ); // Append to fragment tmp = getAll( safe.appendChild( elem ), "script" ); // Preserve script evaluation history if ( contains ) { setGlobalEval( tmp ); } // Capture executables if ( scripts ) { j = 0; while ( (elem = tmp[ j++ ]) ) { if ( rscriptType.test( elem.type || "" ) ) { scripts.push( elem ); } } } } tmp = null; return safe; }, cleanData: function( elems, /* internal */ acceptData ) { var elem, type, id, data, i = 0, internalKey = jQuery.expando, cache = jQuery.cache, deleteExpando = jQuery.support.deleteExpando, special = jQuery.event.special; for ( ; (elem = elems[i]) != null; i++ ) { if ( acceptData || jQuery.acceptData( elem ) ) { id = elem[ internalKey ]; data = id && cache[ id ]; if ( data ) { if ( data.events ) { for ( type in data.events ) { if ( special[ type ] ) { jQuery.event.remove( elem, type ); // This is a shortcut to avoid jQuery.event.remove's overhead } else { jQuery.removeEvent( elem, type, data.handle ); } } } // Remove cache only if it was not already removed by jQuery.event.remove if ( cache[ id ] ) { delete cache[ id ]; // IE does not allow us to delete expando properties from nodes, // nor does it have a removeAttribute function on Document nodes; // we must handle all of these cases if ( deleteExpando ) { delete elem[ internalKey ]; } else if ( typeof elem.removeAttribute !== core_strundefined ) { elem.removeAttribute( internalKey ); } else { elem[ internalKey ] = null; } core_deletedIds.push( id ); } } } } }, _evalUrl: function( url ) { return jQuery.ajax({ url: url, type: "GET", dataType: "script", async: false, global: false, "throws": true }); } }); jQuery.fn.extend({ wrapAll: function( html ) { if ( jQuery.isFunction( html ) ) { return this.each(function(i) { jQuery(this).wrapAll( html.call(this, i) ); }); } if ( this[0] ) { // The elements to wrap the target around var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); if ( this[0].parentNode ) { wrap.insertBefore( this[0] ); } wrap.map(function() { var elem = this; while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { elem = elem.firstChild; } return elem; }).append( this ); } return this; }, wrapInner: function( html ) { if ( jQuery.isFunction( html ) ) { return this.each(function(i) { jQuery(this).wrapInner( html.call(this, i) ); }); } return this.each(function() { var self = jQuery( this ), contents = self.contents(); if ( contents.length ) { contents.wrapAll( html ); } else { self.append( html ); } }); }, wrap: function( html ) { var isFunction = jQuery.isFunction( html ); return this.each(function(i) { jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); }); }, unwrap: function() { return this.parent().each(function() { if ( !jQuery.nodeName( this, "body" ) ) { jQuery( this ).replaceWith( this.childNodes ); } }).end(); } }); var iframe, getStyles, curCSS, ralpha = /alpha\([^)]*\)/i, ropacity = /opacity\s*=\s*([^)]*)/, rposition = /^(top|right|bottom|left)$/, // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display rdisplayswap = /^(none|table(?!-c[ea]).+)/, rmargin = /^margin/, rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), elemdisplay = { BODY: "block" }, cssShow = { position: "absolute", visibility: "hidden", display: "block" }, cssNormalTransform = { letterSpacing: 0, fontWeight: 400 }, cssExpand = [ "Top", "Right", "Bottom", "Left" ], cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; // return a css property mapped to a potentially vendor prefixed property function vendorPropName( style, name ) { // shortcut for names that are not vendor prefixed if ( name in style ) { return name; } // check for vendor prefixed names var capName = name.charAt(0).toUpperCase() + name.slice(1), origName = name, i = cssPrefixes.length; while ( i-- ) { name = cssPrefixes[ i ] + capName; if ( name in style ) { return name; } } return origName; } function isHidden( elem, el ) { // isHidden might be called from jQuery#filter function; // in that case, element will be second argument elem = el || elem; return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); } function showHide( elements, show ) { var display, elem, hidden, values = [], index = 0, length = elements.length; for ( ; index < length; index++ ) { elem = elements[ index ]; if ( !elem.style ) { continue; } values[ index ] = jQuery._data( elem, "olddisplay" ); display = elem.style.display; if ( show ) { // Reset the inline display of this element to learn if it is // being hidden by cascaded rules or not if ( !values[ index ] && display === "none" ) { elem.style.display = ""; } // Set elements which have been overridden with display: none // in a stylesheet to whatever the default browser style is // for such an element if ( elem.style.display === "" && isHidden( elem ) ) { values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); } } else { if ( !values[ index ] ) { hidden = isHidden( elem ); if ( display && display !== "none" || !hidden ) { jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); } } } } // Set the display of most of the elements in a second loop // to avoid the constant reflow for ( index = 0; index < length; index++ ) { elem = elements[ index ]; if ( !elem.style ) { continue; } if ( !show || elem.style.display === "none" || elem.style.display === "" ) { elem.style.display = show ? values[ index ] || "" : "none"; } } return elements; } jQuery.fn.extend({ css: function( name, value ) { return jQuery.access( this, function( elem, name, value ) { var len, styles, map = {}, i = 0; if ( jQuery.isArray( name ) ) { styles = getStyles( elem ); len = name.length; for ( ; i < len; i++ ) { map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); } return map; } return value !== undefined ? jQuery.style( elem, name, value ) : jQuery.css( elem, name ); }, name, value, arguments.length > 1 ); }, show: function() { return showHide( this, true ); }, hide: function() { return showHide( this ); }, toggle: function( state ) { var bool = typeof state === "boolean"; return this.each(function() { if ( bool ? state : isHidden( this ) ) { jQuery( this ).show(); } else { jQuery( this ).hide(); } }); } }); jQuery.extend({ // Add in style property hooks for overriding the default // behavior of getting and setting a style property cssHooks: { opacity: { get: function( elem, computed ) { if ( computed ) { // We should always get a number back from opacity var ret = curCSS( elem, "opacity" ); return ret === "" ? "1" : ret; } } } }, // Don't automatically add "px" to these possibly-unitless properties cssNumber: { "columnCount": true, "fillOpacity": true, "fontWeight": true, "lineHeight": true, "opacity": true, "orphans": true, "widows": true, "zIndex": true, "zoom": true }, // Add in properties whose names you wish to fix before // setting or getting the value cssProps: { // normalize float css property "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" }, // Get and set the style property on a DOM Node style: function( elem, name, value, extra ) { // Don't set styles on text and comment nodes if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { return; } // Make sure that we're working with the right name var ret, type, hooks, origName = jQuery.camelCase( name ), style = elem.style; name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); // gets hook for the prefixed version // followed by the unprefixed version hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // Check if we're setting a value if ( value !== undefined ) { type = typeof value; // convert relative number strings (+= or -=) to relative numbers. #7345 if ( type === "string" && (ret = rrelNum.exec( value )) ) { value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); // Fixes bug #9237 type = "number"; } // Make sure that NaN and null values aren't set. See: #7116 if ( value == null || type === "number" && isNaN( value ) ) { return; } // If a number was passed in, add 'px' to the (except for certain CSS properties) if ( type === "number" && !jQuery.cssNumber[ origName ] ) { value += "px"; } // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, // but it would mean to define eight (for every problematic property) identical functions if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { style[ name ] = "inherit"; } // If a hook was provided, use that value, otherwise just set the specified value if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { // Wrapped to prevent IE from throwing errors when 'invalid' values are provided // Fixes bug #5509 try { style[ name ] = value; } catch(e) {} } } else { // If a hook was provided get the non-computed value from there if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { return ret; } // Otherwise just get the value from the style object return style[ name ]; } }, css: function( elem, name, extra, styles ) { var num, val, hooks, origName = jQuery.camelCase( name ); // Make sure that we're working with the right name name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); // gets hook for the prefixed version // followed by the unprefixed version hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // If a hook was provided get the computed value from there if ( hooks && "get" in hooks ) { val = hooks.get( elem, true, extra ); } // Otherwise, if a way to get the computed value exists, use that if ( val === undefined ) { val = curCSS( elem, name, styles ); } //convert "normal" to computed value if ( val === "normal" && name in cssNormalTransform ) { val = cssNormalTransform[ name ]; } // Return, converting to number if forced or a qualifier was provided and val looks numeric if ( extra === "" || extra ) { num = parseFloat( val ); return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; } return val; } }); // NOTE: we've included the "window" in window.getComputedStyle // because jsdom on node.js will break without it. if ( window.getComputedStyle ) { getStyles = function( elem ) { return window.getComputedStyle( elem, null ); }; curCSS = function( elem, name, _computed ) { var width, minWidth, maxWidth, computed = _computed || getStyles( elem ), // getPropertyValue is only needed for .css('filter') in IE9, see #12537 ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, style = elem.style; if ( computed ) { if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { ret = jQuery.style( elem, name ); } // A tribute to the "awesome hack by Dean Edwards" // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { // Remember the original values width = style.width; minWidth = style.minWidth; maxWidth = style.maxWidth; // Put in the new values to get a computed value out style.minWidth = style.maxWidth = style.width = ret; ret = computed.width; // Revert the changed values style.width = width; style.minWidth = minWidth; style.maxWidth = maxWidth; } } return ret; }; } else if ( document.documentElement.currentStyle ) { getStyles = function( elem ) { return elem.currentStyle; }; curCSS = function( elem, name, _computed ) { var left, rs, rsLeft, computed = _computed || getStyles( elem ), ret = computed ? computed[ name ] : undefined, style = elem.style; // Avoid setting ret to empty string here // so we don't default to auto if ( ret == null && style && style[ name ] ) { ret = style[ name ]; } // From the awesome hack by Dean Edwards // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 // If we're not dealing with a regular pixel number // but a number that has a weird ending, we need to convert it to pixels // but not position css attributes, as those are proportional to the parent element instead // and we can't measure the parent instead because it might trigger a "stacking dolls" problem if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { // Remember the original values left = style.left; rs = elem.runtimeStyle; rsLeft = rs && rs.left; // Put in the new values to get a computed value out if ( rsLeft ) { rs.left = elem.currentStyle.left; } style.left = name === "fontSize" ? "1em" : ret; ret = style.pixelLeft + "px"; // Revert the changed values style.left = left; if ( rsLeft ) { rs.left = rsLeft; } } return ret === "" ? "auto" : ret; }; } function setPositiveNumber( elem, value, subtract ) { var matches = rnumsplit.exec( value ); return matches ? // Guard against undefined "subtract", e.g., when used as in cssHooks Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : value; } function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { var i = extra === ( isBorderBox ? "border" : "content" ) ? // If we already have the right measurement, avoid augmentation 4 : // Otherwise initialize for horizontal or vertical properties name === "width" ? 1 : 0, val = 0; for ( ; i < 4; i += 2 ) { // both box models exclude margin, so add it if we want it if ( extra === "margin" ) { val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); } if ( isBorderBox ) { // border-box includes padding, so remove it if we want content if ( extra === "content" ) { val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); } // at this point, extra isn't border nor margin, so remove border if ( extra !== "margin" ) { val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } } else { // at this point, extra isn't content, so add padding val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); // at this point, extra isn't content nor padding, so add border if ( extra !== "padding" ) { val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } } } return val; } function getWidthOrHeight( elem, name, extra ) { // Start with offset property, which is equivalent to the border-box value var valueIsBorderBox = true, val = name === "width" ? elem.offsetWidth : elem.offsetHeight, styles = getStyles( elem ), isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; // some non-html elements return undefined for offsetWidth, so check for null/undefined // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 if ( val <= 0 || val == null ) { // Fall back to computed then uncomputed css if necessary val = curCSS( elem, name, styles ); if ( val < 0 || val == null ) { val = elem.style[ name ]; } // Computed unit is not pixels. Stop here and return. if ( rnumnonpx.test(val) ) { return val; } // we need the check for style in case a browser which returns unreliable values // for getComputedStyle silently falls back to the reliable elem.style valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); // Normalize "", auto, and prepare for extra val = parseFloat( val ) || 0; } // use the active box-sizing model to add/subtract irrelevant styles return ( val + augmentWidthOrHeight( elem, name, extra || ( isBorderBox ? "border" : "content" ), valueIsBorderBox, styles ) ) + "px"; } // Try to determine the default display value of an element function css_defaultDisplay( nodeName ) { var doc = document, display = elemdisplay[ nodeName ]; if ( !display ) { display = actualDisplay( nodeName, doc ); // If the simple way fails, read from inside an iframe if ( display === "none" || !display ) { // Use the already-created iframe if possible iframe = ( iframe || jQuery("