diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 8b88d6b..0000000 --- a/LICENSE +++ /dev/null @@ -1,98 +0,0 @@ -Creative Commons Attribution-ShareAlike 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. - -Section 1 – Definitions. - -Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. -Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. -BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. -Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. -Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. -Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. -License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike. -Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. -Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. -Licensor means the individual(s) or entity(ies) granting rights under this Public License. -Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. -Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. -You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. -Section 2 – Scope. - -License grant. -Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: -reproduce and Share the Licensed Material, in whole or in part; and -produce, reproduce, and Share Adapted Material. -Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. -Term. The term of this Public License is specified in Section 6(a). -Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. -Downstream recipients. -Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. -Additional offer from the Licensor – Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply. -No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. -No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). -Other rights. - -Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. -Patent and trademark rights are not licensed under this Public License. -To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. -Section 3 – License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the following conditions. - -Attribution. - -If You Share the Licensed Material (including in modified form), You must: - -retain the following if it is supplied by the Licensor with the Licensed Material: -identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); -a copyright notice; -a notice that refers to this Public License; -a notice that refers to the disclaimer of warranties; -a URI or hyperlink to the Licensed Material to the extent reasonably practicable; -indicate if You modified the Licensed Material and retain an indication of any previous modifications; and -indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. -You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. -If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. -ShareAlike. -In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. - -The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License. -You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. -You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. -Section 4 – Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: - -for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; -if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and -You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. -For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. -Section 5 – Disclaimer of Warranties and Limitation of Liability. - -Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. -To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. -The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. -Section 6 – Term and Termination. - -This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. -Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: - -automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or -upon express reinstatement by the Licensor. -For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. -For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. -Sections 1, 5, 6, 7, and 8 survive termination of this Public License. -Section 7 – Other Terms and Conditions. - -The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. -Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. -Section 8 – Interpretation. - -For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. -To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. -No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. -Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. -Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. - -Creative Commons may be contacted at creativecommons.org. \ No newline at end of file diff --git a/README.rst b/README.rst deleted file mode 100644 index 17bbd6a..0000000 --- a/README.rst +++ /dev/null @@ -1,15 +0,0 @@ -System Development With Python -============================== - -System Development With Python: Third class in the Python Certification series. - -This repository contains the source materials for the third class in the the University of Washington Professional and Continuing Education Program Python Certification Program: - -.. _Certificate in Python Programming : http://www.pce.uw.edu/certificates/python-programming.html - -See the Syllabus for more detail. - -Class lecture materials are available in a rendered version from: - -http://UWPCE-PythonCert.github.io/SystemDevelopment2015 - diff --git a/_downloads/CircularReferenceExample.ipynb b/_downloads/CircularReferenceExample.ipynb new file mode 100644 index 0000000..26ca4a7 --- /dev/null +++ b/_downloads/CircularReferenceExample.ipynb @@ -0,0 +1,944 @@ +{ + "metadata": { + "name": "", + "signature": "sha256:6f97c8dc34ce3c2a08fcd08d759ffe2ffad19a7344240d79f91b343846b119c8" + }, + "nbformat": 3, + "nbformat_minor": 0, + "worksheets": [ + { + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Circular reference experiments:\n", + "\n", + "An example of a circular reference case: a parent-child relationship.\n" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "import sys" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 1 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# A child class\n", + "# it keeps a reference to its parent.\n", + "class MyChild(object):\n", + " def __init__(self, parent):\n", + " self.parent = parent\n", + "\n", + " ## store some data so it will use appreciable memory\n", + " ## multiply by 1234 to reduce interning\n", + " self.data = [1234*i for i in range(100000)]\n" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 2 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# A parent class -- holds and creates children\n", + "class MyParent(object):\n", + " def __init__(self):\n", + " self.children = []\n", + "\n", + " def addChild(self):\n", + " \"\"\"\n", + " Create and add a child object\n", + " \"\"\"\n", + " child = MyChild(self)\n", + " self.children.append(child)\n", + " return child" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 3 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# use them:\n", + "p = MyParent()\n", + "c1 = p.addChild()\n", + "c2 = p.addChild()" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 4 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "print c1\n" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "<__main__.MyChild object at 0x104363750>\n" + ] + } + ], + "prompt_number": 5 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "print c1.parent" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "<__main__.MyParent object at 0x1043634d0>\n" + ] + } + ], + "prompt_number": 6 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "c2.parent is p" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 7, + "text": [ + "True" + ] + } + ], + "prompt_number": 7 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "p.children[1] is c2" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 8, + "text": [ + "True" + ] + } + ], + "prompt_number": 8 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### All the objects refer to each-other as we expect.\n", + "\n", + "\n", + "### But what about the reference counts?\n" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "sys.getrefcount(p)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 9, + "text": [ + "4" + ] + } + ], + "prompt_number": 9 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# Add some more children\n", + "p.addChild()\n", + "p.addChild()\n", + "# then the refcount:\n", + "sys.getrefcount(p)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 10, + "text": [ + "6" + ] + } + ], + "prompt_number": 10 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### What will the refcount be if we delete p?\n", + "\n", + "That only reduces it by 1.... still 5 left?\n", + "\n", + "### And what about c1 and c2?" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "sys.getrefcount(c1)\n", + "sys.getrefcount(c2)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 11, + "text": [ + "3" + ] + } + ], + "prompt_number": 11 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So deleting them won't help either....\n", + "\n", + "Classic Circular reference problem -- these objects could live on forever." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Enter Garbage Collection:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "import gc\n", + "gc.collect() # run once to clean things out..." + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 13, + "text": [ + "0" + ] + } + ], + "prompt_number": 13 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "gc.collect()" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 14, + "text": [ + "0" + ] + } + ], + "prompt_number": 14 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "yup -- it's clean" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# delete the names:\n", + "del p, c1, c2\n", + "# try again:\n", + "gc.collect()" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'p' is not defined", + "output_type": "pyerr", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# delete the names:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mdel\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mc1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mc2\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0;31m# try again:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mgc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcollect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mNameError\u001b[0m: name 'p' is not defined" + ] + } + ], + "prompt_number": 16 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### But: What if objects have a ```__del__``` method?\n", + "\n", + "The garbage collector won't delete them (note: very common for compiled extension objects...)" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "class MyChild(object):\n", + " def __init__(self, parent):\n", + " self.parent = parent\n", + "\n", + " ## store some data so it will use appreciable memory\n", + " ## multiply by 1234 to reduce interning\n", + " self.data = [1234*i for i in range(100000)]\n", + "\n", + " #provide a deleter to defeat garbage collection..\n", + " def __del__(self):\n", + " # this way we can see when they get deleted\n", + " print 'MyChild deleted', id(self)\n", + "\n", + "class MyParent(object):\n", + " def __init__(self):\n", + " self.children = []\n", + " def addChild(self):\n", + " child = MyChild(self)\n", + " self.children.append(child)\n", + " return child\n", + " ## note: probably dont need a deleter on both, but just in case...\n", + " def __del__(self):\n", + " print 'MyParent deleted', id(self)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 17 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# simple case -- no children\n", + "p = MyParent()\n", + "del p" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "MyParent deleted 4365479248\n" + ] + } + ], + "prompt_number": 18 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is deleted\n" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "## multiple children:\n", + "p = MyParent()\n", + "p.addChild()\n", + "p.addChild()\n", + "p.addChild()" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 19, + "text": [ + "<__main__.MyChild at 0x104347d90>" + ] + } + ], + "prompt_number": 19 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "sys.getrefcount(p)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 20, + "text": [ + "5" + ] + } + ], + "prompt_number": 20 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# clear out random stuff iPython created...\n", + "gc.collect()" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 21, + "text": [ + "16" + ] + } + ], + "prompt_number": 21 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# call again\n", + "gc.collect()" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 22, + "text": [ + "0" + ] + } + ], + "prompt_number": 22 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# no extra garbage:\n", + "del p\n", + "gc.collect()" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 23, + "text": [ + "0" + ] + } + ], + "prompt_number": 23 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Still zero -- p and its children are still around....\n", + "\n", + "(```memcount.py``` demo....)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The solution: Weak References:\n", + "\n", + "http://docs.python.org/2/library/weakref.html\n", + "\n", + "The child doesn't need to keep the parent around if it's the only reference to it.\n", + "\n", + "So it can use a weak reference ```proxy``` object. A ```proxy``` object acts like a regular object, but the reference count is not increased\n" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "import weakref\n", + "\n", + "class MyChild(object):\n", + " def __init__(self, parent):\n", + " self.parent = weakref.proxy(parent)\n", + "\n", + " ## store some data so it will use appreciable memory\n", + " ## multiply by 1234 to reduce interning\n", + " self.data = [1234*i for i in range(100000)]\n", + "\n", + " def __del__(self):\n", + " # this way we can see when they get deleted\n", + " print 'MyChild deleted', id(self)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 24 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "p = MyParent()\n", + "sys.getrefcount(p)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "MyParent deleted 4373802320\n" + ] + }, + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 35, + "text": [ + "2" + ] + } + ], + "prompt_number": 35 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "print id(p)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "4373801040\n" + ] + } + ], + "prompt_number": 36 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# now add some children\n", + "p.addChild()\n", + "p.addChild()\n", + "sys.getrefcount(p)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 37, + "text": [ + "2" + ] + } + ], + "prompt_number": 37 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Reference count on parent has not increased." + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# but you can still get at the parent from the child\n", + "p.children[1].parent" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 38, + "text": [ + "<__main__.MyParent at 0x104b5b0a8>" + ] + } + ], + "prompt_number": 38 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "del p" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "MyParent deleted 4373801040\n", + "MyChild deleted 4373987408\n", + "MyChild deleted 4373988496\n" + ] + } + ], + "prompt_number": 39 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "And everything got deleted when the reference goes away." + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "p = MyParent()\n", + "c = p.addChild()" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 40 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "c\n" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 41, + "text": [ + "<__main__.MyChild at 0x104b2ea10>" + ] + } + ], + "prompt_number": 41 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "sys.getrefcount(p)\n", + "sys.getrefcount(c)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 43, + "text": [ + "9" + ] + } + ], + "prompt_number": 43 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "del p" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "MyParent deleted 4373802000\n" + ] + } + ], + "prompt_number": 44 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "print c" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "<__main__.MyChild object at 0x104b2ea10>\n" + ] + } + ], + "prompt_number": 45 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "c.parent ## does the child have a parent???\n" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stderr", + "text": [ + "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython/core/formatters.py:239: FormatterWarning: Exception in text/html formatter: weakly-referenced object no longer exists\n", + " FormatterWarning,\n", + "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython/core/formatters.py:239: FormatterWarning: Exception in image/jpeg formatter: weakly-referenced object no longer exists\n", + " FormatterWarning,\n", + "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython/core/formatters.py:239: FormatterWarning: Exception in image/svg+xml formatter: weakly-referenced object no longer exists\n", + " FormatterWarning,\n", + "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython/core/formatters.py:239: FormatterWarning: Exception in image/png formatter: weakly-referenced object no longer exists\n", + " FormatterWarning,\n", + "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython/core/formatters.py:239: FormatterWarning: Exception in application/javascript formatter: weakly-referenced object no longer exists\n", + " FormatterWarning,\n", + "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython/core/formatters.py:239: FormatterWarning: Exception in text/latex formatter: weakly-referenced object no longer exists\n", + " FormatterWarning,\n", + "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython/core/formatters.py:239: FormatterWarning: Exception in application/json formatter: weakly-referenced object no longer exists\n", + " FormatterWarning,\n", + "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython/core/formatters.py:239: FormatterWarning: Exception in application/pdf formatter: weakly-referenced object no longer exists\n", + " FormatterWarning,\n" + ] + }, + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 46, + "text": [ + "" + ] + } + ], + "prompt_number": 46 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "p = c.parent" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 47 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "print p" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "ename": "ReferenceError", + "evalue": "weakly-referenced object no longer exists", + "output_type": "pyerr", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mReferenceError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mprint\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mReferenceError\u001b[0m: weakly-referenced object no longer exists" + ] + } + ], + "prompt_number": 48 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Nope -- but it doesn't crash, either!" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "p = MyParent()\n", + "p.addChild()\n", + "p.addChild()\n" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "MyParent deleted 4374022736\n", + "MyChild deleted 4374022672\n" + ] + }, + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 50, + "text": [ + "<__main__.MyChild at 0x104b64a50>" + ] + } + ], + "prompt_number": 50 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "p" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 51, + "text": [ + "<__main__.MyParent at 0x104b64ad0>" + ] + } + ], + "prompt_number": 51 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "sys.getrefcount(p)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 52, + "text": [ + "8" + ] + } + ], + "prompt_number": 52 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [], + "language": "python", + "metadata": {}, + "outputs": [] + } + ], + "metadata": {} + } + ] +} \ No newline at end of file diff --git a/_downloads/ICanEatGlass.utf16.txt b/_downloads/ICanEatGlass.utf16.txt new file mode 100644 index 0000000..24a0858 Binary files /dev/null and b/_downloads/ICanEatGlass.utf16.txt differ diff --git a/_downloads/ICanEatGlass.utf8.txt b/_downloads/ICanEatGlass.utf8.txt new file mode 100644 index 0000000..9ecba2b --- /dev/null +++ b/_downloads/ICanEatGlass.utf8.txt @@ -0,0 +1,23 @@ +I Can Eat Glass: + +And from the sublime to the ridiculous, here is a certain phrase in an assortment of languages: + +Sanskrit: काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥ + +Sanskrit (standard transcription): kācaṃ śaknomyattum; nopahinasti mām. + +Classical Greek: ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει. + +Greek (monotonic): Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα. + +Greek (polytonic): Μπορῶ νὰ φάω σπασμένα γυαλιὰ χωρὶς νὰ πάθω τίποτα. + +Latin: Vitrum edere possum; mihi non nocet. + +Old French: Je puis mangier del voirre. Ne me nuit. + +French: Je peux manger du verre, ça ne me fait pas mal. + +Provençal / Occitan: Pòdi manjar de veire, me nafrariá pas. + +Québécois: J'peux manger d'la vitre, ça m'fa pas mal. \ No newline at end of file diff --git a/_downloads/add_book_data.py b/_downloads/add_book_data.py new file mode 100644 index 0000000..4ef65cf --- /dev/null +++ b/_downloads/add_book_data.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +""" +sample data for persistence/serializatiion examples + +This version is nested, with more stucture + - can be saved with pickle, JSON, xml... +""" + +AddressBook = [ {'first_name': "Chris", + 'last_name': "Barker", + 'address' : {'line_1':"835 NE 33rd St", + 'line_2' : "", + 'city' : "Seattle", + 'state': "WA", + 'zip': "96543"}, + 'email' : "PythonCHB@gmail.com", + 'home_phone' : "206-555-1234", + 'office_phone' : "123-456-7890", + 'cell_phone' : "234-567-8901", + }, + + {'first_name': "Fred", + 'last_name': "Jones", + 'address' : {'line_1':"123 SE 13th St", + 'line_2' : "Apt. 43", + 'city' : "Tacoma", + 'state': "WA", + 'zip': "93465"}, + 'email' : "FredJones@some_company.com", + 'home_phone' : "510-555-1234", + 'office_phone' : "564-466-7990", + 'cell_phone' : "403-561-8911", + }, + + {'first_name': "Nancy", + 'last_name': "Wilson", + 'address' : {'line_1':"8654 Walnut St", + 'line_2' : "Suite 567", + 'city' : "Pasadena", + 'state': "CA", + 'zip': "12345"}, + 'email' : "Wilson.Nancy@gmail.com", + 'home_phone' : "423-321-9876", + 'office_phone' : "123-765-9877", + 'cell_phone' : "432-567-8466", + }, + ] + diff --git a/_downloads/add_book_data_flat.py b/_downloads/add_book_data_flat.py new file mode 100644 index 0000000..97a0869 --- /dev/null +++ b/_downloads/add_book_data_flat.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +""" +sample data for persistence/serialization examples +this version is flat for saving in CSV, ini, etc. +""" + +AddressBook = [ {'first_name': "Chris", + 'last_name': "Barker", + 'address_line_1':"835 NE 33rd St", + 'address_line_2' : "", + 'address_city' : "Seattle", + 'address_state': "WA", + 'address_zip': "96543", + 'email' : "PythonCHB@gmail.com", + 'home_phone' : "206-555-1234", + 'office_phone' : "123-456-7890", + 'cell_phone' : "234-567-8901", + }, + + {'first_name': "Fred", + 'last_name': "Jones", + 'address_line_1':"123 SE 13th St", + 'address_line_2' : "Apt. 43", + 'address_city' : "Tacoma", + 'address_state': "WA", + 'address_zip': "93465", + 'email' : "FredJones@some_company.com", + 'home_phone' : "510-555-1234", + 'office_phone' : "564-466-7990", + 'cell_phone' : "403-561-8911", + }, + + {'first_name': "Nancy", + 'last_name': "Wilson", + 'address_line_1':"8654 Walnut St", + 'address_line_2' : "Suite 567", + 'address_city' : "Pasadena", + 'address_state': "CA", + 'address_zip': "12345", + 'email' : "Wilson.Nancy@gmail.com", + 'home_phone' : "423-321-9876", + 'office_phone' : "123-765-9877", + 'cell_phone' : "432-567-8466", + }, + ] + diff --git a/_downloads/circular.py b/_downloads/circular.py new file mode 100644 index 0000000..ee9d853 --- /dev/null +++ b/_downloads/circular.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import weakref +import gc + +deleted_object_messages = [] + + +class MyChild(object): + def __init__(self, parent): + self.parent = parent + #self.parent = weakref.ref(parent) + #self.parent = weakref.proxy(parent) + + ## store some data so it will use appreciable memory + ## multiply by 1234 to avoid interning + self.data = [1234*i for i in range(100000)] + + def __del__(self): + deleted_object_messages.append( ('MyChild deleted', id(self)) ) + + +class MyParent(object): + def __init__(self): + self.children = [] + def addChild(self): + child = MyChild(self) + self.children.append(child) + return child + def __del__(self): + deleted_object_messages.append( ('MyParent deleted', id(self)) ) + +if __name__ == "__main__": + + p = MyParent() + + print "refcount for p:", sys.getrefcount(p) + assert sys.getrefcount(p) == 2 + + a = p.addChild() + a2 = p.addChild() + print "refcount for p after adding an two children:", sys.getrefcount(p) + assert sys.getrefcount(p) == 2 + + print "p's children:", p.children + assert len(p.children) == 2 + + print " a is:", a + print "a's parent:", a.parent + print "a's parent's children:", a.parent.children + + assert a is a.parent.children[0] + assert a2 is a.parent.children[1] + + + print "a's refcount:", sys.getrefcount(a) + assert sys.getrefcount(a) == 3 + + print "a2's refcount:", sys.getrefcount(a2) + assert sys.getrefcount(a2) == 3 + + del p + print "after deleting p:" + + print "a's refcount:", sys.getrefcount(a) + assert sys.getrefcount(a) == 2 + + print "a2's refcount:", sys.getrefcount(a2) + assert sys.getrefcount(a2) == 2 + + print "deleting a:" + id_a = id(a) + del a + print deleted_object_messages + assert deleted_object_messages[-1][0] == 'MyChild deleted' + assert deleted_object_messages[-1][1] == id_a + + print "deleting a2:" + id_a2 = id(a2) + del a2 + print deleted_object_messages + assert deleted_object_messages[-1][0] == 'MyChild deleted' + assert deleted_object_messages[-1][1] == id_a2 + + + + diff --git a/_downloads/eggs.csv b/_downloads/eggs.csv new file mode 100644 index 0000000..efb5b9e --- /dev/null +++ b/_downloads/eggs.csv @@ -0,0 +1,3 @@ +Spam, Spam, Spam, Spam, Spam, Baked Beans +Spam, "Lovely, Spam", Wonderful, Spam, and, more + diff --git a/_downloads/exception_test.py b/_downloads/exception_test.py new file mode 100644 index 0000000..975f1df --- /dev/null +++ b/_downloads/exception_test.py @@ -0,0 +1,16 @@ +#!/usr/bin/python + +""" +example for what happens when you pass non-ascii unicode to a Exception +""" + +msg = u'This is an ASCII-compatible unicode message' + +#msg = u'This is an non ASCII\N{EM DASH}compatible unicode message' + +print "\nDo you see this message in the Exception report?\n" +print msg +print + +raise ValueError(msg) + diff --git a/_downloads/hello_unicode.py b/_downloads/hello_unicode.py new file mode 100644 index 0000000..6bbad1d --- /dev/null +++ b/_downloads/hello_unicode.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +hello = 'Hello ' +world = u'世界' + +print hello + world + +print u"It was nice weather today: it reached 80\u00B0" + +print u"Maybe it will reach 90\N{degree sign}" + +print u"It is extremely rare for it ever to reach 100° in Seattle" diff --git a/_downloads/latin1_test.py b/_downloads/latin1_test.py new file mode 100644 index 0000000..3990078 --- /dev/null +++ b/_downloads/latin1_test.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +""" +An example of using latin-1 as a universal encoding + +latin-1 is a superset of ASCII that is suitable for western european languages. + +Is the most common, and a good default, if you need a one-byte per char encoding +for European text. + +It also has a nice property: + : every byte value from 0 to 255 is avalid charactor + +Thus you will never get an UnicodeDecodeError if +you try to decode arbitrary bytes with latin-1. + +And it can "round-trip" trhough a unicode object. + +This can be useful is you don't know the encoding -- at least it won't break. +It's also useful if you need to work with cobined text+binary data. + + + +""" + +# all the byte values in a bytes (str) object: +all_bytes = ''.join( [chr(i) for i in range(255)] ) + +print type(all_bytes) +print len(all_bytes) + +print "Example value: 20" +print ord(all_bytes[20]) == 20 +print "Example high value: 245" +print ord(all_bytes[245]) == 245 + +# now decode it to a unicode object: +try: + uni = all_bytes.decode() +except UnicodeDecodeError: + print "OOPS: can't decode with default encoding" + +# latin-1 works: +try: + all_uni = all_bytes.decode('latin-1') + print "Yup -- that worked" + print all_uni + print "note that the ASCII subset is the same..." +except UnicodeDecodeError: + print "OOPS: This should have worked!!" + raise + +## now show that it round-trips: +all_bytes2 = all_uni.encode('latin-1') + +if all_bytes2 == all_bytes: + print "yup -- that worked...the values are preserved on the round trip." +else: + print "Hey, that should have worked" + + + + + + + diff --git a/_downloads/mem_check.py b/_downloads/mem_check.py new file mode 100644 index 0000000..677c186 --- /dev/null +++ b/_downloads/mem_check.py @@ -0,0 +1,109 @@ +""" +mem_check.py + +functions for getting memoroy use of the current python process + +Windows and *nix versions + +USAGE: + +amount = get_mem_use(units='MB') # options are KB, MB, GB + +""" + +import sys + +div = {'GB': 1024*1024*1024, + 'MB': 1024*1024, + 'KB': 1024, + } + +if sys.platform.startswith('win'): + + """ + + Functions for getting memory usage of Windows processes. + + from: + + http://code.activestate.com/recipes/578513-get-memory-usage-of-windows-processes-using-getpro/ + + get_mem_use(units='MB') is the one to get memory use for the current process. + + + """ + import ctypes + from ctypes import wintypes + + GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess + GetCurrentProcess.argtypes = [] + GetCurrentProcess.restype = wintypes.HANDLE + + SIZE_T = ctypes.c_size_t + + class PROCESS_MEMORY_COUNTERS_EX(ctypes.Structure): + _fields_ = [ + ('cb', wintypes.DWORD), + ('PageFaultCount', wintypes.DWORD), + ('PeakWorkingSetSize', SIZE_T), + ('WorkingSetSize', SIZE_T), + ('QuotaPeakPagedPoolUsage', SIZE_T), + ('QuotaPagedPoolUsage', SIZE_T), + ('QuotaPeakNonPagedPoolUsage', SIZE_T), + ('QuotaNonPagedPoolUsage', SIZE_T), + ('PagefileUsage', SIZE_T), + ('PeakPagefileUsage', SIZE_T), + ('PrivateUsage', SIZE_T), + ] + + GetProcessMemoryInfo = ctypes.windll.psapi.GetProcessMemoryInfo + GetProcessMemoryInfo.argtypes = [ + wintypes.HANDLE, + ctypes.POINTER(PROCESS_MEMORY_COUNTERS_EX), + wintypes.DWORD, + ] + GetProcessMemoryInfo.restype = wintypes.BOOL + + def get_current_process(): + """Return handle to current process.""" + return GetCurrentProcess() + + def get_memory_info(process=None): + """Return Win32 process memory counters structure as a dict.""" + if process is None: + process = get_current_process() + counters = PROCESS_MEMORY_COUNTERS_EX() + ret = GetProcessMemoryInfo(process, ctypes.byref(counters), + ctypes.sizeof(counters)) + if not ret: + raise ctypes.WinError() + info = dict((name, getattr(counters, name)) + for name, _ in counters._fields_) + return info + + def get_mem_use(units='MB'): + """ + returns the total memory use of the current python process + + :param units='MB': the units you want the reslut in. Options are: + 'GB', 'MB', 'KB' + """ + info = get_memory_info() + return info['PrivateUsage'] / float(div[units]) + + +else: # for posix systems only tested on OS-X for now + def get_mem_use(units='MB'): + """ + returns the total memory use of the current python process + + :param units='MB': the units you want the reslut in. Options are: + 'GB', 'MB', 'KB' + """ + import resource + #useage = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + div = {'GB': 1024*1024*1024, + 'MB': 1024*1024, + 'KB': 1024, + } + return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / float(div[units]) diff --git a/_downloads/memcount.py b/_downloads/memcount.py new file mode 100644 index 0000000..332a49f --- /dev/null +++ b/_downloads/memcount.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +a simple test of a circular reference that gc.collect() will not clean up. + +And watch the memory grow! + +""" + + +import sys +import gc +import weakref + +import mem_check + + +class MyChild(object): + def __init__(self, parent): + self.parent = parent + # if a weak ref is used, then no memory leak. + # self.parent = weakref.proxy(parent) + + # store some data so it will use appreciable memory + # multiply by 1234 to reduce interning + self.data = [1234 * i for i in range(100000)] + + def __del__(self): + """ __del__ defined to defeat GC""" + print 'MyChild deleted', id(self) + + +class MyParent(object): + def __init__(self): + self.children = [] + + def add_child(self): + child = MyChild(self) + self.children.append(child) + return child + + def __del__(self): + """ __del__ defined to defeat GC""" + print 'MyParent deleted', id(self) + + +if __name__ == "__main__": + + # create a bunch in a loop: + for i in range(100): + print "iteration:", i + p = MyParent() + p.add_child() + p.add_child() + p.add_child() + print "ref count:", sys.getrefcount(p) + print "mem_use: %f MB" % mem_check.get_mem_use() + del p + print "collected", gc.collect() diff --git a/_downloads/simple_circular.py b/_downloads/simple_circular.py new file mode 100644 index 0000000..537b3f7 --- /dev/null +++ b/_downloads/simple_circular.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +""" +Simple contrived example of circular reference counting +""" + +import sys + +l1 = [1,] +l2 = [2,] + +l1_id = id(l1) +l2_id = id(l2) + +print "initial ref counts:" +print "l1:", sys.getrefcount(l1) +print "l2:", sys.getrefcount(l2) + +print "now add the circular references:" +l1.append(l2) +l2.append(l1) + +print "ref counts after the circular reference is added" +print "l1:", l1, "refcount:", sys.getrefcount(l1) +print "l2:", l2, "refcount:", sys.getrefcount(l2) + +print "delete one" +del l1 +print "l2:", sys.getrefcount(l2) + +print "delete the other" +del l2 + +print "but are they still there???" + + + + + diff --git a/_downloads/simple_circular_di.py b/_downloads/simple_circular_di.py new file mode 100644 index 0000000..0f7da09 --- /dev/null +++ b/_downloads/simple_circular_di.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +""" +Simple contrived example of circular reference counting + +This one does some testing with di.ref_by_id +""" + +import sys +import di +import gc + +l1 = [1,] +l2 = [2,] + +l1_id = id(l1) +l2_id = id(l2) + +print "initial ref counts:" +print "l1:", sys.getrefcount(l1) +print "l2:", sys.getrefcount(l2) + +print "now add the circular references:" +l1.append(l2) +l2.append(l1) + +print "ref counts after the circular reference is added" +print "l1:", sys.getrefcount(l1) +print "l2:", sys.getrefcount(l2) + +print "delete one" +del l1 +print "l2:", sys.getrefcount(l2) + +print "delete the other" +del l2 + +print "are they still there?" +print "l1's refcount:", di.ref_by_id(l1_id) +print "l2's refcount:", di.ref_by_id(l2_id) +print "-- yes they are!" + +print "now run the garbage collector" +print "It collected:", gc.collect(), "objects" + +print "ref counts now???" +print "l1's refcount:", di.ref_by_id(l1_id) +print "l2's refcount:", di.ref_by_id(l2_id) + +print "yup -- they'll get cleaned up" + + + + diff --git a/_downloads/text.utf16 b/_downloads/text.utf16 new file mode 100644 index 0000000..b80b2ef Binary files /dev/null and b/_downloads/text.utf16 differ diff --git a/_downloads/text.utf32 b/_downloads/text.utf32 new file mode 100644 index 0000000..c529531 Binary files /dev/null and b/_downloads/text.utf32 differ diff --git a/_downloads/text.utf8 b/_downloads/text.utf8 new file mode 100644 index 0000000..9de1889 --- /dev/null +++ b/_downloads/text.utf8 @@ -0,0 +1,17 @@ +Origin (in native language) Name (in native language) +Հայաստան Արամ Խաչատրյան + Australia Nicole Kidman + Österreich Johann Strauß + Azərbaycan Vaqif Səmədoğlu + Азәрбајҹан Вагиф Сәмәдоғлу + Azərbaycan Heydər Əliyev + Азәрбајҹан Һејдәр Әлијев + België René Magritte + Belgique René Magritte + Belgien René Magritte + বাংলা সুকুমার রায় + འབྲུག་ཡུལ། མགོན་པོ་རྡོ་རྗེ། + ប្រទេស​​​កម្ពុជា ព្រះ​ពុទ្ឋឃោសាចារ‌្យ​ជួន​ណាត +Canada Céline Dion + ᓄᓇᕗᒻᒥᐅᑦ ᓱᓴᓐ ᐊᒡᓗᒃᑲᖅ + \ No newline at end of file diff --git a/_downloads/unicodify.py b/_downloads/unicodify.py new file mode 100644 index 0000000..15683ee --- /dev/null +++ b/_downloads/unicodify.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +''' +Decorators to convert all arguments passed to a function or method to +unicode or str, including default arguments + +From: http://axialcorps.com/2014/03/20/unicode-str/ + +''' + + +import sys +import functools +import inspect + +def _convert_arg(arg, from_, conv, enc): + '''Safely convert unicode to string or string to unicode''' + return getattr(arg, conv)(encoding=enc) if isinstance(arg, from_) else arg + +def _wrap_convert(from_type, fn, encoding=None): + '''Decorate a function converting all str arguments to unicode or + vice-versa''' + conv = 'decode' if from_type is str else 'encode' + encoding = encoding or sys.getdefaultencoding() + + # override string defaults using partial + aspec, dflts = inspect.getargspec(fn), {} + if aspec.defaults: + for k,v in zip(aspec.args[-len(aspec.defaults):],aspec.defaults): + dflts[k] = _convert_arg(v, from_type, conv, encoding) + fn = functools.partial(fn, **dflts) + + @functools.wraps(fn.func if isinstance(fn, functools.partial) else fn) + def converted(*args, **kwargs): + args = [_convert_arg(a, from_type, conv, encoding) for a in args] + for k,v in kwargs.iteritems(): + kwargs[k] = _convert_arg(v, from_type, conv, encoding) + return fn(*args, **kwargs) + + return converted + +def unicodify(fn=None, encoding=None): + '''Convert all str arguments to unicode''' + if fn is None: + return functools.partial(unicodify, encoding=encoding) + return _wrap_convert(str, fn, encoding=encoding) + +def stringify(fn=None, encoding=None): + '''Convert all unicode arguments to str''' + if fn is None: + return functools.partial(stringify, encoding=encoding) + return _wrap_convert(unicode, fn, encoding=encoding) + +__all__ = ['unicodify', 'stringify'] \ No newline at end of file diff --git a/_images/Diamond_inheritance.png b/_images/Diamond_inheritance.png new file mode 100644 index 0000000..dac9434 Binary files /dev/null and b/_images/Diamond_inheritance.png differ diff --git a/_images/Greenwich_clock.jpg b/_images/Greenwich_clock.jpg new file mode 100644 index 0000000..d1fd94a Binary files /dev/null and b/_images/Greenwich_clock.jpg differ diff --git a/_images/OPP.0108.gif b/_images/OPP.0108.gif new file mode 100644 index 0000000..24e2b23 Binary files /dev/null and b/_images/OPP.0108.gif differ diff --git a/_images/big_o.png b/_images/big_o.png new file mode 100644 index 0000000..ee4e296 Binary files /dev/null and b/_images/big_o.png differ diff --git a/_images/dst_fb_graph.png b/_images/dst_fb_graph.png new file mode 100644 index 0000000..92898de Binary files /dev/null and b/_images/dst_fb_graph.png differ diff --git a/_images/dst_sf_graph.png b/_images/dst_sf_graph.png new file mode 100644 index 0000000..73723a7 Binary files /dev/null and b/_images/dst_sf_graph.png differ diff --git a/_images/flags.jpg b/_images/flags.jpg new file mode 100644 index 0000000..061713d Binary files /dev/null and b/_images/flags.jpg differ diff --git a/_images/gil.png b/_images/gil.png new file mode 100644 index 0000000..301a39d Binary files /dev/null and b/_images/gil.png differ diff --git a/_images/killGIL.jpg b/_images/killGIL.jpg new file mode 100644 index 0000000..08efe7d Binary files /dev/null and b/_images/killGIL.jpg differ diff --git a/_images/program_callstack.png b/_images/program_callstack.png new file mode 100644 index 0000000..821a965 Binary files /dev/null and b/_images/program_callstack.png differ diff --git a/_images/pycallgraph.png b/_images/pycallgraph.png new file mode 100644 index 0000000..1bf4b2d Binary files /dev/null and b/_images/pycallgraph.png differ diff --git a/_images/snakeviz.png b/_images/snakeviz.png new file mode 100644 index 0000000..cedea79 Binary files /dev/null and b/_images/snakeviz.png differ diff --git a/_images/test_v_model.png b/_images/test_v_model.png new file mode 100644 index 0000000..c9aae8c Binary files /dev/null and b/_images/test_v_model.png differ diff --git a/_images/x2.png b/_images/x2.png new file mode 100644 index 0000000..9fdd683 Binary files /dev/null and b/_images/x2.png differ diff --git a/_sources/advanced_oo.rst.txt b/_sources/advanced_oo.rst.txt new file mode 100644 index 0000000..bb6b218 --- /dev/null +++ b/_sources/advanced_oo.rst.txt @@ -0,0 +1,512 @@ +.. _advanced_oo: + +############################################ +Advanced Object Oriented Features of Python +############################################ + +- Chris Barker + + +``PythonCHB@gmail.com`` + + +Multiple Inheritance +##################### + + +Pulling methods from more than one class + + +multiple inheritance +--------------------- + +.. code-block:: python + + class Combined(Super1, Super2, Super3): + def __init__(self, something, something else): + Super1.__init__(self, ......) + Super2.__init__(self, ......) + Super3.__init__(self, ......) + +(calls to the super class' ``__init__`` are optional and case dependent) + + +.. nextslide:: + +**Method Resolution Order:** left to right + +1. Is it an instance attribute ? + +2. Is it a class attribute ? + +3. Is it a superclass attribute ? + + a. is it an attribute of the left-most superclass? + + b. is it an attribute of the next superclass? + + c. ``....`` + +4. Is it a super-superclass attribute ? + +5. also left to right... + + +( This can get complicated --- more on that later...) + +Mix-ins +-------- + +Why would you want to do this? + + +Hierarchies are not always simple: + + +* Animal + + * Mammal + + * GiveBirth() + + * Bird + + * LayEggs() + +Where do you put a Platypus? + + +Real World Example: ``wxPython FloatCanvas``: + +.. rst-class:: small + + https://github.com/wxWidgets/Phoenix/blob/master/wx/lib/floatcanvas/FCObjects.py + + +The Diamond Problem +-------------------- + +.. code-block:: python + + class A(object): + def do_your_stuff(self): + print("doing A's stuff") + + class B(A): + def do_your_stuff(self): + A.do_your_stuff(self) + print("doing B's stuff") + + class C(A): + def do_your_stuff(self): + A.do_your_stuff(self) + print("doing C's stuff") + + class D(B,C): + def do_your_stuff(self): + B.do_your_stuff(self) + C.do_your_stuff(self) + print("doing D's stuff") + + +The Diamond Problem +-------------------- + +Multiple paths to the same superclass: + +.. image:: /_static/Diamond_inheritance.png + :align: center + :height: 400px + +A's methods can get called twice. + +(demo: ``Examples/advancedOO/diamond.py``) + + +The Method Resolution Order +---------------------------- + +Python's The Method Resolution Order ( MRO ) is defined by the C3 +linearization algorithm: + +http://en.wikipedia.org/wiki/C3_linearization. + +In C3, only the last occurrence of a given class is retained. + +In short: corrects the multiple calls to the same method problem + +The classic description of modern MRO by Guido: + +http://www.python.org/download/releases/2.2.2/descrintro/#mro + +And one more: + +http://www.python.org/download/releases/2.3/mro/ + +demo: ``Examples/advancedOO/mro.py`` + +``super()`` +----------- + +``super()`` can handle the MRO for you dynamically + +Getting the superclass: + +.. code-block:: python + + class SafeVehicle(Vehicle): + """ + Safe Vehicle subclass of Vehicle base class... + """ + def __init__(self, position=0, velocity=0, icon='S'): + Vehicle.__init__(self, position, velocity, icon) + + +``Vehicle`` is repeated here -- what if we wanted to change the superclass? + +And there were a bunch of references to Vehicle? + + +super() +------- + +Getting the superclass: + +.. code-block:: python + + class SafeVehicle(Vehicle): + """ + Safe Vehicle subclass of Vehicle base class + """ + def __init__(self, position=0, velocity=0, icon='S'): + super().__init__(position, velocity, icon) + + +``super`` is about more than just making it easier to refactor. + +Remember the method resolution order? + +And the diamond problem? + + +What does super() do? +---------------------- + +``super`` returns a "proxy object" that delegates method calls. + +It's not returning the object itself -- but you can call methods on it. + +It runs through the method resolution order (MRO) to find the method +you call. + +Key point: the MRO is determined *at run time* + +https://docs.python.org/3.5/library/functions.html#super + + +.. nextslide:: + +Not the same as calling one superclass method: ``super()`` +will call all the sibling superclass methods: + +.. code-block:: python + + class D(C, B, A): + def __init__(self): + super().__init__() + +same as: + +.. code-block:: python + + class D(C, B, A): + def __init__(self): + C.__init__() + B.__init__() + A.__init__() + +You may not want that -- + +super() mechanics +------------------ + +In python3, you can usually call super() with no arguments: + +.. code-block:: python + + class B(A): + def a_method(self, *args, **kwargs) + super().a_method(*args, **kwargs) + +However, the actual signature is: + +.. code-block:: python + + super(type[, object-or-type]) + +and in py2, you needed to specify those: + +.. code-block:: python + + class B(A): + def a_method(self, *args, **kwargs) + super(B, self).a_method(*args, **kwargs) + +So why in the world do you need to specify both `B` (the type), and +`self` (the instance?) + +.. nextslide:: + +In py3, those two values are "magically" taken from context. + +But ``super()`` still needs to know that info. + +``super()`` determines the method resolution at run-time, so it needs to +know two things: + +* The mro of current *instance* +* The current *position* in the mro + +Note that while `self` needs to be a subclass of B here, it may not +actually be an *instance* of B -- it could be a subclass. + +That's why both need to be specified. + +Let's experiment with some of this: + +demo: ``Examples/advancedOO/super_test.ipnb`` + + +For more information about super() +---------------------------------- + +Two seminal articles about ``super()``: + + +"*Super Considered Harmful*" + + - James Knight + +https://fuhm.net/super-harmful + + +"*super() Considered Super!*" + + - Raymond Hettinger + + +http://rhettinger.wordpress.com/2011/05/26/super-considered-super + + +(Both worth reading....) + + +super() issues... +----------------- + +Both actually say similar things: + +* The method being called by super() needs to exist +* Every occurrence of the method needs to use super(): + + - Use it consistently, and document that you use it, as it is part + of the external interface for your class, like it or not. + +calling super(): +----------------- + +The caller and callee need to have a matching argument signature: + +Never call super with anything but the exact arguments you received, +unless you really know what you're doing. + +If you add one or more optional arguments, always accept + +.. code-block:: python + + *args, **kwargs + +and call super like + +.. code-block:: python + + super().method(args_declared, *args, **kwargs) + +LAB +---- + +In ``Examples/advancedOO/mixins.py``, you will find a few Vehicle classes +laid out in a hierarchy + +The log() method is defined on Vehicle then called on a couple of +instances + +Modify the class definition for Bike to mix in fancier log() method +from LoggingMixin + +Does the output change accordingly? If it didn't, look at the MRO for +Bike? Is it what you expected? + + +__new__ +######## + +.. rst-class:: large + + Into the depths of object creation: + +.. rst-class:: medium + + What *really* happens when a class instance is created? + +Class Creation +---------------- + +What happens when a class instance is created? + +This is the usual thing... + +.. code-block:: python + + class Class(): + def __init__(self, arg1, arg2): + self.arg1 = arg1 + self.arg2 = arg2 + ..... + +* A new instance is created +* ``__init__`` is called +* The code in ``__init__`` is run to initialize the instance + +Note that ``self`` is already an instance of the class. + +.. nextslide:: + +What if you need to do something before creation? + +Enter: ``__new__`` + +.. code-block:: python + + class Class(): + def __new__(cls, arg1, arg2): + some_code_here + return cls(...) + ... + +* ``__new__`` is called: it returns a new instance + +* The code in ``__new__`` is run to pre-initialize the instance + +* ``__init__`` is called + +* The code in ``__init__`` is run to initialize the instance + + +.. nextslide:: + +``__new__`` is a static method -- but it must be called with a class object as the first argument. + +.. code-block:: python + + class Class(superclass): + def __new__(cls, arg1, arg2): + some_code_here + return superclass.__new__(cls) + ..... + +``cls`` is the class object. + +The arguments (arg1, arg2) are what's passed in when calling the class. + +It needs to return a class instance -- usually by directly calling the superclass ``__new__`` + +If nothing else, you can call ``object.__new__`` (or ``super().__new__``) + + +When to use ``__new__`` +------------------------ + + +When would you need to use it: + +* Subclassing an immutable type: + + - It's too late to change it once you get to ``__init__`` + +* When ``__init__`` is not called: + + - unpickling + + - copying + +You may need to put some code in ``__new__`` to make sure things +go right + +More detail here: + +https://docs.python.org/3/reference/datamodel.html#object.__new__ + + +LAB +---- + +**Demo:** + +``Examples/advancedOO/new_example.py`` + +**Exercise:** + +Write a subclass of int that will always be an even number: +round the input to the closest even number: + +``Examples/advancedOO/even_int.py`` + + +``Examples/advancedOO/test_even_int.py`` + + +Wrap Up +------- + +Thinking OO in Python: + + +Think about what makes sense for your code: + +* Code re-use +* Clean APIs +* ... + + +Don't be a slave to what OO is *supposed to look like*. + + +Let OO work for you, not *create* work for you. + + +Wrap Up +-------- + +OO in Python: + + +*The Art of Subclassing*: -- Raymond Hettinger + + +http://pyvideo.org/video/879/the-art-of-subclassing + + +"classes are for code re-use -- not creating taxonomies" + + +*Stop Writing Classes*: -- Jack Diederich + + +http://pyvideo.org/video/880/stop-writing-classes + +"I hate code: I want as little of it in our product as possible" + +and + +"If your class has only two methods and one of them is ``__init__`` -- you don't need a class" diff --git a/_sources/advanced_oo.txt b/_sources/advanced_oo.txt new file mode 100644 index 0000000..bb6b218 --- /dev/null +++ b/_sources/advanced_oo.txt @@ -0,0 +1,512 @@ +.. _advanced_oo: + +############################################ +Advanced Object Oriented Features of Python +############################################ + +- Chris Barker + + +``PythonCHB@gmail.com`` + + +Multiple Inheritance +##################### + + +Pulling methods from more than one class + + +multiple inheritance +--------------------- + +.. code-block:: python + + class Combined(Super1, Super2, Super3): + def __init__(self, something, something else): + Super1.__init__(self, ......) + Super2.__init__(self, ......) + Super3.__init__(self, ......) + +(calls to the super class' ``__init__`` are optional and case dependent) + + +.. nextslide:: + +**Method Resolution Order:** left to right + +1. Is it an instance attribute ? + +2. Is it a class attribute ? + +3. Is it a superclass attribute ? + + a. is it an attribute of the left-most superclass? + + b. is it an attribute of the next superclass? + + c. ``....`` + +4. Is it a super-superclass attribute ? + +5. also left to right... + + +( This can get complicated --- more on that later...) + +Mix-ins +-------- + +Why would you want to do this? + + +Hierarchies are not always simple: + + +* Animal + + * Mammal + + * GiveBirth() + + * Bird + + * LayEggs() + +Where do you put a Platypus? + + +Real World Example: ``wxPython FloatCanvas``: + +.. rst-class:: small + + https://github.com/wxWidgets/Phoenix/blob/master/wx/lib/floatcanvas/FCObjects.py + + +The Diamond Problem +-------------------- + +.. code-block:: python + + class A(object): + def do_your_stuff(self): + print("doing A's stuff") + + class B(A): + def do_your_stuff(self): + A.do_your_stuff(self) + print("doing B's stuff") + + class C(A): + def do_your_stuff(self): + A.do_your_stuff(self) + print("doing C's stuff") + + class D(B,C): + def do_your_stuff(self): + B.do_your_stuff(self) + C.do_your_stuff(self) + print("doing D's stuff") + + +The Diamond Problem +-------------------- + +Multiple paths to the same superclass: + +.. image:: /_static/Diamond_inheritance.png + :align: center + :height: 400px + +A's methods can get called twice. + +(demo: ``Examples/advancedOO/diamond.py``) + + +The Method Resolution Order +---------------------------- + +Python's The Method Resolution Order ( MRO ) is defined by the C3 +linearization algorithm: + +http://en.wikipedia.org/wiki/C3_linearization. + +In C3, only the last occurrence of a given class is retained. + +In short: corrects the multiple calls to the same method problem + +The classic description of modern MRO by Guido: + +http://www.python.org/download/releases/2.2.2/descrintro/#mro + +And one more: + +http://www.python.org/download/releases/2.3/mro/ + +demo: ``Examples/advancedOO/mro.py`` + +``super()`` +----------- + +``super()`` can handle the MRO for you dynamically + +Getting the superclass: + +.. code-block:: python + + class SafeVehicle(Vehicle): + """ + Safe Vehicle subclass of Vehicle base class... + """ + def __init__(self, position=0, velocity=0, icon='S'): + Vehicle.__init__(self, position, velocity, icon) + + +``Vehicle`` is repeated here -- what if we wanted to change the superclass? + +And there were a bunch of references to Vehicle? + + +super() +------- + +Getting the superclass: + +.. code-block:: python + + class SafeVehicle(Vehicle): + """ + Safe Vehicle subclass of Vehicle base class + """ + def __init__(self, position=0, velocity=0, icon='S'): + super().__init__(position, velocity, icon) + + +``super`` is about more than just making it easier to refactor. + +Remember the method resolution order? + +And the diamond problem? + + +What does super() do? +---------------------- + +``super`` returns a "proxy object" that delegates method calls. + +It's not returning the object itself -- but you can call methods on it. + +It runs through the method resolution order (MRO) to find the method +you call. + +Key point: the MRO is determined *at run time* + +https://docs.python.org/3.5/library/functions.html#super + + +.. nextslide:: + +Not the same as calling one superclass method: ``super()`` +will call all the sibling superclass methods: + +.. code-block:: python + + class D(C, B, A): + def __init__(self): + super().__init__() + +same as: + +.. code-block:: python + + class D(C, B, A): + def __init__(self): + C.__init__() + B.__init__() + A.__init__() + +You may not want that -- + +super() mechanics +------------------ + +In python3, you can usually call super() with no arguments: + +.. code-block:: python + + class B(A): + def a_method(self, *args, **kwargs) + super().a_method(*args, **kwargs) + +However, the actual signature is: + +.. code-block:: python + + super(type[, object-or-type]) + +and in py2, you needed to specify those: + +.. code-block:: python + + class B(A): + def a_method(self, *args, **kwargs) + super(B, self).a_method(*args, **kwargs) + +So why in the world do you need to specify both `B` (the type), and +`self` (the instance?) + +.. nextslide:: + +In py3, those two values are "magically" taken from context. + +But ``super()`` still needs to know that info. + +``super()`` determines the method resolution at run-time, so it needs to +know two things: + +* The mro of current *instance* +* The current *position* in the mro + +Note that while `self` needs to be a subclass of B here, it may not +actually be an *instance* of B -- it could be a subclass. + +That's why both need to be specified. + +Let's experiment with some of this: + +demo: ``Examples/advancedOO/super_test.ipnb`` + + +For more information about super() +---------------------------------- + +Two seminal articles about ``super()``: + + +"*Super Considered Harmful*" + + - James Knight + +https://fuhm.net/super-harmful + + +"*super() Considered Super!*" + + - Raymond Hettinger + + +http://rhettinger.wordpress.com/2011/05/26/super-considered-super + + +(Both worth reading....) + + +super() issues... +----------------- + +Both actually say similar things: + +* The method being called by super() needs to exist +* Every occurrence of the method needs to use super(): + + - Use it consistently, and document that you use it, as it is part + of the external interface for your class, like it or not. + +calling super(): +----------------- + +The caller and callee need to have a matching argument signature: + +Never call super with anything but the exact arguments you received, +unless you really know what you're doing. + +If you add one or more optional arguments, always accept + +.. code-block:: python + + *args, **kwargs + +and call super like + +.. code-block:: python + + super().method(args_declared, *args, **kwargs) + +LAB +---- + +In ``Examples/advancedOO/mixins.py``, you will find a few Vehicle classes +laid out in a hierarchy + +The log() method is defined on Vehicle then called on a couple of +instances + +Modify the class definition for Bike to mix in fancier log() method +from LoggingMixin + +Does the output change accordingly? If it didn't, look at the MRO for +Bike? Is it what you expected? + + +__new__ +######## + +.. rst-class:: large + + Into the depths of object creation: + +.. rst-class:: medium + + What *really* happens when a class instance is created? + +Class Creation +---------------- + +What happens when a class instance is created? + +This is the usual thing... + +.. code-block:: python + + class Class(): + def __init__(self, arg1, arg2): + self.arg1 = arg1 + self.arg2 = arg2 + ..... + +* A new instance is created +* ``__init__`` is called +* The code in ``__init__`` is run to initialize the instance + +Note that ``self`` is already an instance of the class. + +.. nextslide:: + +What if you need to do something before creation? + +Enter: ``__new__`` + +.. code-block:: python + + class Class(): + def __new__(cls, arg1, arg2): + some_code_here + return cls(...) + ... + +* ``__new__`` is called: it returns a new instance + +* The code in ``__new__`` is run to pre-initialize the instance + +* ``__init__`` is called + +* The code in ``__init__`` is run to initialize the instance + + +.. nextslide:: + +``__new__`` is a static method -- but it must be called with a class object as the first argument. + +.. code-block:: python + + class Class(superclass): + def __new__(cls, arg1, arg2): + some_code_here + return superclass.__new__(cls) + ..... + +``cls`` is the class object. + +The arguments (arg1, arg2) are what's passed in when calling the class. + +It needs to return a class instance -- usually by directly calling the superclass ``__new__`` + +If nothing else, you can call ``object.__new__`` (or ``super().__new__``) + + +When to use ``__new__`` +------------------------ + + +When would you need to use it: + +* Subclassing an immutable type: + + - It's too late to change it once you get to ``__init__`` + +* When ``__init__`` is not called: + + - unpickling + + - copying + +You may need to put some code in ``__new__`` to make sure things +go right + +More detail here: + +https://docs.python.org/3/reference/datamodel.html#object.__new__ + + +LAB +---- + +**Demo:** + +``Examples/advancedOO/new_example.py`` + +**Exercise:** + +Write a subclass of int that will always be an even number: +round the input to the closest even number: + +``Examples/advancedOO/even_int.py`` + + +``Examples/advancedOO/test_even_int.py`` + + +Wrap Up +------- + +Thinking OO in Python: + + +Think about what makes sense for your code: + +* Code re-use +* Clean APIs +* ... + + +Don't be a slave to what OO is *supposed to look like*. + + +Let OO work for you, not *create* work for you. + + +Wrap Up +-------- + +OO in Python: + + +*The Art of Subclassing*: -- Raymond Hettinger + + +http://pyvideo.org/video/879/the-art-of-subclassing + + +"classes are for code re-use -- not creating taxonomies" + + +*Stop Writing Classes*: -- Jack Diederich + + +http://pyvideo.org/video/880/stop-writing-classes + +"I hate code: I want as little of it in our product as possible" + +and + +"If your class has only two methods and one of them is ``__init__`` -- you don't need a class" diff --git a/_sources/code_review.rst.txt b/_sources/code_review.rst.txt new file mode 100644 index 0000000..b523ec1 --- /dev/null +++ b/_sources/code_review.rst.txt @@ -0,0 +1,51 @@ +.. _code_review: + +############ +Code Reviews +############ + + +Why code review +=============== +As professional developers, we need to always be learning and improving. + +Code review is one of the best tools for this. + +Code review is like having a personal tutor. + + +Getting code ready for review +----------------------------- + +- First draft is messy +- Refactor +- Hope for no red marks, but expect they will be there +- To get the most out of it, correct all that you can first +- Code to review should be between 200 and 400 lines of code + + +what to look for +---------------- + +- readability +- 'pythonic' +- tests +- short functions/methods +- anything not clear + + +Types of code review +-------------------- + +- in person +- in-line comments +- using tools https://en.wikipedia.org/wiki/List_of_tools_for_code_review + + +When refactoring or doing code reviews in person +------------------------------------------------ +If code is unclear: + +- write a comment explaining what the code is doing +- work on making the code clearer +- goal to make it clear enough to get rid of the comment(s) diff --git a/_sources/code_review.txt b/_sources/code_review.txt new file mode 100644 index 0000000..b523ec1 --- /dev/null +++ b/_sources/code_review.txt @@ -0,0 +1,51 @@ +.. _code_review: + +############ +Code Reviews +############ + + +Why code review +=============== +As professional developers, we need to always be learning and improving. + +Code review is one of the best tools for this. + +Code review is like having a personal tutor. + + +Getting code ready for review +----------------------------- + +- First draft is messy +- Refactor +- Hope for no red marks, but expect they will be there +- To get the most out of it, correct all that you can first +- Code to review should be between 200 and 400 lines of code + + +what to look for +---------------- + +- readability +- 'pythonic' +- tests +- short functions/methods +- anything not clear + + +Types of code review +-------------------- + +- in person +- in-line comments +- using tools https://en.wikipedia.org/wiki/List_of_tools_for_code_review + + +When refactoring or doing code reviews in person +------------------------------------------------ +If code is unclear: + +- write a comment explaining what the code is doing +- work on making the code clearer +- goal to make it clear enough to get rid of the comment(s) diff --git a/_sources/datetime.rst.txt b/_sources/datetime.rst.txt new file mode 100644 index 0000000..b3c88e0 --- /dev/null +++ b/_sources/datetime.rst.txt @@ -0,0 +1,364 @@ +.. _datetime: + +######## +Datetime +######## + +What's so hard about representing time? +####################################### + +Humans are mostly accustomed to communicating in their local timezone. + +There are hundreds of them, whose definitions change + +What happens to your application when it's deployed to a server running +in a different timezone? + +The length of a day is not constant, and `leap +seconds `__ have been inserted +frequently + +Daylight savings time challenges + +`DST `__ +----------------------------------------------------------- + +Daylight savings time creates discontinuities in a time series + +.. image:: /_static/dst_sf_graph.png + :height: 250px + +.. image:: /_static/dst_fb_graph.png + :height: 250px + +On representing a point in time +------------------------------- + +- There are two major time standards which you should understand: + + - `UTC: Coordinated Universal Time (French: Temps Universel + Coordonné) `__ + - `UNIX time `__ + + +.. nextslide:: + +`UTC: Coordinated Universal Time (French: Temps Universel +`__ + + +This is the primary time standard in the world + +Provides a consistent reference all other time zones can relate to + +No daylight savings time + +Also known as Zulu Time, as the UTC time zone is sometimes denoted by +the letter Z, e.g. 12:59Z + +It is similar to, but not the same as, Greenwich Mean Time (GMT). UTC is +more precisely defined + +.. nextslide:: + +.. image:: /_static/Greenwich_clock.jpg + :height: 500px + +`UNIX time `__ +------------------------------------------------------ + +- UNIX time is represented as the number of seconds from one point in + time known as the Epoch, defined to be 00:00 UTC January 1, 1970, not + counting leap seconds +- Note that UNIX time is based on another time standard +- On 32 bit OSes which store time as a signed 32-bit integer, the + largest value that can be stored is 03:14:07 UTC on Tuesday, 19 + January 2038. One second afterwards, time overflows back to zero. + This is known as the Y2038 problem. +- On 64 bit machines, the largest time which can be represented is in + the year 292 billion, at which time our universe is predicted to not + exist + + +timezones and datetime calculations +----------------------------------- + +Calculating the time between two dates spanning timezones, daylight +savings time transitions, and leap seconds is a task fraught with error. + +The `Olson database `__ is a +reference database of the world's timezones. + +It can be obtained through the IANA +`here. `__ + +As stated in the `datetime +docs `__, + +The rules for time adjustment across the world are more political than rational + +See: John Oliver -- Daylight Saving Time - How Is This Still A Thing? + +https://youtu.be/br0NW9ufUUw + + +from datetime import time, date, datetime +----------------------------------------- + +The datetime object represents a specific moment in time + +The date object represents a calendar date + +The time object represents a time + +:: + + from datetime import datetime + + t = datetime(2019, 11, 1) + date = t.date() + + now = datetime.now() + + +limitations of datetime +----------------------- + +precision is limited to microseconds and time range is limited: + + :: + + In [3]: datetime.MINYEAR + Out[3]: 1 + In [4]: datetime.MAXYEAR + Out[4]: 9999 + In [5]: datetime.datetime(37337,1,1) + --------------------------------------------------------------------------- + ValueError Traceback (most recent call last) + in () + + ValueError: year is out of range + + In [6]: datetime.datetime(0,1,1) + --------------------------------------------------------------------------- + ValueError Traceback (most recent call last) + in () + + ValueError: year is out of range + + + +datetime -> string +------------------ + +:: + + from datetime import datetime + t = datetime.now() + t.isoformat() + t.strftime("Date: %B %d, %Y. Time: %H:%M") + +strftime passes format codes to the strftime of the platform's C +library. This may not be standardized! A list of format directives is +`here `__ + +getting the current UNIX time : + +:: + + datetime.now().timestamp() + # in python 2: + int(datetime.datetime.now().strftime('%s')) + + +string -> datetime +------------------ + +datetime.strptime(string, format) + +:: + + In [88]: datetime.datetime.strptime? + Type: builtin_function_or_method + String form: + Docstring: string, format -> new datetime parsed from a string (like time.strptime()). + + time_string = "2019/11/1 2300" + format = "%Y/%m/%d %H%M" + datetime.datetime.strptime(time_string, format) + +strptime format strings use the same formatting tokens as strftime + +If you need to parse arbitrary time strings, or don't want to maintain +format strings, there is +`python-dateutil.parser `__ + + +two types of datetimes - naive and aware +---------------------------------------- + + +so far we haven't created any datetime objects with associated timezone +information, these are known as 'naive' datetimes + +in order to accurately represent a real time, timezone information is +required + +Some systems may expect naive times to represent UTC, others may decide +on local time zone. It's usually safer to be explicit. + +.. nextslide:: + +Timezone is specified with the tzinfo attribute, through the +constructor: datetimes are immutable + +Change the timezone or another attribute with the replace(\*args, +\*\*kwargs) method to create a new object with all the same attributes +except those specified + +tzinfo is expected to be a subclass of datetime.tzinfo + +However, datetime.tzinfo is an abstract base class, which means you'll +need to define your own subclass (filled with peril) or install the pytz +package + +pytz is based on the `Olson +database `__, a reference +database of the world's timezones. + + +pytz +---- + +A list of all the timezones in pytz is available in pytz.all\_timezones + +Timezone is often specified as a UTC offset in hours, e.g. +2019-11-1T11:59-08:00. Don't use this offset directly to permanently +record a user's timezone because it may change with Daylight Savings +Time. Better to obtain the IANA code from the list in pytz and let +datetime do the calculations. + +:: + + from datetime import datetime + + import pytz + + t1 = datetime(2019,11,1, tzinfo=pytz.UTC) + + us_pacific_tz = pytz.timezone('US/Pacific') + t2 = datetime(2019,11,1, tzinfo=us_pacific_tz) + + +time calculations +----------------- + +datetime.timedelta([days[, seconds[, microseconds[, milliseconds[, +minutes[, hours[, weeks]]]]]]]) represents a time difference + +All the datetime objects overload the arithmetic operators, such that +adding or subtracting dates will return a datetime.timedelta object + +:: + + In [17]: datetime.datetime.now() - datetime.datetime.now() + Out[17]: datetime.timedelta(-1, 86399, 999987) + +adding timedelta to a datetime will result in a new datetime + +:: + + In [19]: datetime.datetime(2019,11,1, tzinfo=pytz.UTC) + datetime.timedelta(days=1) + Out[19]: datetime.datetime(2019, 11, 2, 0, 0) + + # or create a shortcut for creating a UTC timezone aware datetime: + import functools + utc = functools.partial(datetime.datetime, tzinfo=pytz.UTC) + utc(2019,11,1,12,0) + datetime.timedelta(days=1) + + +time calculations +----------------- + +timedelta has a few functions and properties to retrieve the results: +timedelta.total\_seconds(), timedelta.days, timedelta.seconds, +timedelta.microseconds + +datetimes in the calculations must be all naive or all aware + +:: + + In [35]: datetime.datetime(2019,11,1,tzinfo=pytz.UTC) - datetime.datetime(2019,10,1) + --------------------------------------------------------------------------- + TypeError Traceback (most recent call last) + in () + TypeError: can't subtract offset-naive and offset-aware datetimes + + +Storing datetimes +----------------- + +Try to deal in UTC as much as possible. Do not depend on the machine's interpretation of local time. +For instance, datetime.now() on my Mac will return a naive datetime containing the local time. Alternatively: + +:: + + # return the current time as an aware datetime in UTC: + datetime.datetime.now(pytz.UTC) + # this will return a naive datetime with the current UTC time + datetime.datetime.utcnow() + + +.. nextslide:: + +When storing a datetime in a database, it will need to be translated +into the database's native format. Depending on the database driver, it +may or may not accept datetimes. If it does, it may or may not respect +the timezone information + +Discovering the behavior is part of your database integration work + + +datetimes in sqlite3 +-------------------- + +From the `sqlite3 docs `__: + +:: + + Dates and times in sqlite3 are stored as TEXT, REAL, or INTEGER values + + TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS"). + REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar. + INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC. + +So Python datetime objects must be translated to these types + + +.. nextslide:: + +sqlite3 has support for "adapters" and "converters" to translate types +going in and out of the DB + +`Default adapters and +converters `__ +are supplied for date and datetime objects + +unfortunately, the default implementation does not handle timezone aware +datetimes, but they are simply implementable + + +Python Date and Time Useful links +--------------------------------- + +http://agiliq.com/blog/2009/02/understanding-datetime-tzinfo-timedelta-amp-timezo/ + +https://pymotw.com/3/calendar/index.html + +https://pymotw.com/3/datetime/index.html + +Dealing with DST and the same time twice in one day +https://www.python.org/dev/peps/pep-0495/ + +Questions? +---------- diff --git a/_sources/datetime.txt b/_sources/datetime.txt new file mode 100644 index 0000000..b3c88e0 --- /dev/null +++ b/_sources/datetime.txt @@ -0,0 +1,364 @@ +.. _datetime: + +######## +Datetime +######## + +What's so hard about representing time? +####################################### + +Humans are mostly accustomed to communicating in their local timezone. + +There are hundreds of them, whose definitions change + +What happens to your application when it's deployed to a server running +in a different timezone? + +The length of a day is not constant, and `leap +seconds `__ have been inserted +frequently + +Daylight savings time challenges + +`DST `__ +----------------------------------------------------------- + +Daylight savings time creates discontinuities in a time series + +.. image:: /_static/dst_sf_graph.png + :height: 250px + +.. image:: /_static/dst_fb_graph.png + :height: 250px + +On representing a point in time +------------------------------- + +- There are two major time standards which you should understand: + + - `UTC: Coordinated Universal Time (French: Temps Universel + Coordonné) `__ + - `UNIX time `__ + + +.. nextslide:: + +`UTC: Coordinated Universal Time (French: Temps Universel +`__ + + +This is the primary time standard in the world + +Provides a consistent reference all other time zones can relate to + +No daylight savings time + +Also known as Zulu Time, as the UTC time zone is sometimes denoted by +the letter Z, e.g. 12:59Z + +It is similar to, but not the same as, Greenwich Mean Time (GMT). UTC is +more precisely defined + +.. nextslide:: + +.. image:: /_static/Greenwich_clock.jpg + :height: 500px + +`UNIX time `__ +------------------------------------------------------ + +- UNIX time is represented as the number of seconds from one point in + time known as the Epoch, defined to be 00:00 UTC January 1, 1970, not + counting leap seconds +- Note that UNIX time is based on another time standard +- On 32 bit OSes which store time as a signed 32-bit integer, the + largest value that can be stored is 03:14:07 UTC on Tuesday, 19 + January 2038. One second afterwards, time overflows back to zero. + This is known as the Y2038 problem. +- On 64 bit machines, the largest time which can be represented is in + the year 292 billion, at which time our universe is predicted to not + exist + + +timezones and datetime calculations +----------------------------------- + +Calculating the time between two dates spanning timezones, daylight +savings time transitions, and leap seconds is a task fraught with error. + +The `Olson database `__ is a +reference database of the world's timezones. + +It can be obtained through the IANA +`here. `__ + +As stated in the `datetime +docs `__, + +The rules for time adjustment across the world are more political than rational + +See: John Oliver -- Daylight Saving Time - How Is This Still A Thing? + +https://youtu.be/br0NW9ufUUw + + +from datetime import time, date, datetime +----------------------------------------- + +The datetime object represents a specific moment in time + +The date object represents a calendar date + +The time object represents a time + +:: + + from datetime import datetime + + t = datetime(2019, 11, 1) + date = t.date() + + now = datetime.now() + + +limitations of datetime +----------------------- + +precision is limited to microseconds and time range is limited: + + :: + + In [3]: datetime.MINYEAR + Out[3]: 1 + In [4]: datetime.MAXYEAR + Out[4]: 9999 + In [5]: datetime.datetime(37337,1,1) + --------------------------------------------------------------------------- + ValueError Traceback (most recent call last) + in () + + ValueError: year is out of range + + In [6]: datetime.datetime(0,1,1) + --------------------------------------------------------------------------- + ValueError Traceback (most recent call last) + in () + + ValueError: year is out of range + + + +datetime -> string +------------------ + +:: + + from datetime import datetime + t = datetime.now() + t.isoformat() + t.strftime("Date: %B %d, %Y. Time: %H:%M") + +strftime passes format codes to the strftime of the platform's C +library. This may not be standardized! A list of format directives is +`here `__ + +getting the current UNIX time : + +:: + + datetime.now().timestamp() + # in python 2: + int(datetime.datetime.now().strftime('%s')) + + +string -> datetime +------------------ + +datetime.strptime(string, format) + +:: + + In [88]: datetime.datetime.strptime? + Type: builtin_function_or_method + String form: + Docstring: string, format -> new datetime parsed from a string (like time.strptime()). + + time_string = "2019/11/1 2300" + format = "%Y/%m/%d %H%M" + datetime.datetime.strptime(time_string, format) + +strptime format strings use the same formatting tokens as strftime + +If you need to parse arbitrary time strings, or don't want to maintain +format strings, there is +`python-dateutil.parser `__ + + +two types of datetimes - naive and aware +---------------------------------------- + + +so far we haven't created any datetime objects with associated timezone +information, these are known as 'naive' datetimes + +in order to accurately represent a real time, timezone information is +required + +Some systems may expect naive times to represent UTC, others may decide +on local time zone. It's usually safer to be explicit. + +.. nextslide:: + +Timezone is specified with the tzinfo attribute, through the +constructor: datetimes are immutable + +Change the timezone or another attribute with the replace(\*args, +\*\*kwargs) method to create a new object with all the same attributes +except those specified + +tzinfo is expected to be a subclass of datetime.tzinfo + +However, datetime.tzinfo is an abstract base class, which means you'll +need to define your own subclass (filled with peril) or install the pytz +package + +pytz is based on the `Olson +database `__, a reference +database of the world's timezones. + + +pytz +---- + +A list of all the timezones in pytz is available in pytz.all\_timezones + +Timezone is often specified as a UTC offset in hours, e.g. +2019-11-1T11:59-08:00. Don't use this offset directly to permanently +record a user's timezone because it may change with Daylight Savings +Time. Better to obtain the IANA code from the list in pytz and let +datetime do the calculations. + +:: + + from datetime import datetime + + import pytz + + t1 = datetime(2019,11,1, tzinfo=pytz.UTC) + + us_pacific_tz = pytz.timezone('US/Pacific') + t2 = datetime(2019,11,1, tzinfo=us_pacific_tz) + + +time calculations +----------------- + +datetime.timedelta([days[, seconds[, microseconds[, milliseconds[, +minutes[, hours[, weeks]]]]]]]) represents a time difference + +All the datetime objects overload the arithmetic operators, such that +adding or subtracting dates will return a datetime.timedelta object + +:: + + In [17]: datetime.datetime.now() - datetime.datetime.now() + Out[17]: datetime.timedelta(-1, 86399, 999987) + +adding timedelta to a datetime will result in a new datetime + +:: + + In [19]: datetime.datetime(2019,11,1, tzinfo=pytz.UTC) + datetime.timedelta(days=1) + Out[19]: datetime.datetime(2019, 11, 2, 0, 0) + + # or create a shortcut for creating a UTC timezone aware datetime: + import functools + utc = functools.partial(datetime.datetime, tzinfo=pytz.UTC) + utc(2019,11,1,12,0) + datetime.timedelta(days=1) + + +time calculations +----------------- + +timedelta has a few functions and properties to retrieve the results: +timedelta.total\_seconds(), timedelta.days, timedelta.seconds, +timedelta.microseconds + +datetimes in the calculations must be all naive or all aware + +:: + + In [35]: datetime.datetime(2019,11,1,tzinfo=pytz.UTC) - datetime.datetime(2019,10,1) + --------------------------------------------------------------------------- + TypeError Traceback (most recent call last) + in () + TypeError: can't subtract offset-naive and offset-aware datetimes + + +Storing datetimes +----------------- + +Try to deal in UTC as much as possible. Do not depend on the machine's interpretation of local time. +For instance, datetime.now() on my Mac will return a naive datetime containing the local time. Alternatively: + +:: + + # return the current time as an aware datetime in UTC: + datetime.datetime.now(pytz.UTC) + # this will return a naive datetime with the current UTC time + datetime.datetime.utcnow() + + +.. nextslide:: + +When storing a datetime in a database, it will need to be translated +into the database's native format. Depending on the database driver, it +may or may not accept datetimes. If it does, it may or may not respect +the timezone information + +Discovering the behavior is part of your database integration work + + +datetimes in sqlite3 +-------------------- + +From the `sqlite3 docs `__: + +:: + + Dates and times in sqlite3 are stored as TEXT, REAL, or INTEGER values + + TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS"). + REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar. + INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC. + +So Python datetime objects must be translated to these types + + +.. nextslide:: + +sqlite3 has support for "adapters" and "converters" to translate types +going in and out of the DB + +`Default adapters and +converters `__ +are supplied for date and datetime objects + +unfortunately, the default implementation does not handle timezone aware +datetimes, but they are simply implementable + + +Python Date and Time Useful links +--------------------------------- + +http://agiliq.com/blog/2009/02/understanding-datetime-tzinfo-timedelta-amp-timezo/ + +https://pymotw.com/3/calendar/index.html + +https://pymotw.com/3/datetime/index.html + +Dealing with DST and the same time twice in one day +https://www.python.org/dev/peps/pep-0495/ + +Questions? +---------- diff --git a/_sources/debugging.rst.txt b/_sources/debugging.rst.txt new file mode 100644 index 0000000..2532318 --- /dev/null +++ b/_sources/debugging.rst.txt @@ -0,0 +1,656 @@ +.. _debugging: + +######### +Debugging +######### + +System Development with Python + +- Maria McKinley + + +``parody@uw.edu`` + + +Topics +###### + + +- The call stack +- Exceptions +- Debugging + + +The Call Stack +-------------- + +- A stack is a Last-In-First-Out (LIFO) data structure (stack of plates) +- The call stack is a stack data structure that stores information + about the current active function call +- The objects in the stack are known as "stack frames". Each frame + contains the arguments passed to the function, space for local + variables, and the return address +- It is usually (unintuitively) displayed like an upside-down stack of + plates, with most recent frame on the bottom. +- When a function is called, a stack frame is created for it and pushed + onto the stack +- When a function returns, it is popped off the stack and control is + passed to the next item in the stack. If the stack is empty, the + program exits + +http://www.pythontutor.com/visualize.html#mode=edit + +.. nextslide:: + +Visualize the stack! +-------------------- + +.. image:: /_static/program_callstack.png + :height: 580 px + +.. nextslide:: + +.. rubric:: How deep can that stack be? + :name: how-deep-can-that-stack-be + +:: + + i = 0 + + def recurse(): + global i + i += 1 + print i + recurse() + + recurse() + + +That value can be changed with sys.setrecursionlimit(N) + +If we try to put more than sys.getrecursionlimit() frames on the stack, we get a RuntimeError, which is python's version of StackOverflow + +.. nextslide:: + +.. code-block:: ipython + + import inspect + + def recurse(limit): + local_variable = '.' * limit + print(limit, inspect.getargvalues(inspect.currentframe())) + if limit <= 0: + return + recurse(limit - 1) + return + + if __name__ == '__main__': + recurse(3) + + +module https://docs.python.org/3/library/inspect.html + +.. nextslide:: + +Exceptions +---------- + +It's easier to ask for forgiveness than permission + +When either the interpreter or your own code detects an error condition, +an exception will be raised + +The exception will bubble up the call stack until it is handled. If it's +not handled by the bottom of the stack, the interpreter will exit the program. + +.. nextslide:: + +At each level in the stack, a handler can either: + +- let it bubble through (the default) +- swallow the exception +- catch the exception and raise it again +- catch the exception and raise a new one + +.. nextslide:: + +.. rubric:: Handling exceptions + :name: handling-exceptions + +The most basic form uses the builtins try and except + +:: + + def temp_f_to_c(var): + try: + return(float(var) - 32)/1.8000 + except ValueError as e: + print("The argument does not contain numbers\n", e) + + +.. nextslide:: + +.. rubric:: A few more builtins for exception handling: finally, else, + and raise + :name: a-few-more-builtins-for-exception-handling-finally-else-and-raise + +:: + + try: + result = x / y + except (ZeroDivisionError, ValueError) as e: + print("caught division error or maybe a value error:\n", e) + except Exception as e: + errno, strerror = e.args + print("I/O error({0}): {1}".format(errno,strerror)) + # or you can just print e + print("unhandled exception:\n", e) + raise + else: + print("everything worked great") + return result + finally: + print("this is executed no matter what") + print('this is only printed if there is no exception') + + +.. nextslide:: + +It is even possible to use a try block without the exception clause: + +:: + + try: + 5/0 + finally: + print('did it work?') + + +.. nextslide:: + +.. rubric:: Built-in exceptions + :name: built-in-exceptions + +:: + + [name for name in dir(__builtin__) if "Error" in name] + + +If one of these meets your needs, by all means use it. You can add messages: + +:: + + raise SyntaxError("That was a mispelling") + +If no builtin exceptions work, define a new exception type by subclassing Exception. + +:: + + class MyException(Exception): + pass + + raise MyException("An exception doesn't always prove the rule!") + +.. nextslide:: + +It is possible, but discouraged to catch all exceptions. + +:: + + try: + my_cool_code() + except: + print('no idea what the exceptions is, but I caught it') + + +An exception to this exception rule is when you are running a service that should not ever crash, +like a web server. + +In this case, it is extremely important to have very good logging so that you +have reports of exactly what happened and what exception should have been thrown. + +.. nextslide:: + +.. rubric:: Further reading + :name: further-reading + +- http://docs.python.org/3/library/exceptions.html +- http://docs.python.org/3/tutorial/errors.html + +.. nextslide:: + +Debugging +--------- + +.. rubric:: Python Debugging + :name: python-debugging + +You will spend most of your time as a developer debugging. +You will spend more time than you expect on google. + + +.. nextslide:: + +The Stack Trace + +You already know what it looks like. Simple traceback: + +:: + + $ python3 test_trie.py + Traceback (most recent call last): + File "test_trie.py", line 3, in + from trie import Trie + File "/Users/maria/python/trie/trie.py", line 144 + print "end of word", node.value + ^ + SyntaxError: Missing parentheses in call to 'print' + +.. nextslide:: + +But things can quickly get complicated (Here is ~1/3 of a recent traceback I had): + +Traceback (most recent call last): + File "snapi3/tests/test_proxy_rest.py", line 21, in test_http_get + resp = self.app.get(self.TRIVIAL_URL, status=200) + File "python3/lib/python3.5/site-packages/webtest/app.py", line 323, in get + expect_errors=expect_errors) + File "python3/lib/python3.5/site-packages/webtest/app.py", line 606, in do_request + res = req.get_response(app, catch_exc_info=True) + File "python3/lib/python3.5/site-packages/webob/request.py", line 1313, in send + application, catch_exc_info=True) + File "python3/lib/python3.5/site-packages/webob/request.py", line 1284, in call_application + output.extend(app_iter) + +.. nextslide:: + +Debuggers are code which allows the inspection of state of other code +during runtime. + +Rudimentary tools + +- print() +- interpreter hints +- import logging.debug +- assert() + +.. nextslide:: + +Console debuggers + +- pdb/ipdb + +GUI debuggers + +- Winpdb +- IDEs: Eclipse, Wing IDE, PyCharm, Visual Studio + +.. nextslide:: + +.. rubric:: help from the interpreter + :name: help-from-the-interpreter + +1. investigate import issues with -v: + +:: + + python -v myscript.py + + +Verbose (trace import statements) + + +2. inspect environment after running script with -i + +:: + + python -i myscript.py + + +Forces interpreter to remain active, and still in scope + +.. nextslide:: + +.. rubric:: `Pdb - The Python + Debugger `__ + :name: pdb---the-python-debugger + +Pros: + +- You have it already, ships with the standard library +- Easy remote debugging +- Works with any development environment + +Cons: + +- Steep-ish learning curve +- Easy to get lost in a deep stack +- Watching variables isn't hard, but non-trivial + +.. nextslide:: + +.. rubric:: `Pdb - The Python + Debugger `__ + :name: pdb---the-python-debugger-1 + +The 4-fold ways of invoking pdb + +- Postmortem mode +- Run mode +- Script mode +- Trace mode + +Note: in most cases where you see the word 'pdb' in the examples, you +can replace it with 'ipdb'. ipdb is the ipython enhanced version of pdb +which is mostly compatible, and generally easier to work with. But it +doesn't ship with Python. + +.. nextslide:: + +.. rubric:: Postmortem mode + :name: postmortem-mode + +For analyzing crashes due to uncaught exceptions + +:: + + python -i script.py + import pdb; pdb.pm() + +.. nextslide:: + +.. rubric:: Run mode + :name: run-mode + +:: + + pdb.run('some.expression()') + +.. nextslide:: + +.. rubric:: Script mode + :name: script-mode + +:: + + python -m pdb script.py + + +"-m [module]" finds [module] in sys.path and executes it as a script + + +.. nextslide:: + +.. rubric:: Trace mode + :name: trace-mode + +Insert the following line into your code where you want execution to +halt: + +:: + + import pdb; pdb.set_trace() + + +It's not always OK/possible to modify your code in order to debug it, +but this is often the quickest way to begin inspecting state + +.. nextslide:: + +.. rubric:: pdb in ipython + :name: pdb-in-ipython + +:: + + + In [2]: pdb + Automatic pdb calling has been turned ON + + %run app.py + + # now halts execution on uncaught exception + +If you forget to turn on pdb, the magic command %debug will activate the +debugger (in 'post-mortem mode'). + +.. nextslide:: + +.. rubric:: Navigating pdb + :name: navigating-pdb + +The goal of each of the preceding techniques was to get to the pdb +prompt and get to work inspecting state. Most commands can be short-cutted +to the first letter. + +:: + + % python -m pdb define.py + pdb> args # print arguments and values to current function + pdb> pp a_variable # pretty-print a_variable + pdb> where # print stack trace, bottom is most recent command + pdb> list # list the code including and surrounding the current running code + + +.. nextslide:: + +To repeat the current command, press only the Enter key + +:: + + # execute until current function returns + pdb> return + # Execute the current line, stop at the first possible occasion + pdb> step + # Continue execution until the next line in the current function is reached or it returns. + pdb> next + # Continue execution until the line with a number greater than the current one is reached + or until the current frame returns. Good for exiting loops. + pdb> until + # move one level up the stack + pdb> up + # move one level down the stack + pdb> down + pdb> continue # goes until next breakpoint or end of program + # advanced: create commands to be executed on a breakpoint + pdb> commands + + +.. nextslide:: + +.. rubric:: Breakpoints + :name: breakpoints + +:: + + pdb> help break + b(reak) ([file:]lineno | function) [, condition] + With a line number argument, set a break there in the current + file. With a function name, set a break at first executable line + of that function. Without argument, list all breaks. If a second + argument is present, it is a string specifying an expression + which must evaluate to true before the breakpoint is honored. + + The line number may be prefixed with a filename and a colon, + to specify a breakpoint in another file (probably one that + hasn't been loaded yet). The file is searched for on sys.path; + the .py suffix may be omitted. + + +.. nextslide:: + +Can use up, down, where and list to evalutate where you are, and use that to +set a new breakpoint in code coming up. Useful for getting out of rabbit holes. + +:: + + pdb> break api.py:21 set a breakpoint file:line # + pdb> break # list breakpoints + pdb> clear 1 # get rid of first breakpoint + pdb> break 35 # set a breakpoint in current file at line 35 + # print lines in range + pdb> list 1,28 + + +.. nextslide:: + +Clear (delete) breakpoints + +:: + + clear [bpnumber [bpnumber...]] + + +disable breakpoints + +:: + + disable [bpnumber [bpnumber...]] + + +enable breakpoints + +:: + + enable [bpnumber [bpnumber...]] + + +.. nextslide:: + +.. rubric:: Conditional Breakpoints + :name: conditional-breakpoints + +:: + + pdb> help condition + condition bpnumber str_condition + str_condition is a string specifying an expression which + must evaluate to true before the breakpoint is honored. + If str_condition is absent, any existing condition is removed; + i.e., the breakpoint is made unconditional. + + +.. nextslide:: + +.. rubric:: Invoking pdb with nose + :name: invoking-pdb-with-nose + +On error condition, drop to pdb + +:: + + nosetests --pdb + + +On test failure, drop to pdb: + +:: + + nosetests --pdb-failures + + +.. nextslide:: + +.. rubric:: Python IDEs + :name: python-ides + +.. rubric:: PyCharm + :name: pycharm + +From JetBrains, and integrates some of their vast array of development +tools + +Free Community Edition (CE) is available + +Good visual debugging support + +.. nextslide:: + +.. rubric:: Eclipse + :name: eclipse + +A multi-language IDE + +Python support via http://pydev.org/ + +Automatic variable and expression watching + +Supports a lot of debugging features like conditional breakpoints, +provided you look in the right places! + +Further reading + +http://pydev.org/manual_adv_debugger.html + +.. nextslide:: + +.. rubric:: winpdb + :name: winpdb + +A multi platform Python debugger with threading support + +Easier to start up and get debugging + +:: + + + winpdb your_app.py + + +.. rubric:: Remote debugging with winpdb + :name: remote-debugging-with-winpdb + +.. nextslide:: + +To debug an application running a different Python, even remotely: + +:: + + + import rpdb2; rpdb2.start_embedded_debugger("password") + + + +http://winpdb.org/tutorial/WinpdbTutorial.html + +.. nextslide:: + +.. rubric:: Debugging exercise + :name: debugging-exercise + +Find the wikidef app in the examples folder + +See if you can find the bug and get the app working. Use whatever debugging +technique(s) you prefer. + +To run the app: +:: + + python define.py interesting_topic + +where interesting_topic is a topic of interest. ;-) + +.. nextslide:: + +Once it is working again: +Using (i)pdb in module mode (python -m pdb ) to find the server type that +wikipedia is using by looking at +response.headers.headers in Wikipedia.article + +You can enter the debugger by running + +:: + + python -m pdb ./define.py robot + +(define.py takes the first sys arg and finds articles on wikipedia on that topic) + +You can get to the code by walking through each line with 's'tep and +'n'ext commands, or by setting a breakpoint and 'c'ontinuing. + +What's the result? + +.. nextslide:: + +.. rubric:: Questions? + :name: questions + diff --git a/_sources/debugging.txt b/_sources/debugging.txt new file mode 100644 index 0000000..2532318 --- /dev/null +++ b/_sources/debugging.txt @@ -0,0 +1,656 @@ +.. _debugging: + +######### +Debugging +######### + +System Development with Python + +- Maria McKinley + + +``parody@uw.edu`` + + +Topics +###### + + +- The call stack +- Exceptions +- Debugging + + +The Call Stack +-------------- + +- A stack is a Last-In-First-Out (LIFO) data structure (stack of plates) +- The call stack is a stack data structure that stores information + about the current active function call +- The objects in the stack are known as "stack frames". Each frame + contains the arguments passed to the function, space for local + variables, and the return address +- It is usually (unintuitively) displayed like an upside-down stack of + plates, with most recent frame on the bottom. +- When a function is called, a stack frame is created for it and pushed + onto the stack +- When a function returns, it is popped off the stack and control is + passed to the next item in the stack. If the stack is empty, the + program exits + +http://www.pythontutor.com/visualize.html#mode=edit + +.. nextslide:: + +Visualize the stack! +-------------------- + +.. image:: /_static/program_callstack.png + :height: 580 px + +.. nextslide:: + +.. rubric:: How deep can that stack be? + :name: how-deep-can-that-stack-be + +:: + + i = 0 + + def recurse(): + global i + i += 1 + print i + recurse() + + recurse() + + +That value can be changed with sys.setrecursionlimit(N) + +If we try to put more than sys.getrecursionlimit() frames on the stack, we get a RuntimeError, which is python's version of StackOverflow + +.. nextslide:: + +.. code-block:: ipython + + import inspect + + def recurse(limit): + local_variable = '.' * limit + print(limit, inspect.getargvalues(inspect.currentframe())) + if limit <= 0: + return + recurse(limit - 1) + return + + if __name__ == '__main__': + recurse(3) + + +module https://docs.python.org/3/library/inspect.html + +.. nextslide:: + +Exceptions +---------- + +It's easier to ask for forgiveness than permission + +When either the interpreter or your own code detects an error condition, +an exception will be raised + +The exception will bubble up the call stack until it is handled. If it's +not handled by the bottom of the stack, the interpreter will exit the program. + +.. nextslide:: + +At each level in the stack, a handler can either: + +- let it bubble through (the default) +- swallow the exception +- catch the exception and raise it again +- catch the exception and raise a new one + +.. nextslide:: + +.. rubric:: Handling exceptions + :name: handling-exceptions + +The most basic form uses the builtins try and except + +:: + + def temp_f_to_c(var): + try: + return(float(var) - 32)/1.8000 + except ValueError as e: + print("The argument does not contain numbers\n", e) + + +.. nextslide:: + +.. rubric:: A few more builtins for exception handling: finally, else, + and raise + :name: a-few-more-builtins-for-exception-handling-finally-else-and-raise + +:: + + try: + result = x / y + except (ZeroDivisionError, ValueError) as e: + print("caught division error or maybe a value error:\n", e) + except Exception as e: + errno, strerror = e.args + print("I/O error({0}): {1}".format(errno,strerror)) + # or you can just print e + print("unhandled exception:\n", e) + raise + else: + print("everything worked great") + return result + finally: + print("this is executed no matter what") + print('this is only printed if there is no exception') + + +.. nextslide:: + +It is even possible to use a try block without the exception clause: + +:: + + try: + 5/0 + finally: + print('did it work?') + + +.. nextslide:: + +.. rubric:: Built-in exceptions + :name: built-in-exceptions + +:: + + [name for name in dir(__builtin__) if "Error" in name] + + +If one of these meets your needs, by all means use it. You can add messages: + +:: + + raise SyntaxError("That was a mispelling") + +If no builtin exceptions work, define a new exception type by subclassing Exception. + +:: + + class MyException(Exception): + pass + + raise MyException("An exception doesn't always prove the rule!") + +.. nextslide:: + +It is possible, but discouraged to catch all exceptions. + +:: + + try: + my_cool_code() + except: + print('no idea what the exceptions is, but I caught it') + + +An exception to this exception rule is when you are running a service that should not ever crash, +like a web server. + +In this case, it is extremely important to have very good logging so that you +have reports of exactly what happened and what exception should have been thrown. + +.. nextslide:: + +.. rubric:: Further reading + :name: further-reading + +- http://docs.python.org/3/library/exceptions.html +- http://docs.python.org/3/tutorial/errors.html + +.. nextslide:: + +Debugging +--------- + +.. rubric:: Python Debugging + :name: python-debugging + +You will spend most of your time as a developer debugging. +You will spend more time than you expect on google. + + +.. nextslide:: + +The Stack Trace + +You already know what it looks like. Simple traceback: + +:: + + $ python3 test_trie.py + Traceback (most recent call last): + File "test_trie.py", line 3, in + from trie import Trie + File "/Users/maria/python/trie/trie.py", line 144 + print "end of word", node.value + ^ + SyntaxError: Missing parentheses in call to 'print' + +.. nextslide:: + +But things can quickly get complicated (Here is ~1/3 of a recent traceback I had): + +Traceback (most recent call last): + File "snapi3/tests/test_proxy_rest.py", line 21, in test_http_get + resp = self.app.get(self.TRIVIAL_URL, status=200) + File "python3/lib/python3.5/site-packages/webtest/app.py", line 323, in get + expect_errors=expect_errors) + File "python3/lib/python3.5/site-packages/webtest/app.py", line 606, in do_request + res = req.get_response(app, catch_exc_info=True) + File "python3/lib/python3.5/site-packages/webob/request.py", line 1313, in send + application, catch_exc_info=True) + File "python3/lib/python3.5/site-packages/webob/request.py", line 1284, in call_application + output.extend(app_iter) + +.. nextslide:: + +Debuggers are code which allows the inspection of state of other code +during runtime. + +Rudimentary tools + +- print() +- interpreter hints +- import logging.debug +- assert() + +.. nextslide:: + +Console debuggers + +- pdb/ipdb + +GUI debuggers + +- Winpdb +- IDEs: Eclipse, Wing IDE, PyCharm, Visual Studio + +.. nextslide:: + +.. rubric:: help from the interpreter + :name: help-from-the-interpreter + +1. investigate import issues with -v: + +:: + + python -v myscript.py + + +Verbose (trace import statements) + + +2. inspect environment after running script with -i + +:: + + python -i myscript.py + + +Forces interpreter to remain active, and still in scope + +.. nextslide:: + +.. rubric:: `Pdb - The Python + Debugger `__ + :name: pdb---the-python-debugger + +Pros: + +- You have it already, ships with the standard library +- Easy remote debugging +- Works with any development environment + +Cons: + +- Steep-ish learning curve +- Easy to get lost in a deep stack +- Watching variables isn't hard, but non-trivial + +.. nextslide:: + +.. rubric:: `Pdb - The Python + Debugger `__ + :name: pdb---the-python-debugger-1 + +The 4-fold ways of invoking pdb + +- Postmortem mode +- Run mode +- Script mode +- Trace mode + +Note: in most cases where you see the word 'pdb' in the examples, you +can replace it with 'ipdb'. ipdb is the ipython enhanced version of pdb +which is mostly compatible, and generally easier to work with. But it +doesn't ship with Python. + +.. nextslide:: + +.. rubric:: Postmortem mode + :name: postmortem-mode + +For analyzing crashes due to uncaught exceptions + +:: + + python -i script.py + import pdb; pdb.pm() + +.. nextslide:: + +.. rubric:: Run mode + :name: run-mode + +:: + + pdb.run('some.expression()') + +.. nextslide:: + +.. rubric:: Script mode + :name: script-mode + +:: + + python -m pdb script.py + + +"-m [module]" finds [module] in sys.path and executes it as a script + + +.. nextslide:: + +.. rubric:: Trace mode + :name: trace-mode + +Insert the following line into your code where you want execution to +halt: + +:: + + import pdb; pdb.set_trace() + + +It's not always OK/possible to modify your code in order to debug it, +but this is often the quickest way to begin inspecting state + +.. nextslide:: + +.. rubric:: pdb in ipython + :name: pdb-in-ipython + +:: + + + In [2]: pdb + Automatic pdb calling has been turned ON + + %run app.py + + # now halts execution on uncaught exception + +If you forget to turn on pdb, the magic command %debug will activate the +debugger (in 'post-mortem mode'). + +.. nextslide:: + +.. rubric:: Navigating pdb + :name: navigating-pdb + +The goal of each of the preceding techniques was to get to the pdb +prompt and get to work inspecting state. Most commands can be short-cutted +to the first letter. + +:: + + % python -m pdb define.py + pdb> args # print arguments and values to current function + pdb> pp a_variable # pretty-print a_variable + pdb> where # print stack trace, bottom is most recent command + pdb> list # list the code including and surrounding the current running code + + +.. nextslide:: + +To repeat the current command, press only the Enter key + +:: + + # execute until current function returns + pdb> return + # Execute the current line, stop at the first possible occasion + pdb> step + # Continue execution until the next line in the current function is reached or it returns. + pdb> next + # Continue execution until the line with a number greater than the current one is reached + or until the current frame returns. Good for exiting loops. + pdb> until + # move one level up the stack + pdb> up + # move one level down the stack + pdb> down + pdb> continue # goes until next breakpoint or end of program + # advanced: create commands to be executed on a breakpoint + pdb> commands + + +.. nextslide:: + +.. rubric:: Breakpoints + :name: breakpoints + +:: + + pdb> help break + b(reak) ([file:]lineno | function) [, condition] + With a line number argument, set a break there in the current + file. With a function name, set a break at first executable line + of that function. Without argument, list all breaks. If a second + argument is present, it is a string specifying an expression + which must evaluate to true before the breakpoint is honored. + + The line number may be prefixed with a filename and a colon, + to specify a breakpoint in another file (probably one that + hasn't been loaded yet). The file is searched for on sys.path; + the .py suffix may be omitted. + + +.. nextslide:: + +Can use up, down, where and list to evalutate where you are, and use that to +set a new breakpoint in code coming up. Useful for getting out of rabbit holes. + +:: + + pdb> break api.py:21 set a breakpoint file:line # + pdb> break # list breakpoints + pdb> clear 1 # get rid of first breakpoint + pdb> break 35 # set a breakpoint in current file at line 35 + # print lines in range + pdb> list 1,28 + + +.. nextslide:: + +Clear (delete) breakpoints + +:: + + clear [bpnumber [bpnumber...]] + + +disable breakpoints + +:: + + disable [bpnumber [bpnumber...]] + + +enable breakpoints + +:: + + enable [bpnumber [bpnumber...]] + + +.. nextslide:: + +.. rubric:: Conditional Breakpoints + :name: conditional-breakpoints + +:: + + pdb> help condition + condition bpnumber str_condition + str_condition is a string specifying an expression which + must evaluate to true before the breakpoint is honored. + If str_condition is absent, any existing condition is removed; + i.e., the breakpoint is made unconditional. + + +.. nextslide:: + +.. rubric:: Invoking pdb with nose + :name: invoking-pdb-with-nose + +On error condition, drop to pdb + +:: + + nosetests --pdb + + +On test failure, drop to pdb: + +:: + + nosetests --pdb-failures + + +.. nextslide:: + +.. rubric:: Python IDEs + :name: python-ides + +.. rubric:: PyCharm + :name: pycharm + +From JetBrains, and integrates some of their vast array of development +tools + +Free Community Edition (CE) is available + +Good visual debugging support + +.. nextslide:: + +.. rubric:: Eclipse + :name: eclipse + +A multi-language IDE + +Python support via http://pydev.org/ + +Automatic variable and expression watching + +Supports a lot of debugging features like conditional breakpoints, +provided you look in the right places! + +Further reading + +http://pydev.org/manual_adv_debugger.html + +.. nextslide:: + +.. rubric:: winpdb + :name: winpdb + +A multi platform Python debugger with threading support + +Easier to start up and get debugging + +:: + + + winpdb your_app.py + + +.. rubric:: Remote debugging with winpdb + :name: remote-debugging-with-winpdb + +.. nextslide:: + +To debug an application running a different Python, even remotely: + +:: + + + import rpdb2; rpdb2.start_embedded_debugger("password") + + + +http://winpdb.org/tutorial/WinpdbTutorial.html + +.. nextslide:: + +.. rubric:: Debugging exercise + :name: debugging-exercise + +Find the wikidef app in the examples folder + +See if you can find the bug and get the app working. Use whatever debugging +technique(s) you prefer. + +To run the app: +:: + + python define.py interesting_topic + +where interesting_topic is a topic of interest. ;-) + +.. nextslide:: + +Once it is working again: +Using (i)pdb in module mode (python -m pdb ) to find the server type that +wikipedia is using by looking at +response.headers.headers in Wikipedia.article + +You can enter the debugger by running + +:: + + python -m pdb ./define.py robot + +(define.py takes the first sys arg and finds articles on wikipedia on that topic) + +You can get to the code by walking through each line with 's'tep and +'n'ext commands, or by setting a breakpoint and 'c'ontinuing. + +What's the result? + +.. nextslide:: + +.. rubric:: Questions? + :name: questions + diff --git a/_sources/documentation.rst.txt b/_sources/documentation.rst.txt new file mode 100644 index 0000000..82bf196 --- /dev/null +++ b/_sources/documentation.rst.txt @@ -0,0 +1,594 @@ +.. _documentation: + +------------- +Documentation +------------- + +A (very) quick run down of how to document your python package. + +* Chris Barker + + +Why +=== + +.. rst-class:: left + + Documentation is a key part of software development. + + You'll be glad you have it, even if you are the only one that uses your code. + + If you are writing a package you want others to use -- documentation can make all the difference + + And there are some nice tools for documenting Python code. + + There is even a hosting service: + + - http://readthedocs.org + + +Sphinx +-------- + +Sphinx is a documentation system built specifically for documenting Python itself: + +http://sphinx-doc.org + +But it's also useful for any sort of structured documentation + +- and is sometimes used for non-code projects. + +It Produces: + +* HTML (multiple styles available) +* PDF (via LaTeX) +* ePub +* man pages +* plain text +* and others! + + +Extendability +------------- + +Sphinx has an extension architecture for adding special functionality: + + * Hieroglyph (It is used for these slides...) + + * Matplotlib added some nice stuff: + + - http://matplotlib.org + + * Math + + * Embedded ASCII art + + * Embedding Excel spreadsheets + + * Unlimited possibilities + + +Automatic Documentation +------------------------ + +One of the great features of Sphinx: + +It can extract docstrings from your code and build docs from them. + +Includes cross referencing of modules and classes, etc. + +This keeps your code and docs in sync, and encourages you to have nice docstrings. + +It's a bit tricky to get it all set up though :-( + + +Documentation for the Documentation System +------------------------------------------- + +Sphinx is, of course, documented with sphinx itself. + +Its tutorial is pretty good, but can be a little confusing (particularly the autodoc stuff) + +- http://sphinx-doc.org/tutorial.html + +So here are a couple other resources (and many more out there): + +Basic getting started tutorial: + +- https://pythonhosted.org/an_example_pypi_project/sphinx.html + +Tutorial focused on getting autodoc set up: + +- http://codeandchaos.wordpress.com/2012/07/30/sphinx-autodoc-tutorial-for-dummies/ + + +reStructuredText +----------------- + +reStructuredText is the markup language used for Sphinx. + +Developed (adapted, really) for Python documentation. + +It's a plain text, easy to read and write markup. + +Like many similar markup languages (Markdown, etc.) + +* designed to be easy to read and write +* makes sense in plain text +* looks a lot like what you might write in plain text anyway. + +So it's suitable for use both as plain text and for fancier formatting +(i.e. docstrings and autodoc) + +But more extensible than most others -- so good for sphinx + + +reStructuredText +----------------- + +:: + + **************************** + This is the top level header + **************************** + + And now some normal text + + And a level-2 header + ==================== + + more text: **this** is bold. + + And ``this`` is code. + + :: + + #And now a code block + for i in range(10): + do_something_interesting(i) + +.. nextslide:: + +You can actually use anything to underline for a header. + +rst will assume they are in order: + + - The first form it sees is Header 1 + - The second encountered is Header 2 + - The third is header 3 + - . . . . + +reStructuredText directives +--------------------------- + +Directives are an extension mechanism for RST. + +A directive starts iwth 2 dots, then a smapce, then the directive, then two colons. + +Directives are indicated by an explicit markup start (".. ") followed by the directive type, two colons, and whitespace. + +Directive types are case-insensitive single words. + +There are three logical parts to the directive block: + +- Directive arguments. +- Directive options. +- Directive content. + +.. nextslide:: + +For example :: + + .. toctree:: + :maxdepth: 2 + +``toctree`` is the directive type + +there are no arguments + +``:maxdepth: 2`` is an option + +The content follown, indented one level + +.. nextslide:: + +Another example:: + + code-block:: python + + for i in range(10): + print i + +``code-block`` is the directive type + +``python`` is an argument (in this case, telling sphinx to format the coce black in python style) + +The code itself is the content. + +.. nextslide:: + +One hint for rst: + +.. rst-class:: medium + + when in doubt, put in an extra empty line! + +Some docs to get started: + + - http://docutils.sourceforge.net/rst.html + + - http://docutils.sourceforge.net/docs/user/rst/quickstart.html + + +Sphinx Directives for docstrings +--------------------------------- + +If you put sphinx formatting in a docsting, then Sphinx Autodoc and format it nicely for you. + +:: + + def a_function(a, b, c='fred'): + """ + computes something which I would describe here. + + :param a: the first input value + :type a: int + + :param b: the second input value + :type b: float + + :param c='fred': a string flag + :type c: str + + :returns: a useless string + """ + return compute_something(a,b,c) + +An Alternative +-------------- + +The previous works OK, but it's really pretty klunky, hard to write and kinda hard to read. + +To much markup not really the "just like plain next" we're going for. + +Recent version os Sphinx include "napolean" to remedy this: + +https://sphinxcontrib-napoleon.readthedocs.org + +It allows the use of "google style" or "numpy style" docstrings. + +.. nextslide:: + +So the previous example would look like: + +.. code-block:: python + + def a_function(a, b, c='fred'): + """ + Computes something which I would describe here. + + Args: + a (int): the first input value + b (float) the second input value + c=fred (str): a string flag + + Returns: + a useless string + """ + + +LAB: +---- + +Set up a Sphinx project to document the package in:: + + Examples/Capitalize + +Put it in:: + + Examples/Capitalize/doc + +Set it up to autodoc + +Clean up the docstrings so that autodoc works well. + +(Or do it for your code!) + + +Tutorial Script: +----------------- + +The following as a script to follow for setting up and starting to document a pacakge with Sphinx and Autodoc. + +It uses the ``Capitalize`` package (included in this repo) as an example, but you can follow along with your own package if you like. + +First, you need the tool:: + + $ pip install sphinx + + +(Thanks to: http://codeandchaos.wordpress.com/2012/07/30/sphinx-autodoc-tutorial-for-dummies/) + +Setting Up sphinx: +------------------- + +You need to be in a good place to build your docs:: + + $ cd code/Capitalize/doc + +Sphinx comes with a nice utility for getting your documentation set up:: + + $ sphinx-quickstart + +It will ask you a number of questions on the command line: You can use the defaults for most of these. + +You are already in a doc dir, so you can use ``.`` (the default) for the root path:: + + > Root path for the documentation [.]: + +QuickStart (cont): +------------------- + +I like to keep the source and the built docs separate:: + + > Separate source and build directories (y/N) [n]: y + +Give it a name and an author:: + + > Project name: Capitalize + > Author name(s): Chris Barker + +Use ``.rst`` for restructured text:: + + > Source file suffix [.rst]: + +QuickStart (cont): +------------------- + +You absolutely want autodoc!:: + + > autodoc: automatically insert docstrings from modules (y/N) [n]: y + +This is kind of nice, to help you keep in line:: + + > coverage: checks for documentation coverage (y/N) [n]: y + +A Makefile (and/or DOS batch file) is really handy:: + + > Create Makefile? (Y/n) [y]: y + > Create Windows command file? (Y/n) [y]: y + +Project Structure: +------------------- + +``sphinx-quickstart`` will have created the project structure for you:: + + $ ls + Makefile README.txt build make.bat source + + $ ls source + _static _templates conf.py index.rst + +``index.rst`` is the start of your documentation + +``conf.py`` is the configuration that was created by ``sphinx-quickstart`` -- you can edit it if you change you mind about anything. + + +Building the docs: +------------------- + +The ``Makefile`` will build the docs for you in various ways:: + + $ make html + sphinx-build -b html -d build/doctrees source build/html + Making output directory... + Running Sphinx v1.1.3 + .... + Build finished. The HTML pages are in build/html. + +Or:: + + $ make latexpdf + +(if you have LaTeX installed...) + +Take a look at ``build/html/index.html`` + + +Getting Started with Writing: +------------------------------ + +The ``index.rst`` file will look like this:: + + Welcome to Capitalize's documentation! + ====================================== + + Contents: + + .. toctree:: + :maxdepth: 2 + + Indices and tables + ================== + + * :ref:`genindex` + * :ref:`modindex` + * :ref:`search` + + + +A tiny bit of RST +----------------- + +Underlining creates headings:: + + Welcome to Capitalize's documentation! + ====================================== + +This will give you a lower level heading:: + + Welcome to a Subsection + ------------------------ + +(each new underlining character you introduce goes another level down.) + +A tiny bit of RST +------------------ + +The ``..`` is either a comment or a "directive":: + + .. toctree:: + :maxdepth: 2 + +if sphinx understand the directive ``toctree``, then it is used. Otherwise, it is treated as a comment. + +``toctree`` builds a table of contents tree. + +AutoDoc +-------- + +AutoDoc extracts the docstrings from your code. + +In order to find them -- sphinx needs to be able to import the code. + +Another reason to build a package and use ``develop`` mode! + +Alternatively, you can add the path to your code by adding this to the conf.py file:: + + os.path.abspath('mydir/myfile.txt') + +(Path is relative to the conf.py file) + +But I'm not going to do that, 'cause I use ``develop`` mode + +Adding Autodoc to your docs. +---------------------------- + +I like to put the auto-generated docs in a "reference" page. + +Create a new file names ``reference.rst`` + +Give it a header:: + + Capitalize Reference + ==================== + +save it and add a reference to it in your ``index.rst`` file:: + + Contents: + + .. toctree:: + :maxdepth: 2 + + reference.rst + +now remake the docs, ans look at the index:: + + make html + +Setting up Autodoc +------------------ + +Add the automodule directive to your ``reference.rst`` file:: + + Capitalize Reference + ==================== + + This is the reference docs. + + The capitalize package + ---------------------- + + .. automodule:: capitalize + +Then rebuild again:: + + $ make html + +And reload ``index.rst`` + +Finding the members. +--------------------- + +Not much there, is there? Where is the module? Where are the functions? + +Sphinx only creates the main doc for each pacakge or module. + +You need to create a entry for each module yourself:: + + capital_mod + ............ + + .. automodule:: capitalize.capital_mod + :members: + +The ``:members:`` directive tells Sphinx you want all the members documented as well. + +Documenting the members. +------------------------ + +You can specify only particular ones if you want:: + + .. automodule:: capitalize.capital_mod + :members: capitalize + +For classes, there is ``autoclass``:: + + .. autoclass:: a_package.a_class + :members: + +You may want to set ``autoclass_content`` configuration to one of: "class", +"init", or "both" + +(http://sphinx-doc.org/ext/autodoc.html) + + +Multiple Files +--------------- + +For most projects, you'll want multiple pages in your docs. You can put each in their own `*.rst` file, and reference them in the ``toctree`` section:: + + .. toctree:: + :maxdepth: 2 + + installation.rst + tutorial.rst + api.rst + +Then you need to create and populate those files - make sure they have a header! + +I put the autocdoc stuff in the api.rst file... + +APIdoc +------- + +For a substantial package, hand writing all those files and autodoc directives can get pretty tedious. + +So you can use APIdoc:: + + pip install apidoc + + sphinx-apidoc [options] -o [pathnames ...] + + $ sphinx-apidoc -o test ../capitalize + Creating file test/capitalize.rst. + Creating file test/capitalize.test.rst. + Creating file test/modules.rst. + +This is actually pretty slick.... + + +Sphinx Appearance +------------------- + +If you don't like the default looks, there are a number of other options, or you can build your own: + +http://sphinx-doc.org/theming.html + +In ``conf.py``:: + + html_theme = "default" + +Of course, this is the primary source of how to use Sphinx itself: + +http://sphinx-doc.org/ diff --git a/_sources/documentation.txt b/_sources/documentation.txt new file mode 100644 index 0000000..82bf196 --- /dev/null +++ b/_sources/documentation.txt @@ -0,0 +1,594 @@ +.. _documentation: + +------------- +Documentation +------------- + +A (very) quick run down of how to document your python package. + +* Chris Barker + + +Why +=== + +.. rst-class:: left + + Documentation is a key part of software development. + + You'll be glad you have it, even if you are the only one that uses your code. + + If you are writing a package you want others to use -- documentation can make all the difference + + And there are some nice tools for documenting Python code. + + There is even a hosting service: + + - http://readthedocs.org + + +Sphinx +-------- + +Sphinx is a documentation system built specifically for documenting Python itself: + +http://sphinx-doc.org + +But it's also useful for any sort of structured documentation + +- and is sometimes used for non-code projects. + +It Produces: + +* HTML (multiple styles available) +* PDF (via LaTeX) +* ePub +* man pages +* plain text +* and others! + + +Extendability +------------- + +Sphinx has an extension architecture for adding special functionality: + + * Hieroglyph (It is used for these slides...) + + * Matplotlib added some nice stuff: + + - http://matplotlib.org + + * Math + + * Embedded ASCII art + + * Embedding Excel spreadsheets + + * Unlimited possibilities + + +Automatic Documentation +------------------------ + +One of the great features of Sphinx: + +It can extract docstrings from your code and build docs from them. + +Includes cross referencing of modules and classes, etc. + +This keeps your code and docs in sync, and encourages you to have nice docstrings. + +It's a bit tricky to get it all set up though :-( + + +Documentation for the Documentation System +------------------------------------------- + +Sphinx is, of course, documented with sphinx itself. + +Its tutorial is pretty good, but can be a little confusing (particularly the autodoc stuff) + +- http://sphinx-doc.org/tutorial.html + +So here are a couple other resources (and many more out there): + +Basic getting started tutorial: + +- https://pythonhosted.org/an_example_pypi_project/sphinx.html + +Tutorial focused on getting autodoc set up: + +- http://codeandchaos.wordpress.com/2012/07/30/sphinx-autodoc-tutorial-for-dummies/ + + +reStructuredText +----------------- + +reStructuredText is the markup language used for Sphinx. + +Developed (adapted, really) for Python documentation. + +It's a plain text, easy to read and write markup. + +Like many similar markup languages (Markdown, etc.) + +* designed to be easy to read and write +* makes sense in plain text +* looks a lot like what you might write in plain text anyway. + +So it's suitable for use both as plain text and for fancier formatting +(i.e. docstrings and autodoc) + +But more extensible than most others -- so good for sphinx + + +reStructuredText +----------------- + +:: + + **************************** + This is the top level header + **************************** + + And now some normal text + + And a level-2 header + ==================== + + more text: **this** is bold. + + And ``this`` is code. + + :: + + #And now a code block + for i in range(10): + do_something_interesting(i) + +.. nextslide:: + +You can actually use anything to underline for a header. + +rst will assume they are in order: + + - The first form it sees is Header 1 + - The second encountered is Header 2 + - The third is header 3 + - . . . . + +reStructuredText directives +--------------------------- + +Directives are an extension mechanism for RST. + +A directive starts iwth 2 dots, then a smapce, then the directive, then two colons. + +Directives are indicated by an explicit markup start (".. ") followed by the directive type, two colons, and whitespace. + +Directive types are case-insensitive single words. + +There are three logical parts to the directive block: + +- Directive arguments. +- Directive options. +- Directive content. + +.. nextslide:: + +For example :: + + .. toctree:: + :maxdepth: 2 + +``toctree`` is the directive type + +there are no arguments + +``:maxdepth: 2`` is an option + +The content follown, indented one level + +.. nextslide:: + +Another example:: + + code-block:: python + + for i in range(10): + print i + +``code-block`` is the directive type + +``python`` is an argument (in this case, telling sphinx to format the coce black in python style) + +The code itself is the content. + +.. nextslide:: + +One hint for rst: + +.. rst-class:: medium + + when in doubt, put in an extra empty line! + +Some docs to get started: + + - http://docutils.sourceforge.net/rst.html + + - http://docutils.sourceforge.net/docs/user/rst/quickstart.html + + +Sphinx Directives for docstrings +--------------------------------- + +If you put sphinx formatting in a docsting, then Sphinx Autodoc and format it nicely for you. + +:: + + def a_function(a, b, c='fred'): + """ + computes something which I would describe here. + + :param a: the first input value + :type a: int + + :param b: the second input value + :type b: float + + :param c='fred': a string flag + :type c: str + + :returns: a useless string + """ + return compute_something(a,b,c) + +An Alternative +-------------- + +The previous works OK, but it's really pretty klunky, hard to write and kinda hard to read. + +To much markup not really the "just like plain next" we're going for. + +Recent version os Sphinx include "napolean" to remedy this: + +https://sphinxcontrib-napoleon.readthedocs.org + +It allows the use of "google style" or "numpy style" docstrings. + +.. nextslide:: + +So the previous example would look like: + +.. code-block:: python + + def a_function(a, b, c='fred'): + """ + Computes something which I would describe here. + + Args: + a (int): the first input value + b (float) the second input value + c=fred (str): a string flag + + Returns: + a useless string + """ + + +LAB: +---- + +Set up a Sphinx project to document the package in:: + + Examples/Capitalize + +Put it in:: + + Examples/Capitalize/doc + +Set it up to autodoc + +Clean up the docstrings so that autodoc works well. + +(Or do it for your code!) + + +Tutorial Script: +----------------- + +The following as a script to follow for setting up and starting to document a pacakge with Sphinx and Autodoc. + +It uses the ``Capitalize`` package (included in this repo) as an example, but you can follow along with your own package if you like. + +First, you need the tool:: + + $ pip install sphinx + + +(Thanks to: http://codeandchaos.wordpress.com/2012/07/30/sphinx-autodoc-tutorial-for-dummies/) + +Setting Up sphinx: +------------------- + +You need to be in a good place to build your docs:: + + $ cd code/Capitalize/doc + +Sphinx comes with a nice utility for getting your documentation set up:: + + $ sphinx-quickstart + +It will ask you a number of questions on the command line: You can use the defaults for most of these. + +You are already in a doc dir, so you can use ``.`` (the default) for the root path:: + + > Root path for the documentation [.]: + +QuickStart (cont): +------------------- + +I like to keep the source and the built docs separate:: + + > Separate source and build directories (y/N) [n]: y + +Give it a name and an author:: + + > Project name: Capitalize + > Author name(s): Chris Barker + +Use ``.rst`` for restructured text:: + + > Source file suffix [.rst]: + +QuickStart (cont): +------------------- + +You absolutely want autodoc!:: + + > autodoc: automatically insert docstrings from modules (y/N) [n]: y + +This is kind of nice, to help you keep in line:: + + > coverage: checks for documentation coverage (y/N) [n]: y + +A Makefile (and/or DOS batch file) is really handy:: + + > Create Makefile? (Y/n) [y]: y + > Create Windows command file? (Y/n) [y]: y + +Project Structure: +------------------- + +``sphinx-quickstart`` will have created the project structure for you:: + + $ ls + Makefile README.txt build make.bat source + + $ ls source + _static _templates conf.py index.rst + +``index.rst`` is the start of your documentation + +``conf.py`` is the configuration that was created by ``sphinx-quickstart`` -- you can edit it if you change you mind about anything. + + +Building the docs: +------------------- + +The ``Makefile`` will build the docs for you in various ways:: + + $ make html + sphinx-build -b html -d build/doctrees source build/html + Making output directory... + Running Sphinx v1.1.3 + .... + Build finished. The HTML pages are in build/html. + +Or:: + + $ make latexpdf + +(if you have LaTeX installed...) + +Take a look at ``build/html/index.html`` + + +Getting Started with Writing: +------------------------------ + +The ``index.rst`` file will look like this:: + + Welcome to Capitalize's documentation! + ====================================== + + Contents: + + .. toctree:: + :maxdepth: 2 + + Indices and tables + ================== + + * :ref:`genindex` + * :ref:`modindex` + * :ref:`search` + + + +A tiny bit of RST +----------------- + +Underlining creates headings:: + + Welcome to Capitalize's documentation! + ====================================== + +This will give you a lower level heading:: + + Welcome to a Subsection + ------------------------ + +(each new underlining character you introduce goes another level down.) + +A tiny bit of RST +------------------ + +The ``..`` is either a comment or a "directive":: + + .. toctree:: + :maxdepth: 2 + +if sphinx understand the directive ``toctree``, then it is used. Otherwise, it is treated as a comment. + +``toctree`` builds a table of contents tree. + +AutoDoc +-------- + +AutoDoc extracts the docstrings from your code. + +In order to find them -- sphinx needs to be able to import the code. + +Another reason to build a package and use ``develop`` mode! + +Alternatively, you can add the path to your code by adding this to the conf.py file:: + + os.path.abspath('mydir/myfile.txt') + +(Path is relative to the conf.py file) + +But I'm not going to do that, 'cause I use ``develop`` mode + +Adding Autodoc to your docs. +---------------------------- + +I like to put the auto-generated docs in a "reference" page. + +Create a new file names ``reference.rst`` + +Give it a header:: + + Capitalize Reference + ==================== + +save it and add a reference to it in your ``index.rst`` file:: + + Contents: + + .. toctree:: + :maxdepth: 2 + + reference.rst + +now remake the docs, ans look at the index:: + + make html + +Setting up Autodoc +------------------ + +Add the automodule directive to your ``reference.rst`` file:: + + Capitalize Reference + ==================== + + This is the reference docs. + + The capitalize package + ---------------------- + + .. automodule:: capitalize + +Then rebuild again:: + + $ make html + +And reload ``index.rst`` + +Finding the members. +--------------------- + +Not much there, is there? Where is the module? Where are the functions? + +Sphinx only creates the main doc for each pacakge or module. + +You need to create a entry for each module yourself:: + + capital_mod + ............ + + .. automodule:: capitalize.capital_mod + :members: + +The ``:members:`` directive tells Sphinx you want all the members documented as well. + +Documenting the members. +------------------------ + +You can specify only particular ones if you want:: + + .. automodule:: capitalize.capital_mod + :members: capitalize + +For classes, there is ``autoclass``:: + + .. autoclass:: a_package.a_class + :members: + +You may want to set ``autoclass_content`` configuration to one of: "class", +"init", or "both" + +(http://sphinx-doc.org/ext/autodoc.html) + + +Multiple Files +--------------- + +For most projects, you'll want multiple pages in your docs. You can put each in their own `*.rst` file, and reference them in the ``toctree`` section:: + + .. toctree:: + :maxdepth: 2 + + installation.rst + tutorial.rst + api.rst + +Then you need to create and populate those files - make sure they have a header! + +I put the autocdoc stuff in the api.rst file... + +APIdoc +------- + +For a substantial package, hand writing all those files and autodoc directives can get pretty tedious. + +So you can use APIdoc:: + + pip install apidoc + + sphinx-apidoc [options] -o [pathnames ...] + + $ sphinx-apidoc -o test ../capitalize + Creating file test/capitalize.rst. + Creating file test/capitalize.test.rst. + Creating file test/modules.rst. + +This is actually pretty slick.... + + +Sphinx Appearance +------------------- + +If you don't like the default looks, there are a number of other options, or you can build your own: + +http://sphinx-doc.org/theming.html + +In ``conf.py``:: + + html_theme = "default" + +Of course, this is the primary source of how to use Sphinx itself: + +http://sphinx-doc.org/ diff --git a/_sources/extensions.rst.txt b/_sources/extensions.rst.txt new file mode 100644 index 0000000..53df1db --- /dev/null +++ b/_sources/extensions.rst.txt @@ -0,0 +1,1007 @@ +.. _extensions: + +=================================== +Extending Python with Complied Code +=================================== + + +- Chris Barker + +.. Contents: + +.. .. toctree:: +.. :maxdepth: 2 + +Topics +======= + +.. rst-class:: left + + * Motivation + + * The C API + + * ctypes + + * Cython + + * Auto-generating wrappers + + * Others to consider + + +Motivation +----------- + +Motivations for exiting pure Python + + - Performance + - Integration with existing C libraries + - Working as a glue language + - Implement new builtin types + +What is an extension module? + + - written in C (C API) + - compiled code + - lets you work directly with the CPython engine + +Further reading: + +http://docs.python.org/2/extending/extending.html + +Example Case +------------- + +To focus on the integration techniques, rather than complex C code, +we'll work with the following function we want to integrate:: + + #include + + int add(int x, int y) { + return x+y; + } + int main(void) { + int w = 3; + int q = 2; + printf("%d + %d = %d\n\n", w, q, add(w,q)); + } + +This is, of course, trivial and built in to Python, but the techniques +are the same. + +(``Examples/week-08-extensions/pure-c/add.c``) + + +.. nextslide:: Building + +Build it with the Makefile (Linux and OS-X):: + + all: add; gcc -o add add.c + +:: + + $ make + gcc -o add add.c + +and run it:: + + $ ./add + 3 + 2 = 5 + +So are simple function works -- but how to call it from Python? + +The C API +--------- + +Write your function in pure C using the Python API and import it into Python + +Good for integrating with C library functions and system calls + +The API isn't trivial to learn + +Lots of opportunity for error -- you must do manual reference counting: + +(http://docs.python.org/2/c-api/refcounting.html) + +Further reading: + + - http://docs.python.org/2/extending/extending.html + + - Python 2.7 source code + + +Intro to the C API +------------------- + +You'll need the Python dev package installed on your system + +Pull in the Python API to your C code via:: + + #include + /* + Note: Since Python may define some pre-processor definitions which + affect the standard headers on some systems, you must include + Python.h before any standard headers are included. + + stdio.h, string.h, errno.h, and stdlib.h are included for you. + */ + +Passing Data in and out of your function +----------------------------------------- + +Function arguments must be parsed on the way in and the way out + +On the way in, we can call ``PyArg_ParseTuple``:: + + if (!PyArg_ParseTuple(args, "s", &var1, ...)) + return NULL; + +http://docs.python.org/2/c-api/arg.html#PyArg_ParseTuple + +| + +On the way out, we can call ``Py_BuildValue``:: + + PyObject* Py_BuildValue(const char *format, ...) + +http://docs.python.org/2/c-api/arg.html#Py_BuildValue + +Registering your functions +--------------------------- + +First, register the name and address of your function in the method table:: + + // Module's method table and initialization function + static PyMethodDef AddMethods[] = { + {"add", add, METH_VARARGS, "add two numbers"}, + {NULL, NULL, 0, NULL} // sentinel + }; + +https://docs.python.org/2/extending/extending.html#the-module-s-method-table-and-initialization-function + + +Initializing the module +----------------------- + +Define an initialization function:: + + PyMODINIT_FUNC // does the right thing on Windows, Linux, etc. + initadd(void) { + // Module's initialization function + // Will be called again if you use Python's reload() + (void) Py_InitModule("add", AddMethods); + } + +It *must* be called ``initthe_module_name`` + +https://docs.python.org/2/extending/extending.html#the-module-s-method-table-and-initialization-function + +The whole thing: +----------------- + +:: + + #include + + static PyObject * + add(PyObject *self, PyObject *args) + { + int x, y, sts; + + if (!PyArg_ParseTuple(args, "ii", &x, &y)) + return NULL; + sts = x+y; + return Py_BuildValue("i", sts); + } + static PyMethodDef AddMethods[] = { + {"add", add, METH_VARARGS, "add two numbers"}, + {NULL, NULL, 0, NULL} // sentinel + }; + PyMODINIT_FUNC initadd(void) { + (void) Py_InitModule("add", AddMethods); + } + +Building your extension +------------------------ + +``setuptools`` provides features for automatically building extensions:: + + from setuptools import setup, Extension + setup( + name='Cadd', + version='1.0', + description='simple c extension for an example', + ext_modules=[Extension('add', sources=['add.c'])], + ) + +(``distutils`` does too -- but setuptools is getting updated to better +support new stuff) + +Run the setup.py:: + + python setup.py build_ext --inplace + +(you can also just do ``install`` or ``develop`` if you want it properly +installed) + + +Run the tests +-------------- + +``test_add.py``:: + + import pytest + + import add + + def test_basic(): + assert add.add(3,4) == 7 + + def test_negative(): + assert add.add(-12, 5) == -7 + + def test_float(): + with pytest.raises(TypeError): + add.add(3, 4.0) + +``$ py.test`` + + + +Subtleties we avoided: +====================== + +There are a LOT of things you need to get right with a hand-written +C Extension. + + +Exception handling +------------------- + +Works somewhat like the Unix errno variable: + +* Global indicator (per thread) of the last error that occurred. +* Most functions don’t clear this on success, but will set it to indicate the cause of the error on failure. +* Most functions also return an error indicator: + + - NULL if they are supposed to return a pointer, + - -1 if they return an integer + - The PyArg_*() functions return 1 for success and 0 for failure (and they set the Exception for you) + +The easy way to set this indicator is with PyErr_SetString + +http://docs.python.org/2/c-api/exceptions.html + +(you can completely control the Exception handling if you need to) + + +ReferenceCounting +------------------ + +Whenever you create or no longer need a Py_Object, you need to increment or decrement the reference count: + +``Py_INCREF(x)`` and ``Py_DECREF(x)`` + +``PyArg_ParseTuple`` and ``Py_BuildValue`` + +Handle this for you. + +But if you're creating new objects inside your function, you need to keep track. + +And what it the function raises an exception in the middle and can't finish? + +This gets really ugly and error-prone (and hard to debug!) + +LAB +---- + +LAB 1: + +* Add another function to the add.c file that multiplies two numbers instead. +* Write some test code and make sure it works. + +LAB 2: + +* Find the divide module in the examples/c-api directory +* What happens when you call divide.divide(1/0)? +* This is a different result than a pure Python 1/0, which throws an exception + +Advanced: + +* Change the divide method to throw an appropriate exception in the + divide-by-zero case + +ctypes +====== + +Isn't there an easier way to just call some C code? + + +What is ctypes? +--------------- + +A foreign function interface in Python + +Binds functions in shared libraries to Python functions + +Benefits: + - Ships with Python, since 2.5 + - No new language to learn, it's all Python + +Drawbacks: + - Performance hit for on the fly type translation + - "thicker" interface in python + +Example:: + + from ctypes import * + add = cdll.LoadLibrary("add.so") + print add.add(3,4) + +Further reading: + +http://docs.python.org/2/library/ctypes.html + + +Calling functions with ctypes +------------------------------ + +The shared lib must be loaded:: + + add = ctypes.cdll.LoadLibrary("add.so") + +An already loaded lib can be found with:: + + libc = ctypes.CDLL("/usr/lib/libc.dylib") + +ctypes comes with a utility to help find libs:: + + ctypes.util.find_library(name) + +(good for system libs) + +.. nextslide:: + +Once loaded, a ctypes wrapper around a c function can be called directly:: + + print add.add(3,4) + +But.... + + +C is statically typed -- once compiled, the function must be called with +the correct types. + +ctypes Data Types +----------------- + +ctypes will auto-translate these native types: + + - ``None`` + - int + - byte strings (``bytes()``, ``str()``) + - ``unicode`` (careful! unicode is ugly in C!) + +These can be directly used as parameters when calling C functions. + +.. nextslide:: + +Most types must be wrapped in a ctypes data type:: + + printf("An int %d, a double %f\n", 1234, c_double(3.14)) + +There are ctypes wrappers for all the "standard" C types + +http://docs.python.org/2/library/ctypes.html#fundamental-data-types + + +You can also do pointers to types:: + + a_lib.a_function( ctypes.byref(c_float(x))) + +http://docs.python.org/2/library/ctypes.html#passing-pointers-or-passing-parameters-by-reference + +.. nextslide:: C structs + +You can define C structs:: + + >>> class POINT(ctypes.Structure): + ... _fields_ = [("x", ctypes.c_int), + ... ("y", ctypes.c_int)] + ... + >>> point = POINT(10, 20) + >>> print point.x, point.y + 10 20 + >>> point = POINT(y=5) + >>> print point.x, point.y + 0 5 + +.. nextslide:: Custom Python Classes + +You can define how to pass data from your custom classes to ctypes: + +Define an ``_as_parameter_`` attribute (or property):: + + class MyObject(object): + def __init__(self, number): + self._as_parameter_ = number + + obj = MyObject(32) + printf("object value: %d\n", obj) + +https://docs.python.org/2/library/ctypes.html#calling-functions-with-your-own-custom-data-types + +(careful with types here!) + +.. nextslide:: Return Types + +To define the return type, define the ``restype`` attribute. + +Pre-defining the entire function signature:: + + libm.pow.restype = ctypes.c_double + libm.pow.argtypes = [ctypes.c_double, ctypes.c_double] + +And you can just call it like a regular python function -- ctypes will type check/convert at run time:: + + In [10]: libm.pow('a string', 4) + --------------------------------------------------------------------------- + ArgumentError Traceback (most recent call last) + in () + ----> 1 libm.pow('a string', 4) + + ArgumentError: argument 1: : wrong type + +Some more features +------------------- + +Defining callbacks into Python code from C:: + + ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) + +http://docs.python.org/2/library/ctypes.html#ctypes.CFUNCTYPE + +| + +Numpy provides utilities for numpy arrays: + +http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ctypes.html + +(works well for C code that takes "classic" C arrays) + + +Summary: +-------- + +``ctypes`` allows you to call shared libraries: + - Your own custom libs + - System libs + - Proprietary libs + +Supports almost all of C: + - Custom data types + + - structs + - unions + - pointers + + - callbacks + +.. nextslide:: + +* Upside: + + - You can call system libs with little code + - You don't need to compile anything + + - at least for system and pre-compiled libs + +* Downsides: + + - You need to specify the interface + + - and it is NOT checked for you! + + - Translation is done on the fly at run time + + - performance considerations + +LAB +---- + +In ``Examples/week-08-extensions/ctypes`` you'll find ``add.c`` + +You can build a shared lib with it with ``make`` +(``make.bat``) on Windows. + +``test_ctypes.py`` will call that dll, and a few system dlls. + +* Take a look at what's there, and how it works. + +* add another function to add.c, that takes different types (maybe divide?) + +* rebuild, and figure out how to call it with ctypes. + +* Try calling other system functions with ctypes. + + +Cython +====== + +A Python like language with static types which compiles down to C code +for Python extensions. + + +Cython +------- + +* Can write pure python + + - Fully understands the python types + +* With careful typing -- you get pure C (and pure C speed) + +* Can also call other C code: libraries or compiled in. + +* Used for custom Python extensions and/or call C and C++ code. + +.. nextslide:: + +Further reading: + +**Web site:** + +http://www.cython.org/ + +**Documentation:** + +http://docs.cython.org/ + +**Wiki:** + +https://github.com/cython/cython/wiki + + + +Developing with Cython +---------------------- + +First, install cython with:: + + pip install cython + +Cython files end in the .pyx extension. An example add.pyx:: + + def add(x, y): + cdef int result=0 + result = x + y + return result + +(looks a lot like Python, eh?) + +.. nextslide:: + +To build a cython module: write a setup.py that defines the extension:: + + from setuptools import setup + from Cython.Build import cythonize + + setup(name = "cython_example", + ext_modules = cythonize(['cy_add1.pyx',]) + ) + +``cythonize`` is a utility that sets up extension module builds for you in a cython-aware way. + +Building a module +------------------ + +For testing, it's helpful to do:: + + python setup.py build_ext --inplace + +which builds the extensions, and puts the resulting modules right in with the code. + +If you have your setup.py set up for a proper package, you can do:: + + python setup.py develop + or + python setup.py install + +Just like for pure-python packages. + +.. nextslide:: + +You can also do only the Cython step by hand at the command line:: + + cython a_file.pyx + +Produces: ``a_file.c`` file that you can examine, or compile. + +For easier reading, you can generate an annotated html version:: + + cython -a a_file.pyx + +Generates``a_file.html`` html file that is easier to read and gives +additional information that is helpful for debugging and performance +tuning. + +More on this later. + + +Basic Cython +------------- + +Cython functions can be declared three ways:: + + def foo # callable from Python + + cdef foo # only callable from Cython/C + + cpdef foo # callable from both Cython and Python + +Inside those functions, you can write virtually any python code. + +But the real magic is with the optional type declarations: the ``cdef`` lines. Well see this as we go... + + +Calling a C function from Cython +-------------------------------- + +You need to tell Cython about extenal functions you want to call with ``cdef extern``. + +The Cython code:: + + # distutils: sources = add.c + # This tells cythonize that you need that c file. + + # telling cython what the function we want to call looks like. + cdef extern from "add.h": + # pull in C add function, renaming to c_add for Cython + int c_add "add" (int x, int y) + + def add(x, y): + # now that cython knows about it -- we can just call it. + return c_add(x, y) + +.. nextslide:: + +and the setup.py:: + + from setuptools import setup + from Cython.Build import cythonize + + setup(name = "cython_example", + ext_modules = cythonize(['cy_add_c.pyx'] ) + ) + + +.. nextslide:: + +To build it:: + + $ python setup.py build_ext --inplace + +and test it:: + + Chris$ python test_cy_add_c.py + + if you didn't get an assertion, it worked + + +A pure Cython solution +---------------------- + +Here it is as python code:: + + def add(x, y): + result = x + y + return result + +Which we can put in a pyx file and compile with the setup.py:: + + #!/usr/bin/env python + + from setuptools import setup + from Cython.Build import cythonize + + setup(name = "cython_example", + ext_modules = cythonize(['cy_add1.pyx', + ]) + ) + +.. nextslide:: + +and build:: + + python setup.py build_ext --inplace + +and test:: + + Chris$ python test_cy_add1.py + + if you didn't get an assertion, it worked + +.. nextslide:: + +But this is still essentially Python. So let's type define it:: + + def add(int x, int y): + + cdef int result=0 + result = x + y + + return result + +now Cython knows that ``x, y``, and ``result`` are ``ints``, and can use +raw C for that. + +Build and test again:: + + Chris$ python setup.py build_ext --inplace + + Chris$ python test_cy_add2.py + +If you didn't get an assertion, it worked + + +A real Example: the Cython process +----------------------------------- + +Consider a more expensive function:: + + def f(x): + return x**2-x + + def integrate_f(a, b, N): + s = 0 + dx = (b-a)/N + for i in range(N): + s += f(a+i*dx) + return s * dx + +This is a good candidate for Cython -- an essentially static function called a lot. + +Cython from pure Python to C +----------------------------- + +Let's go through the steps one by one. In the ``Examples/week-08-extensions/cython/integrate`` directory:: + + + cy_integrate1.pyx + cy_integrate2.pyx + cy_integrate3.pyx + cy_integrate4.pyx + cy_integrate5.pyx + cy_integrate6.pyx + cy_integrate7.pyx + +At each step, we'll time and look at the output from:: + + $cython -a cy_integrate1.pyx + +AGC Example +----------- + +Another useful example of doing something useful, and using a numpy +array is in: + +Examples/week-08-extensions/AGC_example + +This one impliments an Automatic Gain Control Signal processing filter. + +It turns out that you can use some advanced numpy tricks to get pretty +good performancew with this filter, but you can't get full-on speed +without some compiled code. + + +This example uses all of: + * Pure Cython + * C called from Cython + * f2py and Fortran + + +Auto-generated wrappers +======================= + +There are few ways to auto-generate wrapper for C/C++ code: + +SWIG + +SIP + +XDress + +[also Boost-Python -- not really a wrapper generator] + +f2py -- for Fortran + + +SWIG +----- + +**Simple Wrapper Interface Generator** + +A language agnostic tool for integrating C/C++ code with high level languages + +**Advantages:** + + * Code generation for other environments than Python. + + * Doesn't require modification to your C source. + +**Disadvantages:** + + * For anything non-trivial, requires substantial effort to develop the interface. + + * Awkward when you want to mix python and C in the interface. + + * Inefficient passing of "Swigified Pointers" + + +.. nextslide:: + +Language interfaces: + + * Python + + * Tcl + + * Perl + + * Guile (Scheme/Lisp) + + * Java + + * Ruby + +And a bunch of others: + +http://www.swig.org/compat.html#SupportedLanguages + +Further reading: + +http://www.swig.org/Doc1.3/Python.html + +SWIGifying add() +---------------- + +SWIG doesn't require modification to your C source code + +The language interface is defined by an "interface file", usually with +a suffix of ``.i`` + +From there, SWIG can generate interfaces for the languages it supports + +The interface file contains ANSI C prototypes and variable declarations + +The ``%module`` directive defines the name of the module that will be +created by SWIG + +Creating a wrapper: +------------------- + +(``Examples/week-08-extensions/swig``) + +Create ``add.i``:: + + %module add + %{ + %} + extern int add(int x, int y); + +Create ``setup.py``:: + + from setuptools import setup, Extension + + setup( + name='add', + py_modules=['add'], + ext_modules=[ + Extension('_add', sources=['add.c', 'add.i']) + ] + ) + +.. nextslide:: + +And build it:: + + python setup.py build_ext --inplace + +NOTE: distutils (and thus setuptools) "knows" about SWIG, so it does the +swig step for you when you give it a \*.i file. + +Notice what gets created: + + * an ``add_wrap.c`` file -- the wrapper code. + * an ``add.py`` file -- python code that calls the C function + * an ``_add.so`` (or ``_add.pyd``) file -- the compiled extension + +.. nextslide:: + + +You can then run the code:: + + python -c 'import add; print add.add(4,5)' + +http://www.swig.org/Doc2.0/SWIGDocumentation.html#Introduction_nn5 + +Installing SWIG +---------------- + +On the SWIG download page, there is a source tarball for \*nix, and +Windows binaries: + +http://www.swig.org/download.html + +For Linux: + +You may have it in your package repository: + +apt-get install swig + +If not, download the tarball, unpack it, and:: + + ./configure + make + sudo make install + +should do it. + +.. nextslide:: OS-X Install + +For OS-X: the same thing, except you also need the "pcre" package. +Which you can get from: + +http://sourceforge.net/projects/pcre/files/pcre/8.37/pcre-8.37.tar.gz/download + +Put it in the dir created when the SWIG source was unpacked. + +Unpack it, then run this to set it up for use with SWIG:: + + Tools/pcre-build.sh + +Then you can do the standard:: + + ./configure + make + make install + + + + +Decisions, Decisions... +======================= + +.. rst-class:: large + + So what to use??? + + +My decision tree +----------------- + +Are you calling a few system library calls? + + * Use ctypes + +Do you have a really big library to wrap? -- use a wrapper generator: + + * SWIG (other languages?) + + * SIP + + * XDress + +Are you writing extensions from scratch? + + * Cython + + * Do you love C++ ? + + - Boost Python + +Do you want a "thick" wrapper around a C/C++ lib: + + * Cython + diff --git a/_sources/extensions.txt b/_sources/extensions.txt new file mode 100644 index 0000000..53df1db --- /dev/null +++ b/_sources/extensions.txt @@ -0,0 +1,1007 @@ +.. _extensions: + +=================================== +Extending Python with Complied Code +=================================== + + +- Chris Barker + +.. Contents: + +.. .. toctree:: +.. :maxdepth: 2 + +Topics +======= + +.. rst-class:: left + + * Motivation + + * The C API + + * ctypes + + * Cython + + * Auto-generating wrappers + + * Others to consider + + +Motivation +----------- + +Motivations for exiting pure Python + + - Performance + - Integration with existing C libraries + - Working as a glue language + - Implement new builtin types + +What is an extension module? + + - written in C (C API) + - compiled code + - lets you work directly with the CPython engine + +Further reading: + +http://docs.python.org/2/extending/extending.html + +Example Case +------------- + +To focus on the integration techniques, rather than complex C code, +we'll work with the following function we want to integrate:: + + #include + + int add(int x, int y) { + return x+y; + } + int main(void) { + int w = 3; + int q = 2; + printf("%d + %d = %d\n\n", w, q, add(w,q)); + } + +This is, of course, trivial and built in to Python, but the techniques +are the same. + +(``Examples/week-08-extensions/pure-c/add.c``) + + +.. nextslide:: Building + +Build it with the Makefile (Linux and OS-X):: + + all: add; gcc -o add add.c + +:: + + $ make + gcc -o add add.c + +and run it:: + + $ ./add + 3 + 2 = 5 + +So are simple function works -- but how to call it from Python? + +The C API +--------- + +Write your function in pure C using the Python API and import it into Python + +Good for integrating with C library functions and system calls + +The API isn't trivial to learn + +Lots of opportunity for error -- you must do manual reference counting: + +(http://docs.python.org/2/c-api/refcounting.html) + +Further reading: + + - http://docs.python.org/2/extending/extending.html + + - Python 2.7 source code + + +Intro to the C API +------------------- + +You'll need the Python dev package installed on your system + +Pull in the Python API to your C code via:: + + #include + /* + Note: Since Python may define some pre-processor definitions which + affect the standard headers on some systems, you must include + Python.h before any standard headers are included. + + stdio.h, string.h, errno.h, and stdlib.h are included for you. + */ + +Passing Data in and out of your function +----------------------------------------- + +Function arguments must be parsed on the way in and the way out + +On the way in, we can call ``PyArg_ParseTuple``:: + + if (!PyArg_ParseTuple(args, "s", &var1, ...)) + return NULL; + +http://docs.python.org/2/c-api/arg.html#PyArg_ParseTuple + +| + +On the way out, we can call ``Py_BuildValue``:: + + PyObject* Py_BuildValue(const char *format, ...) + +http://docs.python.org/2/c-api/arg.html#Py_BuildValue + +Registering your functions +--------------------------- + +First, register the name and address of your function in the method table:: + + // Module's method table and initialization function + static PyMethodDef AddMethods[] = { + {"add", add, METH_VARARGS, "add two numbers"}, + {NULL, NULL, 0, NULL} // sentinel + }; + +https://docs.python.org/2/extending/extending.html#the-module-s-method-table-and-initialization-function + + +Initializing the module +----------------------- + +Define an initialization function:: + + PyMODINIT_FUNC // does the right thing on Windows, Linux, etc. + initadd(void) { + // Module's initialization function + // Will be called again if you use Python's reload() + (void) Py_InitModule("add", AddMethods); + } + +It *must* be called ``initthe_module_name`` + +https://docs.python.org/2/extending/extending.html#the-module-s-method-table-and-initialization-function + +The whole thing: +----------------- + +:: + + #include + + static PyObject * + add(PyObject *self, PyObject *args) + { + int x, y, sts; + + if (!PyArg_ParseTuple(args, "ii", &x, &y)) + return NULL; + sts = x+y; + return Py_BuildValue("i", sts); + } + static PyMethodDef AddMethods[] = { + {"add", add, METH_VARARGS, "add two numbers"}, + {NULL, NULL, 0, NULL} // sentinel + }; + PyMODINIT_FUNC initadd(void) { + (void) Py_InitModule("add", AddMethods); + } + +Building your extension +------------------------ + +``setuptools`` provides features for automatically building extensions:: + + from setuptools import setup, Extension + setup( + name='Cadd', + version='1.0', + description='simple c extension for an example', + ext_modules=[Extension('add', sources=['add.c'])], + ) + +(``distutils`` does too -- but setuptools is getting updated to better +support new stuff) + +Run the setup.py:: + + python setup.py build_ext --inplace + +(you can also just do ``install`` or ``develop`` if you want it properly +installed) + + +Run the tests +-------------- + +``test_add.py``:: + + import pytest + + import add + + def test_basic(): + assert add.add(3,4) == 7 + + def test_negative(): + assert add.add(-12, 5) == -7 + + def test_float(): + with pytest.raises(TypeError): + add.add(3, 4.0) + +``$ py.test`` + + + +Subtleties we avoided: +====================== + +There are a LOT of things you need to get right with a hand-written +C Extension. + + +Exception handling +------------------- + +Works somewhat like the Unix errno variable: + +* Global indicator (per thread) of the last error that occurred. +* Most functions don’t clear this on success, but will set it to indicate the cause of the error on failure. +* Most functions also return an error indicator: + + - NULL if they are supposed to return a pointer, + - -1 if they return an integer + - The PyArg_*() functions return 1 for success and 0 for failure (and they set the Exception for you) + +The easy way to set this indicator is with PyErr_SetString + +http://docs.python.org/2/c-api/exceptions.html + +(you can completely control the Exception handling if you need to) + + +ReferenceCounting +------------------ + +Whenever you create or no longer need a Py_Object, you need to increment or decrement the reference count: + +``Py_INCREF(x)`` and ``Py_DECREF(x)`` + +``PyArg_ParseTuple`` and ``Py_BuildValue`` + +Handle this for you. + +But if you're creating new objects inside your function, you need to keep track. + +And what it the function raises an exception in the middle and can't finish? + +This gets really ugly and error-prone (and hard to debug!) + +LAB +---- + +LAB 1: + +* Add another function to the add.c file that multiplies two numbers instead. +* Write some test code and make sure it works. + +LAB 2: + +* Find the divide module in the examples/c-api directory +* What happens when you call divide.divide(1/0)? +* This is a different result than a pure Python 1/0, which throws an exception + +Advanced: + +* Change the divide method to throw an appropriate exception in the + divide-by-zero case + +ctypes +====== + +Isn't there an easier way to just call some C code? + + +What is ctypes? +--------------- + +A foreign function interface in Python + +Binds functions in shared libraries to Python functions + +Benefits: + - Ships with Python, since 2.5 + - No new language to learn, it's all Python + +Drawbacks: + - Performance hit for on the fly type translation + - "thicker" interface in python + +Example:: + + from ctypes import * + add = cdll.LoadLibrary("add.so") + print add.add(3,4) + +Further reading: + +http://docs.python.org/2/library/ctypes.html + + +Calling functions with ctypes +------------------------------ + +The shared lib must be loaded:: + + add = ctypes.cdll.LoadLibrary("add.so") + +An already loaded lib can be found with:: + + libc = ctypes.CDLL("/usr/lib/libc.dylib") + +ctypes comes with a utility to help find libs:: + + ctypes.util.find_library(name) + +(good for system libs) + +.. nextslide:: + +Once loaded, a ctypes wrapper around a c function can be called directly:: + + print add.add(3,4) + +But.... + + +C is statically typed -- once compiled, the function must be called with +the correct types. + +ctypes Data Types +----------------- + +ctypes will auto-translate these native types: + + - ``None`` + - int + - byte strings (``bytes()``, ``str()``) + - ``unicode`` (careful! unicode is ugly in C!) + +These can be directly used as parameters when calling C functions. + +.. nextslide:: + +Most types must be wrapped in a ctypes data type:: + + printf("An int %d, a double %f\n", 1234, c_double(3.14)) + +There are ctypes wrappers for all the "standard" C types + +http://docs.python.org/2/library/ctypes.html#fundamental-data-types + + +You can also do pointers to types:: + + a_lib.a_function( ctypes.byref(c_float(x))) + +http://docs.python.org/2/library/ctypes.html#passing-pointers-or-passing-parameters-by-reference + +.. nextslide:: C structs + +You can define C structs:: + + >>> class POINT(ctypes.Structure): + ... _fields_ = [("x", ctypes.c_int), + ... ("y", ctypes.c_int)] + ... + >>> point = POINT(10, 20) + >>> print point.x, point.y + 10 20 + >>> point = POINT(y=5) + >>> print point.x, point.y + 0 5 + +.. nextslide:: Custom Python Classes + +You can define how to pass data from your custom classes to ctypes: + +Define an ``_as_parameter_`` attribute (or property):: + + class MyObject(object): + def __init__(self, number): + self._as_parameter_ = number + + obj = MyObject(32) + printf("object value: %d\n", obj) + +https://docs.python.org/2/library/ctypes.html#calling-functions-with-your-own-custom-data-types + +(careful with types here!) + +.. nextslide:: Return Types + +To define the return type, define the ``restype`` attribute. + +Pre-defining the entire function signature:: + + libm.pow.restype = ctypes.c_double + libm.pow.argtypes = [ctypes.c_double, ctypes.c_double] + +And you can just call it like a regular python function -- ctypes will type check/convert at run time:: + + In [10]: libm.pow('a string', 4) + --------------------------------------------------------------------------- + ArgumentError Traceback (most recent call last) + in () + ----> 1 libm.pow('a string', 4) + + ArgumentError: argument 1: : wrong type + +Some more features +------------------- + +Defining callbacks into Python code from C:: + + ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) + +http://docs.python.org/2/library/ctypes.html#ctypes.CFUNCTYPE + +| + +Numpy provides utilities for numpy arrays: + +http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ctypes.html + +(works well for C code that takes "classic" C arrays) + + +Summary: +-------- + +``ctypes`` allows you to call shared libraries: + - Your own custom libs + - System libs + - Proprietary libs + +Supports almost all of C: + - Custom data types + + - structs + - unions + - pointers + + - callbacks + +.. nextslide:: + +* Upside: + + - You can call system libs with little code + - You don't need to compile anything + + - at least for system and pre-compiled libs + +* Downsides: + + - You need to specify the interface + + - and it is NOT checked for you! + + - Translation is done on the fly at run time + + - performance considerations + +LAB +---- + +In ``Examples/week-08-extensions/ctypes`` you'll find ``add.c`` + +You can build a shared lib with it with ``make`` +(``make.bat``) on Windows. + +``test_ctypes.py`` will call that dll, and a few system dlls. + +* Take a look at what's there, and how it works. + +* add another function to add.c, that takes different types (maybe divide?) + +* rebuild, and figure out how to call it with ctypes. + +* Try calling other system functions with ctypes. + + +Cython +====== + +A Python like language with static types which compiles down to C code +for Python extensions. + + +Cython +------- + +* Can write pure python + + - Fully understands the python types + +* With careful typing -- you get pure C (and pure C speed) + +* Can also call other C code: libraries or compiled in. + +* Used for custom Python extensions and/or call C and C++ code. + +.. nextslide:: + +Further reading: + +**Web site:** + +http://www.cython.org/ + +**Documentation:** + +http://docs.cython.org/ + +**Wiki:** + +https://github.com/cython/cython/wiki + + + +Developing with Cython +---------------------- + +First, install cython with:: + + pip install cython + +Cython files end in the .pyx extension. An example add.pyx:: + + def add(x, y): + cdef int result=0 + result = x + y + return result + +(looks a lot like Python, eh?) + +.. nextslide:: + +To build a cython module: write a setup.py that defines the extension:: + + from setuptools import setup + from Cython.Build import cythonize + + setup(name = "cython_example", + ext_modules = cythonize(['cy_add1.pyx',]) + ) + +``cythonize`` is a utility that sets up extension module builds for you in a cython-aware way. + +Building a module +------------------ + +For testing, it's helpful to do:: + + python setup.py build_ext --inplace + +which builds the extensions, and puts the resulting modules right in with the code. + +If you have your setup.py set up for a proper package, you can do:: + + python setup.py develop + or + python setup.py install + +Just like for pure-python packages. + +.. nextslide:: + +You can also do only the Cython step by hand at the command line:: + + cython a_file.pyx + +Produces: ``a_file.c`` file that you can examine, or compile. + +For easier reading, you can generate an annotated html version:: + + cython -a a_file.pyx + +Generates``a_file.html`` html file that is easier to read and gives +additional information that is helpful for debugging and performance +tuning. + +More on this later. + + +Basic Cython +------------- + +Cython functions can be declared three ways:: + + def foo # callable from Python + + cdef foo # only callable from Cython/C + + cpdef foo # callable from both Cython and Python + +Inside those functions, you can write virtually any python code. + +But the real magic is with the optional type declarations: the ``cdef`` lines. Well see this as we go... + + +Calling a C function from Cython +-------------------------------- + +You need to tell Cython about extenal functions you want to call with ``cdef extern``. + +The Cython code:: + + # distutils: sources = add.c + # This tells cythonize that you need that c file. + + # telling cython what the function we want to call looks like. + cdef extern from "add.h": + # pull in C add function, renaming to c_add for Cython + int c_add "add" (int x, int y) + + def add(x, y): + # now that cython knows about it -- we can just call it. + return c_add(x, y) + +.. nextslide:: + +and the setup.py:: + + from setuptools import setup + from Cython.Build import cythonize + + setup(name = "cython_example", + ext_modules = cythonize(['cy_add_c.pyx'] ) + ) + + +.. nextslide:: + +To build it:: + + $ python setup.py build_ext --inplace + +and test it:: + + Chris$ python test_cy_add_c.py + + if you didn't get an assertion, it worked + + +A pure Cython solution +---------------------- + +Here it is as python code:: + + def add(x, y): + result = x + y + return result + +Which we can put in a pyx file and compile with the setup.py:: + + #!/usr/bin/env python + + from setuptools import setup + from Cython.Build import cythonize + + setup(name = "cython_example", + ext_modules = cythonize(['cy_add1.pyx', + ]) + ) + +.. nextslide:: + +and build:: + + python setup.py build_ext --inplace + +and test:: + + Chris$ python test_cy_add1.py + + if you didn't get an assertion, it worked + +.. nextslide:: + +But this is still essentially Python. So let's type define it:: + + def add(int x, int y): + + cdef int result=0 + result = x + y + + return result + +now Cython knows that ``x, y``, and ``result`` are ``ints``, and can use +raw C for that. + +Build and test again:: + + Chris$ python setup.py build_ext --inplace + + Chris$ python test_cy_add2.py + +If you didn't get an assertion, it worked + + +A real Example: the Cython process +----------------------------------- + +Consider a more expensive function:: + + def f(x): + return x**2-x + + def integrate_f(a, b, N): + s = 0 + dx = (b-a)/N + for i in range(N): + s += f(a+i*dx) + return s * dx + +This is a good candidate for Cython -- an essentially static function called a lot. + +Cython from pure Python to C +----------------------------- + +Let's go through the steps one by one. In the ``Examples/week-08-extensions/cython/integrate`` directory:: + + + cy_integrate1.pyx + cy_integrate2.pyx + cy_integrate3.pyx + cy_integrate4.pyx + cy_integrate5.pyx + cy_integrate6.pyx + cy_integrate7.pyx + +At each step, we'll time and look at the output from:: + + $cython -a cy_integrate1.pyx + +AGC Example +----------- + +Another useful example of doing something useful, and using a numpy +array is in: + +Examples/week-08-extensions/AGC_example + +This one impliments an Automatic Gain Control Signal processing filter. + +It turns out that you can use some advanced numpy tricks to get pretty +good performancew with this filter, but you can't get full-on speed +without some compiled code. + + +This example uses all of: + * Pure Cython + * C called from Cython + * f2py and Fortran + + +Auto-generated wrappers +======================= + +There are few ways to auto-generate wrapper for C/C++ code: + +SWIG + +SIP + +XDress + +[also Boost-Python -- not really a wrapper generator] + +f2py -- for Fortran + + +SWIG +----- + +**Simple Wrapper Interface Generator** + +A language agnostic tool for integrating C/C++ code with high level languages + +**Advantages:** + + * Code generation for other environments than Python. + + * Doesn't require modification to your C source. + +**Disadvantages:** + + * For anything non-trivial, requires substantial effort to develop the interface. + + * Awkward when you want to mix python and C in the interface. + + * Inefficient passing of "Swigified Pointers" + + +.. nextslide:: + +Language interfaces: + + * Python + + * Tcl + + * Perl + + * Guile (Scheme/Lisp) + + * Java + + * Ruby + +And a bunch of others: + +http://www.swig.org/compat.html#SupportedLanguages + +Further reading: + +http://www.swig.org/Doc1.3/Python.html + +SWIGifying add() +---------------- + +SWIG doesn't require modification to your C source code + +The language interface is defined by an "interface file", usually with +a suffix of ``.i`` + +From there, SWIG can generate interfaces for the languages it supports + +The interface file contains ANSI C prototypes and variable declarations + +The ``%module`` directive defines the name of the module that will be +created by SWIG + +Creating a wrapper: +------------------- + +(``Examples/week-08-extensions/swig``) + +Create ``add.i``:: + + %module add + %{ + %} + extern int add(int x, int y); + +Create ``setup.py``:: + + from setuptools import setup, Extension + + setup( + name='add', + py_modules=['add'], + ext_modules=[ + Extension('_add', sources=['add.c', 'add.i']) + ] + ) + +.. nextslide:: + +And build it:: + + python setup.py build_ext --inplace + +NOTE: distutils (and thus setuptools) "knows" about SWIG, so it does the +swig step for you when you give it a \*.i file. + +Notice what gets created: + + * an ``add_wrap.c`` file -- the wrapper code. + * an ``add.py`` file -- python code that calls the C function + * an ``_add.so`` (or ``_add.pyd``) file -- the compiled extension + +.. nextslide:: + + +You can then run the code:: + + python -c 'import add; print add.add(4,5)' + +http://www.swig.org/Doc2.0/SWIGDocumentation.html#Introduction_nn5 + +Installing SWIG +---------------- + +On the SWIG download page, there is a source tarball for \*nix, and +Windows binaries: + +http://www.swig.org/download.html + +For Linux: + +You may have it in your package repository: + +apt-get install swig + +If not, download the tarball, unpack it, and:: + + ./configure + make + sudo make install + +should do it. + +.. nextslide:: OS-X Install + +For OS-X: the same thing, except you also need the "pcre" package. +Which you can get from: + +http://sourceforge.net/projects/pcre/files/pcre/8.37/pcre-8.37.tar.gz/download + +Put it in the dir created when the SWIG source was unpacked. + +Unpack it, then run this to set it up for use with SWIG:: + + Tools/pcre-build.sh + +Then you can do the standard:: + + ./configure + make + make install + + + + +Decisions, Decisions... +======================= + +.. rst-class:: large + + So what to use??? + + +My decision tree +----------------- + +Are you calling a few system library calls? + + * Use ctypes + +Do you have a really big library to wrap? -- use a wrapper generator: + + * SWIG (other languages?) + + * SIP + + * XDress + +Are you writing extensions from scratch? + + * Cython + + * Do you love C++ ? + + - Boost Python + +Do you want a "thick" wrapper around a C/C++ lib: + + * Cython + diff --git a/_sources/homework/index.rst.txt b/_sources/homework/index.rst.txt new file mode 100644 index 0000000..92a410a --- /dev/null +++ b/_sources/homework/index.rst.txt @@ -0,0 +1,5 @@ +Homework Materials +====================== + +There may be some stuff here for homework + diff --git a/_sources/homework/index.txt b/_sources/homework/index.txt new file mode 100644 index 0000000..92a410a --- /dev/null +++ b/_sources/homework/index.txt @@ -0,0 +1,5 @@ +Homework Materials +====================== + +There may be some stuff here for homework + diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 0000000..8655fb7 --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,145 @@ +.. _course_outline: + +============================== +System Development with Python +============================== + +| +| + +.. rst-class:: left credit + + These materials copyright Christopher Barker, Joseph Sheedy and Maria McKinley. + + Licensed under the Creative Commons Attribution-ShareAlike 4.0 International Public License. + + https://creativecommons.org/licenses/by-sa/4.0/legalcode + +Syllabus: +========= + +.. rst-class:: left + + :ref:`syllabus` + + +Session 1 +========= + +.. rst-class:: left + + - :ref:`debugging` + + - :ref:`logging` + +Session 2 +========= + +.. rst-class:: left + + - :ref:`testing` + + +Session 3 +========= + +.. rst-class:: left + + - :ref:`serialization` + + - :ref:`code_review` + + +Session 4 +========= + +Code Reviews begin + +.. rst-class:: left + + - :ref:`packaging` + + - :ref:`documentation` + + +Session 5 +========= +.. rst-class:: left + + - :ref:`nosql` + + - :ref:`scipy` + +Session 6 +========= + +.. rst-class:: left + + - :ref:`advanced_oo` + + - :ref:`metaclasses` + + +Session 7 +========= + +.. rst-class:: left + + - :ref:`threading` + + +Session 8 +========= + +.. rst-class:: left + + - :ref:`datetime` + + - :ref:`profiling` + +Session 9 +========= + +.. rst-class:: left + + - :ref:wxpython + + +Session 10 +========== + +Student Presentations + + +Other Topics: +============= + +These are topics that we don't have time for in class, but you might want to look at these notes just the same: + + - `Working with SQL Databases `_ + - :ref:`unicode` + - :ref:`extensions` + - :ref:`weak_references` + + +Assorted Notes: +=============== + +Notes on other topics, or that aren't specific to a particular lecture -- or are general purpose, in any case. + +:ref:`installing_scipy` + +:ref:`building_extensions` + +:ref:`installing_wxpython` + +:ref:`installing_wxpython_demo` + + + +.. - serialization review / XML + +.. - functools +.. - itertools +.. - Beautiful idiomatic code + diff --git a/_sources/index.txt b/_sources/index.txt new file mode 100644 index 0000000..4dab990 --- /dev/null +++ b/_sources/index.txt @@ -0,0 +1,145 @@ +.. _course_outline: + +============================== +System Development with Python +============================== + +| +| + +.. rst-class:: left credit + + These materials copyright Christopher Barker, Joseph Sheedy and Maria McKinley. + + Licensed under the Creative Commons Attribution-ShareAlike 4.0 International Public License. + + https://creativecommons.org/licenses/by-sa/4.0/legalcode + +Syllabus: +========= + +.. rst-class:: left + + :ref:`syllabus` + + +Session 1 +========= + +.. rst-class:: left + + - :ref:`debugging` + + - :ref:`logging` + +Session 2 +========= + +.. rst-class:: left + + - :ref:`testing` + + +Session 3 +========= + +.. rst-class:: left + + - :ref:`serialization` + + - :ref:`code_review` + + +Session 4 +========= + +Code Reviews begin + +.. rst-class:: left + + - :ref:`packaging` + + - :ref:`documentation` + + +Session 5 +========= +.. rst-class:: left + + - :ref:`nosql` + + - :ref:`scipy` + +Session 6 +========= + +.. rst-class:: left + + - :ref:`advanced_oo` + + - :ref:`metaclasses` + + +Session 7 +========= + +.. rst-class:: left + + - :ref:`threading` + + +Session 8 +========= + +.. rst-class:: left + + - :ref:`datetime` + + - :ref:`profiling` + +Session 9 +========= + +.. rst-class:: left + + - :ref:`wxpython` + + +Session 10 +========== + +Student Presentations + + +Other Topics: +============= + +These are topics that we don't have time for in class, but you might want to look at these notes just the same: + + - `Working with SQL Databases `_ + - :ref:`unicode` + - :ref:`extensions` + - :ref:`weak_references` + + +Assorted Notes: +=============== + +Notes on other topics, or that aren't specific to a particular lecture -- or are general purpose, in any case. + +:ref:`installing_scipy` + +:ref:`building_extensions` + +:ref:`installing_wxpython` + +:ref:`installing_wxpython_demo` + + + +.. - serialization review / XML + +.. - functools +.. - itertools +.. - Beautiful idiomatic code + diff --git a/_sources/logging.rst.txt b/_sources/logging.rst.txt new file mode 100644 index 0000000..14be2dd --- /dev/null +++ b/_sources/logging.rst.txt @@ -0,0 +1,331 @@ +.. _logging: + +############################### +Logging and the logging module +############################### + + +What is Logging? +================ + +.. rst-class:: left small + + What is logging? + + In computing, a logfile is a file that records either events that occur in an operating system or other software runs, or messages between different users of a communication software. + + Logging is the act of keeping a log. In the simplest case, messages are written to a single logfile. + + + (https://en.wikipedia.org/wiki/Logfile) + + But in fact, a file is only *one* place to keep a log. You may want to send a log of what your program is doing to another system, to the console, or???? + +what to log? +------------ + +.. rst-class:: medium:: + + What might you want to log? + +System information + +Error messages + +Fine-grain tracing output + + +The logging module +------------------- + +A flexible logging system that comes with the standard library + +Any module using the logging api can have logging output routed the same +as your code. + +Resources for learning more: + +https://docs.python.org/3.5/howto/logging.html + +http://docs.python-guide.org/en/latest/writing/logging/ + +https://pymotw.com/2/logging/ + +NOTE: the last one hasn't been updated for py3 -- but not much (anything?) has changed. + +Why not ``print()``? +-------------------- + +We've all been using ``print()`` all over the place to track what's going on in a program. + +And I still use it -- a lot. + +But we (usually) don't want all sorts of crap sent to stdout when the program is running in production. + +So we comment out or delete those ``print()`` s -- but if we wanted to know what the program was doing when developing -- maybe we want to know when something unanticipated goes wrong, too? + +The ``logging`` module give you a flexible system that allows you to monitor what's going on in your system, when you need to, without cluttering things up when you don't need it. + + +Background +========== + +.. rst-class:: left + + There are lots of good tutorials, etc, on the web for getting you started with *useing* the logging module. + + But not much about how it works -- how it is structured. + + I found it hard to get beyond the basics without that knowledge, so the following should help. + + The logging module provides a very flexible framework for customizing the logging in a simple or complex application. + +The ``logging`` module +----------------------- + +.. code-block:: python + + import logging + +The logging module not only provides the classes and functions required to build a logging system, but also a place to centrally manage the logging for an entire application. + +This allows you to set up logging in one place, and everywhere in the app, the system can be used. + +So, for instance, when developing and debugging, you may want logging messages to go to the console, but for deployment, to log files. + +That configuration can be changed in one place. + +(NOTE: this is one good reason to prefer logging over ``print()``) + +Logging "levels" +---------------- + +The built in way to catagorize logging messages is by level. + +Levels are ordered numerically, so you can think of them as in order of importance, and it's easy to choose how much detail you want. + +The built-in set is:: + + CRITICAL 50 + ERROR 40 + WARNING 30 + INFO 20 + DEBUG 10 + +so DEBUG provides the most detail, and CRITICAL you'd pretty much always want to see. + +.. nextslide:: + +The logging API provides easy ways to send messages with these levels: + +.. code-block:: python + + logging.debug('this is a debuggin message') + + +https://docs.python.org/3/library/logging.html#levels + + +The logging classes +------------------- + +The four main classes (you need to deal with) for logging + +- Loggers - the interface for your code +- Handlers - handle log routing +- Filters - define which log messages to let through +- Formatters - how the log messages get rendered + +The ``Logger`` class +-------------------- + +The ``Logger`` class is the core class that handles logging. + +Messages get sent to a ``Logger`` instance, and it is responsibile for routing them appropriately. + +``Logger`` s can be nested in a hierarchical fashion, so that a message can be sent to sub-loggers, and any messages not handled will be passed the chain to eventually be handled by the "root" logger. + +There is always a root logger, and often the only one you need. + +Each ``Logger`` represents a single logging channel. + +.. nextslide:: + +``Logger`` instances are given text names, with module-style "dots" representing the hierarchy: + +.. code-block:: python + + "main" + "main.sub_logger1" + "main.sub_logger2" + ... + +The "root" logger has no name, but is the root of all created loggers + +The logging module keeps track of all the loggers you create, so you can reference them by name. + +``logging.get_logger()`` +------------------------ + +The ``logging.get_logger()`` function returns the logger you ask for: + +.. code-block:: python + + the_root_logger = logging.get_logger() + another_logger = logging.get_logger("name") + +If the logger you ask for doesn't exist, ``get_logger`` will create a new one for you by that name. (It won't be configured, though...) + +This whole system allows you to have multiple loggers without having to pass logging instances around. + +The ``Handler`` classes +----------------------- + +logging ``Handler`` s are what actually do the work of, well, handling, the log message. + +Formatting it, and actually writing to a file or somehow performing the 'log' duty. + +There are handlers for writing to files, streams (stdout, stderr), sockets, and nifty things like automaticaly rotating log files. + +And, of course, you can make your own. + +Each logger can have multiple Handlers + +.. nextslide:: + +You will most likely use: + + - ``FileHandler`` + - ``StreamHandler`` + +The others are documented here. + +https://docs.python.org/3/library/logging.handlers.html#module-logging.handlers + +The ``Formatter`` classes +------------------------- + +``Formatters`` are responsible for formatting the log message. + +Each log message is stored in a ``LogRecord`` object, which has a lot of data about the message and where it came from. + +So you can use a formatter to add the data you want to your log entry. + +.. code-block:: python + + formatter = logging.Formatter('%(levelname)9s - %(module)s - %(message)s') + +``levelname`` is the "level" of the log message: debug, warning, etc. ``module`` is the name of the module the message came from. ``message`` is the message itself. + +There are lots of other options: + +https://docs.python.org/3/library/logging.html#logrecord-attributes + +Each ``Handler`` can have its own ``Formatter`` + +The Filter classes +------------------ + +Each ``Logger`` can have a ``Filter`` object. + +``Filters`` determine lwhich messages will be handled by a given logger, and which pass on to other loggers up the hierarchy. + +They can do very flexible filtering based on where the message came from, etc. + +But only really needed for complex systems: + +Loggers filter by "level" by default -- which is enough for most uses. + + +Basic logging usage +------------------- + +As you can see from the above -- the logging system is a complex nest of classes that can be configured and mixed and matched in complex ways. + +The system was ported from Java -- can you tell? + +However, the module provides a Pythonic API for common usage: the ``logging.basicConfig()`` function. + +Example: + +.. code-block:: python + + import logging + + logging.basicConfig(filename='example.log', + filemode='w', + format='%(asctime)s %(message)s', + level=logging.DEBUG) + +.. nextslide:: + +This creates a "root" logger, and sets it up with: + +* a ``FileHandler`` with the given filename and mode + + - The mode is the file opening mode: 'w' to clobber and make a new file each time, 'a' to append to an existing file + +* sets up the handler to use the provided format string + + - ``asctime`` provides a datetime stamp (you can spcify a format for that, too) + +* sets the level to debug -- so all messages will get logged. + + +What does ``basicConfig`` do for you? +-------------------------------------- +A LOT! + +If you were to do this by hand: + +.. code-block:: python + + filename = 'example.log' + filemode = 'w' + handler = logger.FileHandler(filename, mode) + format_str = '%(asctime)s %(message)s' + fmt = logger.Formatter(format_str) + handler.setFormatter(fmt) + logging.root.addHandler(h) + logging.root.setLevel(logging.EBUG) + +Wouldn't that be fun? + + +A more complex logging setup +---------------------------- + +See: + +see ``Examples/logging/example.py`` + +In there, a logging system is set up that logs to a file, and also the console. + +It calls a fake "application" that does things in random order, logging as it goes... + +Let's go check it out! + +References +---------- + +The logging system is very powerful and flexible. + +And frankly, not as clean and Pythonic as it could be... + +So it's pretty tricky to figure out. + +I highly recommend the cookbook to get beyond the basics: + +https://docs.python.org/3/howto/logging-cookbook.html + +Also these: + +http://atlee.ca/blog/posts/diving-into-python-logging.html + +http://victorlin.me/posts/2012/08/26/good-logging-practice-in-python + + + + + + diff --git a/_sources/logging.txt b/_sources/logging.txt new file mode 100644 index 0000000..14be2dd --- /dev/null +++ b/_sources/logging.txt @@ -0,0 +1,331 @@ +.. _logging: + +############################### +Logging and the logging module +############################### + + +What is Logging? +================ + +.. rst-class:: left small + + What is logging? + + In computing, a logfile is a file that records either events that occur in an operating system or other software runs, or messages between different users of a communication software. + + Logging is the act of keeping a log. In the simplest case, messages are written to a single logfile. + + + (https://en.wikipedia.org/wiki/Logfile) + + But in fact, a file is only *one* place to keep a log. You may want to send a log of what your program is doing to another system, to the console, or???? + +what to log? +------------ + +.. rst-class:: medium:: + + What might you want to log? + +System information + +Error messages + +Fine-grain tracing output + + +The logging module +------------------- + +A flexible logging system that comes with the standard library + +Any module using the logging api can have logging output routed the same +as your code. + +Resources for learning more: + +https://docs.python.org/3.5/howto/logging.html + +http://docs.python-guide.org/en/latest/writing/logging/ + +https://pymotw.com/2/logging/ + +NOTE: the last one hasn't been updated for py3 -- but not much (anything?) has changed. + +Why not ``print()``? +-------------------- + +We've all been using ``print()`` all over the place to track what's going on in a program. + +And I still use it -- a lot. + +But we (usually) don't want all sorts of crap sent to stdout when the program is running in production. + +So we comment out or delete those ``print()`` s -- but if we wanted to know what the program was doing when developing -- maybe we want to know when something unanticipated goes wrong, too? + +The ``logging`` module give you a flexible system that allows you to monitor what's going on in your system, when you need to, without cluttering things up when you don't need it. + + +Background +========== + +.. rst-class:: left + + There are lots of good tutorials, etc, on the web for getting you started with *useing* the logging module. + + But not much about how it works -- how it is structured. + + I found it hard to get beyond the basics without that knowledge, so the following should help. + + The logging module provides a very flexible framework for customizing the logging in a simple or complex application. + +The ``logging`` module +----------------------- + +.. code-block:: python + + import logging + +The logging module not only provides the classes and functions required to build a logging system, but also a place to centrally manage the logging for an entire application. + +This allows you to set up logging in one place, and everywhere in the app, the system can be used. + +So, for instance, when developing and debugging, you may want logging messages to go to the console, but for deployment, to log files. + +That configuration can be changed in one place. + +(NOTE: this is one good reason to prefer logging over ``print()``) + +Logging "levels" +---------------- + +The built in way to catagorize logging messages is by level. + +Levels are ordered numerically, so you can think of them as in order of importance, and it's easy to choose how much detail you want. + +The built-in set is:: + + CRITICAL 50 + ERROR 40 + WARNING 30 + INFO 20 + DEBUG 10 + +so DEBUG provides the most detail, and CRITICAL you'd pretty much always want to see. + +.. nextslide:: + +The logging API provides easy ways to send messages with these levels: + +.. code-block:: python + + logging.debug('this is a debuggin message') + + +https://docs.python.org/3/library/logging.html#levels + + +The logging classes +------------------- + +The four main classes (you need to deal with) for logging + +- Loggers - the interface for your code +- Handlers - handle log routing +- Filters - define which log messages to let through +- Formatters - how the log messages get rendered + +The ``Logger`` class +-------------------- + +The ``Logger`` class is the core class that handles logging. + +Messages get sent to a ``Logger`` instance, and it is responsibile for routing them appropriately. + +``Logger`` s can be nested in a hierarchical fashion, so that a message can be sent to sub-loggers, and any messages not handled will be passed the chain to eventually be handled by the "root" logger. + +There is always a root logger, and often the only one you need. + +Each ``Logger`` represents a single logging channel. + +.. nextslide:: + +``Logger`` instances are given text names, with module-style "dots" representing the hierarchy: + +.. code-block:: python + + "main" + "main.sub_logger1" + "main.sub_logger2" + ... + +The "root" logger has no name, but is the root of all created loggers + +The logging module keeps track of all the loggers you create, so you can reference them by name. + +``logging.get_logger()`` +------------------------ + +The ``logging.get_logger()`` function returns the logger you ask for: + +.. code-block:: python + + the_root_logger = logging.get_logger() + another_logger = logging.get_logger("name") + +If the logger you ask for doesn't exist, ``get_logger`` will create a new one for you by that name. (It won't be configured, though...) + +This whole system allows you to have multiple loggers without having to pass logging instances around. + +The ``Handler`` classes +----------------------- + +logging ``Handler`` s are what actually do the work of, well, handling, the log message. + +Formatting it, and actually writing to a file or somehow performing the 'log' duty. + +There are handlers for writing to files, streams (stdout, stderr), sockets, and nifty things like automaticaly rotating log files. + +And, of course, you can make your own. + +Each logger can have multiple Handlers + +.. nextslide:: + +You will most likely use: + + - ``FileHandler`` + - ``StreamHandler`` + +The others are documented here. + +https://docs.python.org/3/library/logging.handlers.html#module-logging.handlers + +The ``Formatter`` classes +------------------------- + +``Formatters`` are responsible for formatting the log message. + +Each log message is stored in a ``LogRecord`` object, which has a lot of data about the message and where it came from. + +So you can use a formatter to add the data you want to your log entry. + +.. code-block:: python + + formatter = logging.Formatter('%(levelname)9s - %(module)s - %(message)s') + +``levelname`` is the "level" of the log message: debug, warning, etc. ``module`` is the name of the module the message came from. ``message`` is the message itself. + +There are lots of other options: + +https://docs.python.org/3/library/logging.html#logrecord-attributes + +Each ``Handler`` can have its own ``Formatter`` + +The Filter classes +------------------ + +Each ``Logger`` can have a ``Filter`` object. + +``Filters`` determine lwhich messages will be handled by a given logger, and which pass on to other loggers up the hierarchy. + +They can do very flexible filtering based on where the message came from, etc. + +But only really needed for complex systems: + +Loggers filter by "level" by default -- which is enough for most uses. + + +Basic logging usage +------------------- + +As you can see from the above -- the logging system is a complex nest of classes that can be configured and mixed and matched in complex ways. + +The system was ported from Java -- can you tell? + +However, the module provides a Pythonic API for common usage: the ``logging.basicConfig()`` function. + +Example: + +.. code-block:: python + + import logging + + logging.basicConfig(filename='example.log', + filemode='w', + format='%(asctime)s %(message)s', + level=logging.DEBUG) + +.. nextslide:: + +This creates a "root" logger, and sets it up with: + +* a ``FileHandler`` with the given filename and mode + + - The mode is the file opening mode: 'w' to clobber and make a new file each time, 'a' to append to an existing file + +* sets up the handler to use the provided format string + + - ``asctime`` provides a datetime stamp (you can spcify a format for that, too) + +* sets the level to debug -- so all messages will get logged. + + +What does ``basicConfig`` do for you? +-------------------------------------- +A LOT! + +If you were to do this by hand: + +.. code-block:: python + + filename = 'example.log' + filemode = 'w' + handler = logger.FileHandler(filename, mode) + format_str = '%(asctime)s %(message)s' + fmt = logger.Formatter(format_str) + handler.setFormatter(fmt) + logging.root.addHandler(h) + logging.root.setLevel(logging.EBUG) + +Wouldn't that be fun? + + +A more complex logging setup +---------------------------- + +See: + +see ``Examples/logging/example.py`` + +In there, a logging system is set up that logs to a file, and also the console. + +It calls a fake "application" that does things in random order, logging as it goes... + +Let's go check it out! + +References +---------- + +The logging system is very powerful and flexible. + +And frankly, not as clean and Pythonic as it could be... + +So it's pretty tricky to figure out. + +I highly recommend the cookbook to get beyond the basics: + +https://docs.python.org/3/howto/logging-cookbook.html + +Also these: + +http://atlee.ca/blog/posts/diving-into-python-logging.html + +http://victorlin.me/posts/2012/08/26/good-logging-practice-in-python + + + + + + diff --git a/_sources/metaclasses.rst.txt b/_sources/metaclasses.rst.txt new file mode 100644 index 0000000..342850e --- /dev/null +++ b/_sources/metaclasses.rst.txt @@ -0,0 +1,369 @@ +.. _metaclasses: + +########### +Metaclasses +########### + +adapted from work by Joseph Sheedy + +A class is just an object +========================= + +.. rst-class:: left + + A class is a first-class object: + + Can be created at runtime + + Passed as a parameter + + Returned from a function + + Assigned to a variable + + +Example +------- + +:: + + >>> def create_a_class(**kw): + ... return type('CoolClass', (object,), dict(**kw)) + ... + >>> cool_class = create_a_class(foo='nice', bar='sweet') + >>> cool_class + + >>> cool_object = cool_class() + >>> cool_object + <__main__.CoolClass object at 0x10224e208> + >>> cool_object.foo + 'nice' + >>> cool_object.bar + 'sweet' + + +Equivalent to: +-------------- + + +:: + + class CoolClass(object): + foo = 'nice' + bar = 'sweet' + + +But it was created at runtime, returned from a function and assigned to a variable. + + +http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example + + +More on Classes +--------------- + + Objects get created from classes. So what is the class of a class? + + The class of Class is a metaclass + + The metaclass can be used to dynamically create a class + + The metaclass, being a class, also has a metaclass + + +What is a metaclass? +-------------------- + +- A class is something that makes instances +- A metaclass is something that makes classes +- A metaclass is most commonly used as a class factory +- Metaclasses allow you to do 'extra things' when creating a class, + like registering the new class with some registry, adding methods + dynamically, or even replace the class with something else entirely +- Every object in Python has a metaclass +- The default metaclass is ``type()`` + + +``type()`` +---------- + +With one argument, ``type()`` returns the type of the argument + +With 3 arguments, ``type()`` returns a new class + +.. code-block:: ipython + + type? + Type: type + String Form: + Namespace: Python builtin + Docstring: + type(object) -> the object's type + type(name, bases, dict) -> a new type + + name: string name of the class + bases: tuple of the parent classes + dict: dict containing attribute names and values + + +using type() to build a class +----------------------------- + +The ``class`` keyword is syntactic sugar, we can get by without it by +using type + +.. code-block:: python + + class MyClass(object): + x = 1 + +or + +.. code-block:: python + + MyClass = type('MyClass', (), {'x': 1}) + +(``object`` is automatically a superclass) + + +Adding methods to a class built with ``type()`` +----------------------------------------------- + +Just define a function with the correct signature and add it to the attr +dictionary + +.. code-block:: python + + def my_method(self): + print("called my_method, x = %s" % self.x) + + MyClass = type('MyClass',(), {'x': 1, 'my_method': my_method}) + o = MyClass() + o.my_method() + + +MyClass = type(name, bases, dct) + +- name: name of newly created class +- bases: tuple of class's base classes +- dct: class attribute mapping + + +What type is type? +------------------ + +.. code-block:: ipython + + In [30]: type(type) + Out[30]: type + + +``metaclass`` +--------------- + +Setting a class' metaclass: + +.. code-block:: python + + class Foo(metaclass=MyMetaClass): + pass + + +the class assigned to the ``metaclass`` keyword argument will be used to create the object class ``Foo``. + +If the ``metaclass`` kwarg is not defined, it will use type to create the class. + +Whatever is assigned to ``metaclass`` should be a callable with the +same signature as type() + +**Python2 NOTE:** + +In Python 2, instead of the keyword argument, a special class attribute: ``__metaclass__`` is used: + +.. code-block:: python + + class Foo(object): + __metaclass__ = MyMetaClass + + +Why use metaclasses? +-------------------- + +Useful when creating an API or framework + +Whenever you need to manage object creation for one or more classes + +For example, see ``Examples/metclasses/singleton.py`` + +Or consider the Django ORM: + +.. code-block:: python + + class Person(models.Model): + name = models.CharField(max_length=30) + age = models.IntegerField() + + person = Person(name='bob', age=35) + print person.name + +When the Person class is created, it is dynamically modified to +integrate with the database configured backend. Thus, different +configurations will lead to different class definitions. This is +abstracted from the user of the Model class. + +.. nextslide:: + +Here is the Django Model metaclass: + +https://github.com/django/django/blob/master/django/db/models/base.py#L77 + + +__new__ vs __init__ in Metaclasses +------------------------------------ + + +``__new__`` is used when you want to control the creation of the class (object) + +``__init__`` is used when you want to control the initiation of the class (object) + +``__new__`` and ``__init__`` are both called when the module containing the class is imported for the first time. + +``__call__`` is used when you want to control how a class (object) is called (instantiation) + + +.. nextslide:: + + +.. code-block:: python + + class CoolMeta(type): + def __new__(meta, name, bases, dct): + print('Creating class', name) + return super(CoolMeta, meta).__new__(meta, name, bases, dct) + def __init__(cls, name, bases, dct): + print('Initializing class', name) + super(CoolMeta, cls).__init__(name, bases, dct) + def __call__(cls, *args, **kw): + print('Meta has been called') + return type(cls, *args, **kw) + + class CoolClass(metaclass=CoolMeta): + def __init__(self): + print('And now my CoolClass exists') + + print('Actually instantiating now') + foo = CoolClass() + + +Metaclass example +----------------- + +Consider wanting a metaclass which mangles all attribute names to +provide uppercase and lower case attributes + +.. code-block:: python + + class Foo(metaclass=NameMangler): + x = 1 + + f = Foo() + print(f.X) + print(f.x) + + +NameMangler +----------- + +.. code-block:: python + + class NameMangler(type): + + def __new__(cls, clsname, bases, _dict): + uppercase_attr = {} + for name, val in _dict.items(): + if not name.startswith('__'): + uppercase_attr[name.upper()] = val + uppercase_attr[name] = val + else: + uppercase_attr[name] = val + + return super().__new__(cls, clsname, bases, uppercase_attr) + + + class Foo(metaclass=NameMangler): + x = 1 + + +Exercise: Working with NameMangler +---------------------------------- + +In the repository, find and run ``Examples/metaclasses/mangler.py`` + +Modify the NameMangler metaclass such that setting an attribute f.x also +sets f.xx + +Now create a new metaclass, MangledSingleton, composed of the +NameMangler and Singleton classes in the ``Examples/metaclasses`` directory. + +Assign it to the ``metaclass`` keyword argument of a new class and verify that it works. + +Your code should look like this: + +.. code-block:: python + + class MyClass(metaclass=MangledSingleton) # define this + x = 1 + + o1 = MyClass() + o2 = MyClass() + print(o1.X) + assert id(o1) == id(o2) + + +The Singleton +------------- + +One common use of metaclasses is to create a singleton. There is an example of this called singleton.py in the Examples directory. However, metaclasses are not the only way to create a singleton. It really depends on what you are trying to do with your singleton. + + +http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Singleton.html + +http://stackoverflow.com/questions/6760685/creating-a-singleton-in-python + + +Reference reading +----------------- + +About metaclasses (Python 3): + +.. rst-class:: small + + http://blog.thedigitalcatonline.com/blog/2014/09/01/python-3-oop-part-5-metaclasses + +Python 2 (mostly the same): + +What is a metaclass in Python? + +.. rst-class:: small + + http://stackoverflow.com/a/6581949/747729 + +Python metaclasses by example: + +.. rst-class:: small + + http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example/ + +A Primer on Python Metaclasses: + +.. rst-class:: small + + http://jakevdp.github.io/blog/2012/12/01/a-primer-on-python-metaclasses/ + +And some even more advanced tricks: + +.. rst-class:: small + + http://blog.thedigitalcatonline.com/blog/2014/10/14/decorators-and-metaclasses + + diff --git a/_sources/metaclasses.txt b/_sources/metaclasses.txt new file mode 100644 index 0000000..342850e --- /dev/null +++ b/_sources/metaclasses.txt @@ -0,0 +1,369 @@ +.. _metaclasses: + +########### +Metaclasses +########### + +adapted from work by Joseph Sheedy + +A class is just an object +========================= + +.. rst-class:: left + + A class is a first-class object: + + Can be created at runtime + + Passed as a parameter + + Returned from a function + + Assigned to a variable + + +Example +------- + +:: + + >>> def create_a_class(**kw): + ... return type('CoolClass', (object,), dict(**kw)) + ... + >>> cool_class = create_a_class(foo='nice', bar='sweet') + >>> cool_class + + >>> cool_object = cool_class() + >>> cool_object + <__main__.CoolClass object at 0x10224e208> + >>> cool_object.foo + 'nice' + >>> cool_object.bar + 'sweet' + + +Equivalent to: +-------------- + + +:: + + class CoolClass(object): + foo = 'nice' + bar = 'sweet' + + +But it was created at runtime, returned from a function and assigned to a variable. + + +http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example + + +More on Classes +--------------- + + Objects get created from classes. So what is the class of a class? + + The class of Class is a metaclass + + The metaclass can be used to dynamically create a class + + The metaclass, being a class, also has a metaclass + + +What is a metaclass? +-------------------- + +- A class is something that makes instances +- A metaclass is something that makes classes +- A metaclass is most commonly used as a class factory +- Metaclasses allow you to do 'extra things' when creating a class, + like registering the new class with some registry, adding methods + dynamically, or even replace the class with something else entirely +- Every object in Python has a metaclass +- The default metaclass is ``type()`` + + +``type()`` +---------- + +With one argument, ``type()`` returns the type of the argument + +With 3 arguments, ``type()`` returns a new class + +.. code-block:: ipython + + type? + Type: type + String Form: + Namespace: Python builtin + Docstring: + type(object) -> the object's type + type(name, bases, dict) -> a new type + + name: string name of the class + bases: tuple of the parent classes + dict: dict containing attribute names and values + + +using type() to build a class +----------------------------- + +The ``class`` keyword is syntactic sugar, we can get by without it by +using type + +.. code-block:: python + + class MyClass(object): + x = 1 + +or + +.. code-block:: python + + MyClass = type('MyClass', (), {'x': 1}) + +(``object`` is automatically a superclass) + + +Adding methods to a class built with ``type()`` +----------------------------------------------- + +Just define a function with the correct signature and add it to the attr +dictionary + +.. code-block:: python + + def my_method(self): + print("called my_method, x = %s" % self.x) + + MyClass = type('MyClass',(), {'x': 1, 'my_method': my_method}) + o = MyClass() + o.my_method() + + +MyClass = type(name, bases, dct) + +- name: name of newly created class +- bases: tuple of class's base classes +- dct: class attribute mapping + + +What type is type? +------------------ + +.. code-block:: ipython + + In [30]: type(type) + Out[30]: type + + +``metaclass`` +--------------- + +Setting a class' metaclass: + +.. code-block:: python + + class Foo(metaclass=MyMetaClass): + pass + + +the class assigned to the ``metaclass`` keyword argument will be used to create the object class ``Foo``. + +If the ``metaclass`` kwarg is not defined, it will use type to create the class. + +Whatever is assigned to ``metaclass`` should be a callable with the +same signature as type() + +**Python2 NOTE:** + +In Python 2, instead of the keyword argument, a special class attribute: ``__metaclass__`` is used: + +.. code-block:: python + + class Foo(object): + __metaclass__ = MyMetaClass + + +Why use metaclasses? +-------------------- + +Useful when creating an API or framework + +Whenever you need to manage object creation for one or more classes + +For example, see ``Examples/metclasses/singleton.py`` + +Or consider the Django ORM: + +.. code-block:: python + + class Person(models.Model): + name = models.CharField(max_length=30) + age = models.IntegerField() + + person = Person(name='bob', age=35) + print person.name + +When the Person class is created, it is dynamically modified to +integrate with the database configured backend. Thus, different +configurations will lead to different class definitions. This is +abstracted from the user of the Model class. + +.. nextslide:: + +Here is the Django Model metaclass: + +https://github.com/django/django/blob/master/django/db/models/base.py#L77 + + +__new__ vs __init__ in Metaclasses +------------------------------------ + + +``__new__`` is used when you want to control the creation of the class (object) + +``__init__`` is used when you want to control the initiation of the class (object) + +``__new__`` and ``__init__`` are both called when the module containing the class is imported for the first time. + +``__call__`` is used when you want to control how a class (object) is called (instantiation) + + +.. nextslide:: + + +.. code-block:: python + + class CoolMeta(type): + def __new__(meta, name, bases, dct): + print('Creating class', name) + return super(CoolMeta, meta).__new__(meta, name, bases, dct) + def __init__(cls, name, bases, dct): + print('Initializing class', name) + super(CoolMeta, cls).__init__(name, bases, dct) + def __call__(cls, *args, **kw): + print('Meta has been called') + return type(cls, *args, **kw) + + class CoolClass(metaclass=CoolMeta): + def __init__(self): + print('And now my CoolClass exists') + + print('Actually instantiating now') + foo = CoolClass() + + +Metaclass example +----------------- + +Consider wanting a metaclass which mangles all attribute names to +provide uppercase and lower case attributes + +.. code-block:: python + + class Foo(metaclass=NameMangler): + x = 1 + + f = Foo() + print(f.X) + print(f.x) + + +NameMangler +----------- + +.. code-block:: python + + class NameMangler(type): + + def __new__(cls, clsname, bases, _dict): + uppercase_attr = {} + for name, val in _dict.items(): + if not name.startswith('__'): + uppercase_attr[name.upper()] = val + uppercase_attr[name] = val + else: + uppercase_attr[name] = val + + return super().__new__(cls, clsname, bases, uppercase_attr) + + + class Foo(metaclass=NameMangler): + x = 1 + + +Exercise: Working with NameMangler +---------------------------------- + +In the repository, find and run ``Examples/metaclasses/mangler.py`` + +Modify the NameMangler metaclass such that setting an attribute f.x also +sets f.xx + +Now create a new metaclass, MangledSingleton, composed of the +NameMangler and Singleton classes in the ``Examples/metaclasses`` directory. + +Assign it to the ``metaclass`` keyword argument of a new class and verify that it works. + +Your code should look like this: + +.. code-block:: python + + class MyClass(metaclass=MangledSingleton) # define this + x = 1 + + o1 = MyClass() + o2 = MyClass() + print(o1.X) + assert id(o1) == id(o2) + + +The Singleton +------------- + +One common use of metaclasses is to create a singleton. There is an example of this called singleton.py in the Examples directory. However, metaclasses are not the only way to create a singleton. It really depends on what you are trying to do with your singleton. + + +http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Singleton.html + +http://stackoverflow.com/questions/6760685/creating-a-singleton-in-python + + +Reference reading +----------------- + +About metaclasses (Python 3): + +.. rst-class:: small + + http://blog.thedigitalcatonline.com/blog/2014/09/01/python-3-oop-part-5-metaclasses + +Python 2 (mostly the same): + +What is a metaclass in Python? + +.. rst-class:: small + + http://stackoverflow.com/a/6581949/747729 + +Python metaclasses by example: + +.. rst-class:: small + + http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example/ + +A Primer on Python Metaclasses: + +.. rst-class:: small + + http://jakevdp.github.io/blog/2012/12/01/a-primer-on-python-metaclasses/ + +And some even more advanced tricks: + +.. rst-class:: small + + http://blog.thedigitalcatonline.com/blog/2014/10/14/decorators-and-metaclasses + + diff --git a/_sources/nosql.rst.txt b/_sources/nosql.rst.txt new file mode 100644 index 0000000..c7189c0 --- /dev/null +++ b/_sources/nosql.rst.txt @@ -0,0 +1,362 @@ +.. _nosql: + + +################ +No SQL Databases +################ + +"No SQL"? +========= + +An SQL system is not always the best way to store your data. + + + +What is a Database? +------------------- + +A database is an organized collection of data. The data are typically organized to model relevant aspects of reality in a way that supports processes requiring this information. + +Usually a way to persist and recover that organized data. + +These days, when you say "Database" almost everyone thinks "Relational Database", and SQL is the standard way to do that. + +SQL RDBMS systems are robust, powerful, scalable and very well optimized. + +But: They require you to adapt the relational data model. + + +Non RDBMS options: +------------------ + +A key buzzword these days is "NOSQL" + +OK: They don't use SQL -- but what are they? + +Not one thing, but key features are mostly shared: + +* "schema less" + - Document oriented + +* More direct mapping to an object model. + +* Highly Scalable + - Easier to distribute / parallelize + + +Database Schema +--------------- + + +**Schema:** + +A database schema is the organization of data, and description of how a database is constructed: Divided into database tables, and relationships: foreign keys, etc... + +Includes what fields in what tables, what data types each field is, normalization of shared data, etc. + +This requires a fair bit of work up-front, and can be hard to adapt as the system requirements change. + +It also can be a bit ugly to map your programming data model to the schema. + + +Schemaless +---------- + +Schemaless databases generally follow a "document model". + +Each entry in the database is a "document": + +* essentially an arbitrary collection of fields. +* often looks like a Python dict. + +Not every entry has to have exactly the same structure. + +Maps well to dynamic programming languages. + +Adapts well as the system changes. + + +NoSQL in Python: +---------------- + +Three Categories: + + +1. Simple key-value object store: +--------------------------------- + +- shelve +- anydbm +- Can store (almost) any Python object +- Only provides storage and retrieval + + + +2. External NoSQL system: +------------------------- + +* Python bindings to external NoSQL system + +* Doesn't store full Python objects + +* Generally stores arbitrary collections of data (but not classes) + +* Can be simple key-value stores + + - Redis, etc... + +* Or a more full featured document database: + + - in-database searching, etc. + - mongoDB, etc... + +* Or a Map/Reduce engine: + + - Hadoop + + +3. Python object database: +-------------------------- + +* Stores and retrieves arbitrary Python objects. + + - Don't need to adapt your data model at all. + +* ZODB is the only robust maintained system (I know of) + +* ZODB is as close a match as you can get between the store and your code -- references and everything. + +http://blog.startifact.com/posts/older/a-misconception-about-the-zodb.html + +Why a DB at all? +---------------- + +Reasons to use a database: + +- Need to persist the data your application uses + +- May need to store more data than you can hold in memory + +- May need to have multiple apps (or multiple instances) accessing the same data + +- May need to scale -- have the DB running on a separate server(s) + +- May need to access data from systems written in different languages. + + +ZODB +---- + +The Zope Object Data Base: A native object database for Python + +* Transparent persistence for Python objects + +* Full ACID-compatible transaction support (including savepoints) + +* History/undo ability + +* Efficient support for binary large objects (BLOBs) + +* Pluggable storages + +* Scalable architecture + +http://http://www.zodb.org/ + +MongoDB +-------- + +Document-Oriented Storage + * JSON-style documents with dynamic schemas offer simplicity and power. + +Full Index Support + * Index on any attribute, just like you're used to. + +Replication & High Availability + * Mirror across LANs and WANs for scale and peace of mind. + +Auto-Sharding + * Scale horizontally without compromising functionality. + +Querying + * Rich, document-based queries. + +https://www.mongodb.org/ + + +Other Options to Consider: +-------------------------- + + +Redis: Advanced, Scalable key-value store. + - http://redis.io/ + +Riak: High availablity/scalablity (but not so good for small) + - http://docs.basho.com/riak/latest/dev/taste-of-riak/python/ + +HyperDex: "Next generation key-value store" + - http://hyperdex.org/ + +Apache Cassandra: A more schema-based NoSQL solution + - http://pycassa.github.io/pycassa/ + + +A Data Model +------------ + +An Address Book with a not quite trivial data model. + +There are people:: + + self.first_name + self.last_name + self.middle_name + self.cell_phone + self.email + +There are households:: + + self.name + self.people + self.address + self.phone + +(similarly businesses) + +``address_book_model.py`` + +Using ZODB +---------- + +ZODB stored Python objects. + +To make an object persistent:: + + import persistent + + class Something(persistent.Persistent): + def __init__(self): + self.a_field = '' + self.another_field '' + +When a change is made to the fields, the DB will keep it updated. + +``Examples/nosql/address_book_zodb.py`` + +Mutable Attributes +------------------- + +``Something.this = that`` will trigger a DB action + +But: + +``Something.a_list.append`` will not trigger anything. + +The DB doesn't know that that the list has been altered. + +Solution: + +``self.a_list = PersistentList()`` + +(also ``PersistantDict()`` ) + +(or write getters and setters...) + +``Examples/nosql/address_book_zodb.py`` + +mongoDB +------- + +Essentially a key-value store, but the values are JSON-like objects. (Actually BSON (binary JSON) ) + +So you can store any object that can look like JSON: + * dicts + * lists + * numbers + * strings + * richer than JSON. + +mongoDB and Python +------------------ + +mongoDB is written in C++ -- can be accessed by various language drivers. + +( http://docs.mongodb.org/manual/applications/drivers/ ) + +For Python: ``PyMongo`` + +http://api.mongodb.org/python/current/tutorial.html + +(``pip install pymongo`` - binary wheels available!) + +There are also various tools for integrating mongoDB with Frameworks: + +* Django MongoDB Engine +* mongodb_beaker +* MongoLog: Python logging handler +* Flask-PyMongo +* others... + +Getting started with mongoDB +---------------------------- + +mongoDB is separate program. Installers here: + +http://www.mongodb.org/downloads + +Simple copy and paste install or use homebrew (at least on OS-X) + +(drop the files from ``bin`` into ``usr/local/bin`` or similar) + +Create a dir for the database: + +``$ mkdir mongo_data`` + +And start it up: + +``$ mongod --dbpath=mongo_data/`` + +Creating a DB: +-------------- + +:: + + # create the DB + from pymongo import MongoClient + + client = MongoClient('localhost', 27017) + store = client.store_name # creates a Database + people = store.people # creates a collection + +mongo will link to the given database ans collection, or create new ones if they don't exist. + +Adding some stuff:: + + people.insert({'first_name': 'Fred', + 'last_name': 'Jones'}) + +Pulling Stuff Out: +------------------ + +And reading it back:: + + In [16]: people.find_one({'first_name':"Fred"}) + Out[16]: + {u'_id': ObjectId('534dcdcb5c84d28b596ad15e'), + u'first_name': u'Fred', + u'last_name': u'Jones'} + +Note that it adds an ObjectID for you. + +``Examples/nosql/address_book_mongo.py`` + + + + + + + + + + + diff --git a/_sources/nosql.txt b/_sources/nosql.txt new file mode 100644 index 0000000..c7189c0 --- /dev/null +++ b/_sources/nosql.txt @@ -0,0 +1,362 @@ +.. _nosql: + + +################ +No SQL Databases +################ + +"No SQL"? +========= + +An SQL system is not always the best way to store your data. + + + +What is a Database? +------------------- + +A database is an organized collection of data. The data are typically organized to model relevant aspects of reality in a way that supports processes requiring this information. + +Usually a way to persist and recover that organized data. + +These days, when you say "Database" almost everyone thinks "Relational Database", and SQL is the standard way to do that. + +SQL RDBMS systems are robust, powerful, scalable and very well optimized. + +But: They require you to adapt the relational data model. + + +Non RDBMS options: +------------------ + +A key buzzword these days is "NOSQL" + +OK: They don't use SQL -- but what are they? + +Not one thing, but key features are mostly shared: + +* "schema less" + - Document oriented + +* More direct mapping to an object model. + +* Highly Scalable + - Easier to distribute / parallelize + + +Database Schema +--------------- + + +**Schema:** + +A database schema is the organization of data, and description of how a database is constructed: Divided into database tables, and relationships: foreign keys, etc... + +Includes what fields in what tables, what data types each field is, normalization of shared data, etc. + +This requires a fair bit of work up-front, and can be hard to adapt as the system requirements change. + +It also can be a bit ugly to map your programming data model to the schema. + + +Schemaless +---------- + +Schemaless databases generally follow a "document model". + +Each entry in the database is a "document": + +* essentially an arbitrary collection of fields. +* often looks like a Python dict. + +Not every entry has to have exactly the same structure. + +Maps well to dynamic programming languages. + +Adapts well as the system changes. + + +NoSQL in Python: +---------------- + +Three Categories: + + +1. Simple key-value object store: +--------------------------------- + +- shelve +- anydbm +- Can store (almost) any Python object +- Only provides storage and retrieval + + + +2. External NoSQL system: +------------------------- + +* Python bindings to external NoSQL system + +* Doesn't store full Python objects + +* Generally stores arbitrary collections of data (but not classes) + +* Can be simple key-value stores + + - Redis, etc... + +* Or a more full featured document database: + + - in-database searching, etc. + - mongoDB, etc... + +* Or a Map/Reduce engine: + + - Hadoop + + +3. Python object database: +-------------------------- + +* Stores and retrieves arbitrary Python objects. + + - Don't need to adapt your data model at all. + +* ZODB is the only robust maintained system (I know of) + +* ZODB is as close a match as you can get between the store and your code -- references and everything. + +http://blog.startifact.com/posts/older/a-misconception-about-the-zodb.html + +Why a DB at all? +---------------- + +Reasons to use a database: + +- Need to persist the data your application uses + +- May need to store more data than you can hold in memory + +- May need to have multiple apps (or multiple instances) accessing the same data + +- May need to scale -- have the DB running on a separate server(s) + +- May need to access data from systems written in different languages. + + +ZODB +---- + +The Zope Object Data Base: A native object database for Python + +* Transparent persistence for Python objects + +* Full ACID-compatible transaction support (including savepoints) + +* History/undo ability + +* Efficient support for binary large objects (BLOBs) + +* Pluggable storages + +* Scalable architecture + +http://http://www.zodb.org/ + +MongoDB +-------- + +Document-Oriented Storage + * JSON-style documents with dynamic schemas offer simplicity and power. + +Full Index Support + * Index on any attribute, just like you're used to. + +Replication & High Availability + * Mirror across LANs and WANs for scale and peace of mind. + +Auto-Sharding + * Scale horizontally without compromising functionality. + +Querying + * Rich, document-based queries. + +https://www.mongodb.org/ + + +Other Options to Consider: +-------------------------- + + +Redis: Advanced, Scalable key-value store. + - http://redis.io/ + +Riak: High availablity/scalablity (but not so good for small) + - http://docs.basho.com/riak/latest/dev/taste-of-riak/python/ + +HyperDex: "Next generation key-value store" + - http://hyperdex.org/ + +Apache Cassandra: A more schema-based NoSQL solution + - http://pycassa.github.io/pycassa/ + + +A Data Model +------------ + +An Address Book with a not quite trivial data model. + +There are people:: + + self.first_name + self.last_name + self.middle_name + self.cell_phone + self.email + +There are households:: + + self.name + self.people + self.address + self.phone + +(similarly businesses) + +``address_book_model.py`` + +Using ZODB +---------- + +ZODB stored Python objects. + +To make an object persistent:: + + import persistent + + class Something(persistent.Persistent): + def __init__(self): + self.a_field = '' + self.another_field '' + +When a change is made to the fields, the DB will keep it updated. + +``Examples/nosql/address_book_zodb.py`` + +Mutable Attributes +------------------- + +``Something.this = that`` will trigger a DB action + +But: + +``Something.a_list.append`` will not trigger anything. + +The DB doesn't know that that the list has been altered. + +Solution: + +``self.a_list = PersistentList()`` + +(also ``PersistantDict()`` ) + +(or write getters and setters...) + +``Examples/nosql/address_book_zodb.py`` + +mongoDB +------- + +Essentially a key-value store, but the values are JSON-like objects. (Actually BSON (binary JSON) ) + +So you can store any object that can look like JSON: + * dicts + * lists + * numbers + * strings + * richer than JSON. + +mongoDB and Python +------------------ + +mongoDB is written in C++ -- can be accessed by various language drivers. + +( http://docs.mongodb.org/manual/applications/drivers/ ) + +For Python: ``PyMongo`` + +http://api.mongodb.org/python/current/tutorial.html + +(``pip install pymongo`` - binary wheels available!) + +There are also various tools for integrating mongoDB with Frameworks: + +* Django MongoDB Engine +* mongodb_beaker +* MongoLog: Python logging handler +* Flask-PyMongo +* others... + +Getting started with mongoDB +---------------------------- + +mongoDB is separate program. Installers here: + +http://www.mongodb.org/downloads + +Simple copy and paste install or use homebrew (at least on OS-X) + +(drop the files from ``bin`` into ``usr/local/bin`` or similar) + +Create a dir for the database: + +``$ mkdir mongo_data`` + +And start it up: + +``$ mongod --dbpath=mongo_data/`` + +Creating a DB: +-------------- + +:: + + # create the DB + from pymongo import MongoClient + + client = MongoClient('localhost', 27017) + store = client.store_name # creates a Database + people = store.people # creates a collection + +mongo will link to the given database ans collection, or create new ones if they don't exist. + +Adding some stuff:: + + people.insert({'first_name': 'Fred', + 'last_name': 'Jones'}) + +Pulling Stuff Out: +------------------ + +And reading it back:: + + In [16]: people.find_one({'first_name':"Fred"}) + Out[16]: + {u'_id': ObjectId('534dcdcb5c84d28b596ad15e'), + u'first_name': u'Fred', + u'last_name': u'Jones'} + +Note that it adds an ObjectID for you. + +``Examples/nosql/address_book_mongo.py`` + + + + + + + + + + + diff --git a/_sources/notes/BuildingExtensions.rst.txt b/_sources/notes/BuildingExtensions.rst.txt new file mode 100644 index 0000000..2204fa4 --- /dev/null +++ b/_sources/notes/BuildingExtensions.rst.txt @@ -0,0 +1,91 @@ +.. _building_extensions: + +============================== +Building Extensions to Python +============================== + +To build extensions to Python, you need a C (and maybe C++) compiler that is compatible with your Python build. + +Linux +====== + +Linux is pretty straightforward -- most systems are set up to build stuff out of the box. If not, you'll need to install the development tools. In Ubuntu, that's:: + + sudo apt-get install build-essential + +Other systems will have something similar. + +To make sure it's working, you can make sure gcc is there with:: + + gcc --version + +at the command line, and make sure you get something! + +To compile Python extensions, you'll need the some extra files that come with python. Most distros have an extra package, called something like "python-dev" that you'll need to get the headers, etc. required to build extensions:: + + apt-get install python-dev + +That should do it. + +Windows: +======== + +The easiest way is to use the same MS compiler as the ``python.org`` build. + +With Python2.7, that's MS Visual Studio 2008. + +MS has recently started distributing a version of the compiler set up specifically to build Python extensions: + +http://www.microsoft.com/en-us/download/details.aspx?id=44266 + +The trick is that this installs things a bit differently than distutils expects. But newer versions of setuptools do support it. So make sure you have an updated setuptools:: + + pip install --upgrade setuptools + +and that the extension you are trying to compile is using setuptools, rather than raw distutils. + +OS-X: +===== + +Apple provides a free compiler, as part of the "XCode" IDE. +You can get it for free from the App store, but be prepared, it is a big download! + +Just the command line tools: + + +It looks like you can just get the command line tools by running this command on the command line:: + + xcode-select --install + +see: http://osxdaily.com/2014/02/12/install-command-line-tools-mac-os-x/ + +for more detail. + +If you opt for the full package, after you install XCode, you STILL need to install the command line tools. To install these tools, go to the Downloads tab within the Xcode Preferences menu and click "Install" next to the Command Line Tools entry. + +(https://developer.apple.com/support/xcode/) + +Macports / Homebrew +-------------------- + +If you installed python with Macports or Homebrew, it should be all set up to compile extensions. If not, then you may need to install a python-dev package, or something like that. + +Testing if it works: +====================== + +In the ``Examples\week-08 class``, you'll find a number of samples. + +Go to the: ``Examples\week-08-extensions/c-api`` dir. + +type:: + + python setup.py build_ext --inplace + +This should spew out a bunch of stuff while it builds the extension, then hopefully finish without an error. On Linux and the Mac, you should get an add.so file, on Windows, an add.pyd file. + +Try:: + + py.test + +and hopefully 3 tests will pass. + diff --git a/_sources/notes/BuildingExtensions.txt b/_sources/notes/BuildingExtensions.txt new file mode 100644 index 0000000..2204fa4 --- /dev/null +++ b/_sources/notes/BuildingExtensions.txt @@ -0,0 +1,91 @@ +.. _building_extensions: + +============================== +Building Extensions to Python +============================== + +To build extensions to Python, you need a C (and maybe C++) compiler that is compatible with your Python build. + +Linux +====== + +Linux is pretty straightforward -- most systems are set up to build stuff out of the box. If not, you'll need to install the development tools. In Ubuntu, that's:: + + sudo apt-get install build-essential + +Other systems will have something similar. + +To make sure it's working, you can make sure gcc is there with:: + + gcc --version + +at the command line, and make sure you get something! + +To compile Python extensions, you'll need the some extra files that come with python. Most distros have an extra package, called something like "python-dev" that you'll need to get the headers, etc. required to build extensions:: + + apt-get install python-dev + +That should do it. + +Windows: +======== + +The easiest way is to use the same MS compiler as the ``python.org`` build. + +With Python2.7, that's MS Visual Studio 2008. + +MS has recently started distributing a version of the compiler set up specifically to build Python extensions: + +http://www.microsoft.com/en-us/download/details.aspx?id=44266 + +The trick is that this installs things a bit differently than distutils expects. But newer versions of setuptools do support it. So make sure you have an updated setuptools:: + + pip install --upgrade setuptools + +and that the extension you are trying to compile is using setuptools, rather than raw distutils. + +OS-X: +===== + +Apple provides a free compiler, as part of the "XCode" IDE. +You can get it for free from the App store, but be prepared, it is a big download! + +Just the command line tools: + + +It looks like you can just get the command line tools by running this command on the command line:: + + xcode-select --install + +see: http://osxdaily.com/2014/02/12/install-command-line-tools-mac-os-x/ + +for more detail. + +If you opt for the full package, after you install XCode, you STILL need to install the command line tools. To install these tools, go to the Downloads tab within the Xcode Preferences menu and click "Install" next to the Command Line Tools entry. + +(https://developer.apple.com/support/xcode/) + +Macports / Homebrew +-------------------- + +If you installed python with Macports or Homebrew, it should be all set up to compile extensions. If not, then you may need to install a python-dev package, or something like that. + +Testing if it works: +====================== + +In the ``Examples\week-08 class``, you'll find a number of samples. + +Go to the: ``Examples\week-08-extensions/c-api`` dir. + +type:: + + python setup.py build_ext --inplace + +This should spew out a bunch of stuff while it builds the extension, then hopefully finish without an error. On Linux and the Mac, you should get an add.so file, on Windows, an add.pyd file. + +Try:: + + py.test + +and hopefully 3 tests will pass. + diff --git a/_sources/notes/InstallingTheScipyStack.rst.txt b/_sources/notes/InstallingTheScipyStack.rst.txt new file mode 100644 index 0000000..39f11ec --- /dev/null +++ b/_sources/notes/InstallingTheScipyStack.rst.txt @@ -0,0 +1,115 @@ +.. _installing_scipy: + +========================== +Installing The SciPy Stack +========================== + +There are lot of notes online about this, but this is summary of my recommendations. + +To build extensions to Python, you need a C (and maybe C++) compiler that is compatible with your Python build. + +Scipy +===== + +"SciPy" is really two things: + +1) A particular package of modules useful for computational/scientific computing: what you get when you do:: + +.. code-block:: python + + import scipy + +2) A community of folks doing scientific computing, and all the packages that are developed and used -- **A LOT** + +But there are a few packages that what most people will want, regardless of domain. To help calrify all this the "scipy stack" was officially declared: + +http://www.scipy.org/stackspec.html + +What's in the scipy stack? +-------------------------- + +* Python (http://www.python.org) +* NumPy (http://www.numpy.org) +* SciPy library (http://www.scipy.org) +* Matplotlib (http://matplotlib.org/) +* IPython (http://ipython.org/) +* pandas (http://pandas.pydata.org/) + +* nose (https://nose.readthedocs.org) (for testing) +* Sympy (http://sympy.org/) (symbolic algebra) + +Installing the Stack +--------------------- + +Many of these packages are compiled code, some of it in Fortran -- so it can be quite a pain to install from source. There are a number of ways to find pre-complied packages, some of which are outlined here: + +http://www.scipy.org/install.html + +But here are my thoughts: + +* If you are doing a lot of scientific computing, and not much else: + + - Install the Anaconda distribution, and use it to install the other packages you'll need: + + https://store.continuum.io/cshop/anaconda/ + + There are free versions for Windows, Linux and OS-X. + +If you want to just add numpy, etc. to the python.org install + +* Windows and OS-X: + + - try ``pip install``: I _think_ that there are binaries up on pypi for Windows and OS-X. So + +:: + + pip install numpy + pip install scipy + pip install ipython[all] + pip install pandas + +* Windows: + +Another good option is Chris Gohlke's packages, which you can find here: + +http://www.lfd.uci.edu/~gohlke/pythonlibs/ + +These are binary wheels, so once downloaded, they can be installed with pip:: + + pip install numpy‑1.9.2+mkl‑cp26‑none‑win_amd64.whl + +Note that he has both 32 bit and 64 bit packages there, install the ones that match your python. Despite the name, the "amd64" packages will work with Intel processors, too. + + +* Linux + +These may be available from your system package manager (yum, apt-get) -- try that first. If not, then you'll need the compilers set up right, which may require some additional system packages first. + +Checking if it works: +--------------------- + +Once installed, you should be able do the following imports:: + + import numpy + import scipy + import matplotlib + import pandas + +And be able to run the ipython notebook from the command line:: + + $ ipython notebook + +(that should start up your browser, pointed back at the notebook running in the dir you started it up in) + + + + + + + + + + + + + diff --git a/_sources/notes/InstallingTheScipyStack.txt b/_sources/notes/InstallingTheScipyStack.txt new file mode 100644 index 0000000..39f11ec --- /dev/null +++ b/_sources/notes/InstallingTheScipyStack.txt @@ -0,0 +1,115 @@ +.. _installing_scipy: + +========================== +Installing The SciPy Stack +========================== + +There are lot of notes online about this, but this is summary of my recommendations. + +To build extensions to Python, you need a C (and maybe C++) compiler that is compatible with your Python build. + +Scipy +===== + +"SciPy" is really two things: + +1) A particular package of modules useful for computational/scientific computing: what you get when you do:: + +.. code-block:: python + + import scipy + +2) A community of folks doing scientific computing, and all the packages that are developed and used -- **A LOT** + +But there are a few packages that what most people will want, regardless of domain. To help calrify all this the "scipy stack" was officially declared: + +http://www.scipy.org/stackspec.html + +What's in the scipy stack? +-------------------------- + +* Python (http://www.python.org) +* NumPy (http://www.numpy.org) +* SciPy library (http://www.scipy.org) +* Matplotlib (http://matplotlib.org/) +* IPython (http://ipython.org/) +* pandas (http://pandas.pydata.org/) + +* nose (https://nose.readthedocs.org) (for testing) +* Sympy (http://sympy.org/) (symbolic algebra) + +Installing the Stack +--------------------- + +Many of these packages are compiled code, some of it in Fortran -- so it can be quite a pain to install from source. There are a number of ways to find pre-complied packages, some of which are outlined here: + +http://www.scipy.org/install.html + +But here are my thoughts: + +* If you are doing a lot of scientific computing, and not much else: + + - Install the Anaconda distribution, and use it to install the other packages you'll need: + + https://store.continuum.io/cshop/anaconda/ + + There are free versions for Windows, Linux and OS-X. + +If you want to just add numpy, etc. to the python.org install + +* Windows and OS-X: + + - try ``pip install``: I _think_ that there are binaries up on pypi for Windows and OS-X. So + +:: + + pip install numpy + pip install scipy + pip install ipython[all] + pip install pandas + +* Windows: + +Another good option is Chris Gohlke's packages, which you can find here: + +http://www.lfd.uci.edu/~gohlke/pythonlibs/ + +These are binary wheels, so once downloaded, they can be installed with pip:: + + pip install numpy‑1.9.2+mkl‑cp26‑none‑win_amd64.whl + +Note that he has both 32 bit and 64 bit packages there, install the ones that match your python. Despite the name, the "amd64" packages will work with Intel processors, too. + + +* Linux + +These may be available from your system package manager (yum, apt-get) -- try that first. If not, then you'll need the compilers set up right, which may require some additional system packages first. + +Checking if it works: +--------------------- + +Once installed, you should be able do the following imports:: + + import numpy + import scipy + import matplotlib + import pandas + +And be able to run the ipython notebook from the command line:: + + $ ipython notebook + +(that should start up your browser, pointed back at the notebook running in the dir you started it up in) + + + + + + + + + + + + + diff --git a/_sources/notes/Installing_wxPython.rst.txt b/_sources/notes/Installing_wxPython.rst.txt new file mode 100644 index 0000000..9339865 --- /dev/null +++ b/_sources/notes/Installing_wxPython.rst.txt @@ -0,0 +1,184 @@ +.. _installing_wxpython: + +=================== +Installing wxPython +=================== + +As of this writing (Spring 2016), wxPython is in a state of Flux. + +This makes it a bit harder to figure out how to get it running. + +Hopefully, these notes will help. + +Phoenix +======= + +.. rst-class:: left + + There are now two versions of wxPython available: + + * "classic" + * "Phoenix": + + http://wiki.wxpython.org/ProjectPhoenix (information) + + and + + https://github.com/wxWidgets/Phoenix (source) + + "Phoenix" is a refactor of the wxPython code, and currently in a pre-release state. + + But it's the only version that works with py3, and it's pretty solid at this point, at least for the core functionality. + +Installing Phoenix +------------------ + +While not officially released, there are regular "snapshot" builds made available for Windows and OS-X: + +http://wxpython.org/Phoenix/snapshot-builds/ + +These are "binary wheels" -- i.e. complete, ready to install by pip packages + +You can either: + +Find the one you need (not so easy!), download it, and and then:: + + python -m pip install wxPython_Phoenix-3.0.3.dev2022+b85bcd3-cp35-cp35m-macosx_10_6_intel.whl + +or: + +point pip to that location:: + + python -m pip install --no-index --find-links=http://wxpython.org/Phoenix/snapshot-builds/ --trusted-host wxpython.org wxPython_Phoenix + +pip should find the one that matches your OS and Python version. + +(and yes, it took me a while to figure out all those flags) + +Once you've done that (hopefully without errors) you should be able to do:: + + import wx + +in Python without error. + +Linux +----- + +There are no pre-built Linux wheels available (there are way too many flavors of Linux to do that). And as it's "preview" version, none of the Linux distros provide packages. So you will need to build from source. The best way to do that is to clone the repo from gitHub, and follow the directions here: + +https://github.com/wxWidgets/Phoenix/blob/master/README.rst#how-to-build-phoenix + +I don't have a Linux system to test on, so I have no idea how straightforward this is :-( + +There was a tutorial posted recently to the wxPython mailing list: + +http://wxpython-users.1045709.n5.nabble.com/Problem-when-Installing-wxPython-Phoenix-td5725474.html#a5725492 + +I've re-posted it here: + + +How to install wxPython Phoenix on Ubuntu +========================================= + +From Damien Ruiz, on the wxpython-users list. + +About +----- + +Last update : Monday the 21st, January 2016. + +This tutorial deals with wxPython Phoenix installation on Ubuntu. + +I am using Ubuntu 12.04 LTS - 64 bits. + +Note: + +Ubuntu 12.04 LTS comes with Python 2.7.3. You might have to make sure +that Python 3 is installed on your computer: + +- Type ``python3`` in the console to run the python interpreter. + You will also see the current version. + +If you don't have python3 -- you may need to install it. Here is Ubuntu's Python info page: + +https://wiki.ubuntu.com/Python + + +--- Let's go --- +---------------- + +1. Install some modules: + + - dpkg-dev + - build-essential + - python3.5-dev # use appropriate Python version + - libwebkitgtk-dev + - libjpeg-dev + - libtiff-dev + - libgtk2.0-dev + - libsdl1.2-dev + - libgstreamer-plugins-base0.10-dev + - libnotify-dev + - freeglut3 + - freeglut3-dev + + +2. Go to: + +http://wxpython.org/Phoenix/snapshot-builds/ and download the latest .tar.gz + +Example: ``wxPython_Phoenix-3.0.3-dev1836+f764b32.tar.gz`` + +Note: A .tar.gz file is called a tarball. + +3. Untar the tarball:: + + tar -xvzf wxPython_Phoenix-3.0.3-dev1836+f764b32.tar.gz + + +4. Go into the directory:: + + cd wxPython_Phoenix-3.0.3-dev1836+f764b32.tar.gz + +Note : You can/should use the tab key for auto-completing. + +5. Install: + + sudo python setup.py install + +Note: Your password will be asked to copy files. + + +6. Check if the module works : + + 6.1 Make sure your current folder is your home folder : type cd + + 6.2 Do as follows : + +Example:: + + damien@Ubuntu1204VB:~$ python + Python 3.5.4 (default, Jun 22 2015, 19:33:41) + [GCC 4.6.3] on linux2 + Type "help", "copyright", "credits" or "license" for more information. + >>> import wx + >>> wx.version() + '3.0.3.dev1836+f764b32 gtk2 (phoenix)' + +You should then be able to run the examples in: + +``Examples/wxpython`` + +:: + + python3 CalculatorDemo.py + + +Author: + + Damien Ruiz + + + + + diff --git a/_sources/notes/Installing_wxPython.txt b/_sources/notes/Installing_wxPython.txt new file mode 100644 index 0000000..9339865 --- /dev/null +++ b/_sources/notes/Installing_wxPython.txt @@ -0,0 +1,184 @@ +.. _installing_wxpython: + +=================== +Installing wxPython +=================== + +As of this writing (Spring 2016), wxPython is in a state of Flux. + +This makes it a bit harder to figure out how to get it running. + +Hopefully, these notes will help. + +Phoenix +======= + +.. rst-class:: left + + There are now two versions of wxPython available: + + * "classic" + * "Phoenix": + + http://wiki.wxpython.org/ProjectPhoenix (information) + + and + + https://github.com/wxWidgets/Phoenix (source) + + "Phoenix" is a refactor of the wxPython code, and currently in a pre-release state. + + But it's the only version that works with py3, and it's pretty solid at this point, at least for the core functionality. + +Installing Phoenix +------------------ + +While not officially released, there are regular "snapshot" builds made available for Windows and OS-X: + +http://wxpython.org/Phoenix/snapshot-builds/ + +These are "binary wheels" -- i.e. complete, ready to install by pip packages + +You can either: + +Find the one you need (not so easy!), download it, and and then:: + + python -m pip install wxPython_Phoenix-3.0.3.dev2022+b85bcd3-cp35-cp35m-macosx_10_6_intel.whl + +or: + +point pip to that location:: + + python -m pip install --no-index --find-links=http://wxpython.org/Phoenix/snapshot-builds/ --trusted-host wxpython.org wxPython_Phoenix + +pip should find the one that matches your OS and Python version. + +(and yes, it took me a while to figure out all those flags) + +Once you've done that (hopefully without errors) you should be able to do:: + + import wx + +in Python without error. + +Linux +----- + +There are no pre-built Linux wheels available (there are way too many flavors of Linux to do that). And as it's "preview" version, none of the Linux distros provide packages. So you will need to build from source. The best way to do that is to clone the repo from gitHub, and follow the directions here: + +https://github.com/wxWidgets/Phoenix/blob/master/README.rst#how-to-build-phoenix + +I don't have a Linux system to test on, so I have no idea how straightforward this is :-( + +There was a tutorial posted recently to the wxPython mailing list: + +http://wxpython-users.1045709.n5.nabble.com/Problem-when-Installing-wxPython-Phoenix-td5725474.html#a5725492 + +I've re-posted it here: + + +How to install wxPython Phoenix on Ubuntu +========================================= + +From Damien Ruiz, on the wxpython-users list. + +About +----- + +Last update : Monday the 21st, January 2016. + +This tutorial deals with wxPython Phoenix installation on Ubuntu. + +I am using Ubuntu 12.04 LTS - 64 bits. + +Note: + +Ubuntu 12.04 LTS comes with Python 2.7.3. You might have to make sure +that Python 3 is installed on your computer: + +- Type ``python3`` in the console to run the python interpreter. + You will also see the current version. + +If you don't have python3 -- you may need to install it. Here is Ubuntu's Python info page: + +https://wiki.ubuntu.com/Python + + +--- Let's go --- +---------------- + +1. Install some modules: + + - dpkg-dev + - build-essential + - python3.5-dev # use appropriate Python version + - libwebkitgtk-dev + - libjpeg-dev + - libtiff-dev + - libgtk2.0-dev + - libsdl1.2-dev + - libgstreamer-plugins-base0.10-dev + - libnotify-dev + - freeglut3 + - freeglut3-dev + + +2. Go to: + +http://wxpython.org/Phoenix/snapshot-builds/ and download the latest .tar.gz + +Example: ``wxPython_Phoenix-3.0.3-dev1836+f764b32.tar.gz`` + +Note: A .tar.gz file is called a tarball. + +3. Untar the tarball:: + + tar -xvzf wxPython_Phoenix-3.0.3-dev1836+f764b32.tar.gz + + +4. Go into the directory:: + + cd wxPython_Phoenix-3.0.3-dev1836+f764b32.tar.gz + +Note : You can/should use the tab key for auto-completing. + +5. Install: + + sudo python setup.py install + +Note: Your password will be asked to copy files. + + +6. Check if the module works : + + 6.1 Make sure your current folder is your home folder : type cd + + 6.2 Do as follows : + +Example:: + + damien@Ubuntu1204VB:~$ python + Python 3.5.4 (default, Jun 22 2015, 19:33:41) + [GCC 4.6.3] on linux2 + Type "help", "copyright", "credits" or "license" for more information. + >>> import wx + >>> wx.version() + '3.0.3.dev1836+f764b32 gtk2 (phoenix)' + +You should then be able to run the examples in: + +``Examples/wxpython`` + +:: + + python3 CalculatorDemo.py + + +Author: + + Damien Ruiz + + + + + diff --git a/_sources/notes/Installing_wxPython_demo.rst.txt b/_sources/notes/Installing_wxPython_demo.rst.txt new file mode 100644 index 0000000..b6faaf9 --- /dev/null +++ b/_sources/notes/Installing_wxPython_demo.rst.txt @@ -0,0 +1,31 @@ +.. _installing_wxpython_demo: + +======================== +Installing WXPython Demo +======================== + +We are going to download the source for the whole project from the github page, and then extract the demo part. + +https://github.com/wxWidgets/Phoenix + +You can use git copy to download the repository: + +git clone https://github.com/wxWidgets/Phoenix.git + +Once it is downlaoded, cd into the Phoenix directory. One of the subdirectories is called demo. Move this entire directory to somewhere useful. Here is the command I used: + +:: + + mv demo ~/python_examples/wxpython_demo + +This moves the directory into a better place and gives it a better name. Now you can delete the rest of the Phoenix directory, as you don't need it. + +Useful links: + +blog post about the demo: + +http://www.blog.pythonlibrary.org/2010/10/15/the-wxpython-demo/ + +blog post about converting demo code to production code: + +http://wiki.wxpython.org/Using%20wxPython%20Demo%20Code diff --git a/_sources/notes/Installing_wxPython_demo.txt b/_sources/notes/Installing_wxPython_demo.txt new file mode 100644 index 0000000..b6faaf9 --- /dev/null +++ b/_sources/notes/Installing_wxPython_demo.txt @@ -0,0 +1,31 @@ +.. _installing_wxpython_demo: + +======================== +Installing WXPython Demo +======================== + +We are going to download the source for the whole project from the github page, and then extract the demo part. + +https://github.com/wxWidgets/Phoenix + +You can use git copy to download the repository: + +git clone https://github.com/wxWidgets/Phoenix.git + +Once it is downlaoded, cd into the Phoenix directory. One of the subdirectories is called demo. Move this entire directory to somewhere useful. Here is the command I used: + +:: + + mv demo ~/python_examples/wxpython_demo + +This moves the directory into a better place and gives it a better name. Now you can delete the rest of the Phoenix directory, as you don't need it. + +Useful links: + +blog post about the demo: + +http://www.blog.pythonlibrary.org/2010/10/15/the-wxpython-demo/ + +blog post about converting demo code to production code: + +http://wiki.wxpython.org/Using%20wxPython%20Demo%20Code diff --git a/_sources/numpy.txt b/_sources/numpy.txt new file mode 100644 index 0000000..d09ccf2 --- /dev/null +++ b/_sources/numpy.txt @@ -0,0 +1,307 @@ + +.. numpy slides file, created by + hieroglyph-quickstart on Sun Apr 27 15:13:20 2014. + + +********************** +Intro to Numpy / Scipy +********************** + +- Chris Barker + +(April 29, 2014) + +.. Contents: + +.. .. toctree:: +.. :maxdepth: 2 + + +Scipy +----- + +The scipy "Stack" is a collection of core packages used for scientific / numerical computing. + +http://www.scipy.org/stackspec.html + +Many other domain-specific packages area available: + + Core "stack" is what most people will want, regardless of domain. + +What's in the scipy stack? +-------------------------- + +* Python (http://www.python.org) +* NumPy (http://www.numpy.org) +* SciPy library (http://www.scipy.org) +* Matplotlib (http://matplotlib.org/) +* IPython (http://ipython.org/) + +* nose (https://nose.readthedocs.org) +* pandas (http://pandas.pydata.org/) +* Sympy (http://sympy.org/) + +numpy +----- + +numpy is the core package that the rest of the scipy stack is built on. Mostly what I'll talk about here. + +Not just for lots of numbers! +(but it's great for that!) + +http://www.numpy.org/ + + +An N-Dimensional array object + +A whole pile of tools for operations on/with that object. + + +Why numpy? +---------- + +Classic answer: Lots of numbers + + * Faster + * Less memory + * More data types + +Even if you don't have lot of numbers: + + * N-d array slicing + * Vector operations + * Flexible data types + + +Why numpy? +---------- + +Wrapper for a block of memory: + + * Interfacing with C libs + * PyOpenGL + * GDAL + * NetCDF4 + * Shapely + +Image processing: + + * PIL + * WxImage + * ndimage + + +This Talk +---------- + +There are a lot of tutorials and documentation out there. + +So I'm not going to spend much time on the regualr old "how do you use it" stuff. + +Rather, I'm going to cover a bit about the guts and some advanced issues. + +This is harder to find expainations for -- and will help you understand what's really going on under the hood. + + + +Array Constructors: +------------------- + +From scratch: + * ``ones(), zeros(), empty(), arange(), linspace(), logspace()`` + + ( Default dtype: ``np.float64`` ) + +From sequences: + * ``array(), asarray()`` ( Build from any sequence ) + +From binary data: + * ``fromstring(), frombuffer(), fromfile()`` + +Assorted linear algebra standards: + +``eye(), diag()``, etc. + +demo: ``constructors.ipynb`` + + + +Slicing -- views: +----------------- + +a slice is a "view" on the array -- new object, but shares memory: + +:: + + In [12]: a = np.array((1,2,3,4)) + In [13]: b = a[:] + # for lists -- [:] means copy -- not for arrays! + In [15]: a is b + Out[15]: False + # it's new array, but... + In [16]: b[2] = 5 + In [17]: a + Out[17]: array([1, 2, 5, 4]) + # a and b share data + + +demo: ``slice.ipynb`` + + +Broadcasting: +------------- + +Element-wise operations among two different rank arrays: + +Simple case: scalar and array: +:: + + In [37]: a + Out[37]: array([1, 2, 3]) + In [38]: a*3 + Out[38]: array([3, 6, 9]) + + +Great for functions of more than one variable on a grid + +demo: ``broadcasting.ipynb`` + +What is an nd array? +-------------------- + + * N-dimensional (up to 32!) + * Homogeneous array: + + * Every element is the same type + (but that type can be a pyObject) + * Int, float, char -- more exotic types + + * "rank" – number of dimensions + * Strided data: + + * Describes how to index into block of memory + * PEP 3118 -- Revising the buffer protocol + + +demo: ``mem_struct.ipynb`` + + +Built-in Data Types +------------------- + + * Signed and unsigned Integers + 8, 16, 32, 64 bits + * Floating Point + 32, 64, 96, 128 bits (not all platforms) + * Complex + 64, 128, 192, 256 bits + * String and unicode + Static length + * Bool -- 8 bit + * Python Object + Really a pointer + +demo: ``object.ipynb`` + + +Text File I/O +-------------- + +Loading from text (CSV, etc): + + * ``np.loadtxt`` + * ``np.genfromtxt`` ( a few more features ) + +Saving as text (CSV): + + * ``np.savetxt()`` + +Compound dtypes +--------------- + + + * Can define any combination of other types + Still Homogeneous: Array of structs. + * Can name the fields + * Can be like a database table + * Useful for reading binary data + + +demo: ``dtypes.ipynb`` + + +Numpy Persistence: +------------------ + +``np.tofile() / np.fromfile()`` + + -- Just the raw bytes, no metadata + +pickle + +``np.savez()`` -- numpy zip format + +Compact: binary dump plus metadata + +netcdf + * NetCDF4 + +Hdf + * Pyhdf + * pytables + + +Working with compiled code +--------------------------- + +Wrapper around a C pointer to a block of data} + + * Some code can't be vectorized + * Interface with existing libraries + +Tools: + + * C API: you don't want to do that! + * Cython: typed arrays + * Ctypes + * SWIG: numpy.i + * Boost: boost array + * f2py + + +Example of numpy+cython: http://wiki.cython.org/examples/mandelbrot + +Other stuff: +------------ + + * Masked arrays + * Memory-mapped files + * Set operations: unique, etc + * Random numbers + * Polynomials + * FFT + * Sorting and searching + * Linear Algebra + * Statistics + +(And all of scipy, pandas, etc.) + +numpy docs: +----------- + +www.numpy.org + -- Numpy reference Downloads, etc + +www.scipy.org + -- lots of docs + +Scipy cookbook: + + http://www.scipy.org/Cookbook + +"The Numpy Book" + +http://www.tramy.us/numpybook.pdf + + + diff --git a/slides_sources/source/packaging.rst b/_sources/packaging.rst.txt similarity index 66% rename from slides_sources/source/packaging.rst rename to _sources/packaging.rst.txt index 36cc6c2..2a6bdec 100644 --- a/slides_sources/source/packaging.rst +++ b/_sources/packaging.rst.txt @@ -1,8 +1,8 @@ +.. _packaging: - -========================= +------------------------- Building Your Own Package -========================= +------------------------- The very basics of what you need to know to make your own package. @@ -10,16 +10,21 @@ The very basics of what you need to know to make your own package. :maxdepth: 2 Why Build a Package? -===================== +==================== + +.. rst-class:: left -There are a bunch of nifty tools that help you build, install and distribute packages. + There are a bunch of nifty tools that help you build, install and + distribute packages. -Using a well structured, standard layout for your package makes it easy to use those tools. + Using a well structured, standard layout for your package makes it + easy to use those tools. -Even if you never want to give anyone else your code, a well structured package eases development. + Even if you never want to give anyone else your code, a well + structured package eases development. What is a Package? -=================== +-------------------- **A collection of modules** @@ -34,7 +39,7 @@ What is a Package? * ... and a way to build and install it... Python packaging tools: -======================== +------------------------ The ``distutils``:: @@ -51,38 +56,44 @@ But it gets the job done -- and it does it well for the simple cases. ``wheel``: for binary distributions Where do I go to figure this out? -=================================== +----------------------------------- This is a really good guide: -The Hitchhiker’s Guide to Packaging: +Python Packaging User Guide: + +https://packaging.python.org/en/latest/ + +**Follow it!** - http://guide.python-distribute.org/index.html +And a sample project here: -Follow it! +https://github.com/pypa/sampleproject + +(this has all the complexity you might need...) Basic Package Structure: -======================== +------------------------ :: - PackageName/ - bin/ - CHANGES.txt - docs/ - LICENSE.txt - MANIFEST.in - README.txt - setup.py package_name/ - __init__.py - module1.py - module2.py - test/ - __init__.py - test_module1.py - test_module2.py + bin/ + CHANGES.txt + docs/ + LICENSE.txt + MANIFEST.in + README.txt + setup.py + package_name/ + __init__.py + module1.py + module2.py + test/ + __init__.py + test_module1.py + test_module2.py .. nextslide:: @@ -95,7 +106,7 @@ Basic Package Structure: ``README.txt``: description of the package -- should be written in reST (for PyPi): -http://docutils.sourceforge.net/rst.html +(http://docutils.sourceforge.net/rst.html) ``setup.py``: distutils script for building/installing package. @@ -112,7 +123,7 @@ http://docutils.sourceforge.net/rst.html ``test/``: your unit tests. Options here: -* Put it inside the package -- supports :: +Put it inside the package -- supports :: $ pip install package_name >> import package_name.test @@ -121,7 +132,7 @@ http://docutils.sourceforge.net/rst.html Or keep it at the top level. The ``setup.py`` File -====================== +---------------------- Your ``setup.py`` file is what describes your package, and tells the distutils how to pacakge, build and install it @@ -130,14 +141,14 @@ It is python code, so you can add anything custom you need to it But in the simple case, it is essentially declarative. -``http://docs.python.org/2/distutils/`` +``http://docs.python.org/3/distutils/`` .. nextslide:: :: - from distutils.core import setup + from setuptools import setup setup( name='PackageName', @@ -157,7 +168,7 @@ But in the simple case, it is essentially declarative. ) ``setup.cfg`` -============== +-------------- ``setup.cfg`` provides a way to give the end user some ability to customise the install @@ -176,20 +187,20 @@ simple to read and write. Note that an option spelled ``--foo-bar`` on the command-line is spelled f``foo_bar`` in configuration files. -Running `setup.py`` -===================== +Running ``setup.py`` +--------------------- -With a setup.py script defined, the distutils can do a lot: +With a ``setup.py`` script defined, the distutils can do a lot: * builds a source distribution (defaults to tar file):: - python setup.py sdist - python setup.py sdist --format=zip + python setup.py sdist + python setup.py sdist --format=zip * builds binary distributions:: - python setup.py bdist_rpm - python setup.py bdist_wininst + python setup.py bdist_rpm + python setup.py bdist_wininst (other, more obscure ones, too....) @@ -199,18 +210,18 @@ But you probably want to use wheel for binary disributions now. * build from source:: - python setup.py build + python setup.py build * and install:: - python setup.py install + python setup.py install setuptools -=========== +----------- ``setuptools`` is an extension to ``distutils`` that provides a number of extensions:: - from setuptools import setup + from setuptools import setup superset of the ``distutils setup`` @@ -225,13 +236,16 @@ This buys you a bunch of additional functionality: http://pythonhosted.org//setuptools/ wheels -======= +------- Wheels are a new binary format for packages. http://wheel.readthedocs.org/en/latest/ -Pretty simple, essentially an zip archive of all the stuff that gets put in ``site-packages`` +Pretty simple, essentially an zip archive of all the stuff that gets put +in + +``site-packages`` Can be just pure python or binary with compiled extensions @@ -256,7 +270,7 @@ Create a set of wheels (a wheelhouse):: ``pip install --no-use-wheel`` avoids that. PyPi -===== +----- The Python package index: @@ -275,12 +289,16 @@ http://docs.python.org/2/distutils/packageindex.html Under Development -================== +------------------ -Develop mode is really really nice:: +Develop mode is *really* *really* nice:: python setup.py develop +or:: + + pip install -e ./ + It puts links into the python installation to your code, so that your package is installed, but any changes will immediately take effect. This way all your test code, and client code, etc, can all import your package the usual way. @@ -291,8 +309,59 @@ Good idea to use it for anything more than a single file project. (requires ``setuptools``) +Running tests +------------- + +It can be a good idea to set up yoru tests to be run from ``setup.py`` + +So that you (or your users) can: + +.. code-block:: bash + + $ pip install . + $ python setup.py test + +Do do this, you need to add a ``test_suite`` stanza in setup.py. + +**nose** + +.. code-block:: python + + setup ( + # ... + test_suite = 'nose.collector' + ) + +**pytest** + +.. code-block:: python + + setup( + #..., + setup_requires=['pytest-runner', ...], + tests_require=['pytest', ...], + #..., + ) + +And create an alias into setup.cfg file:: + + [aliases] + test=pytest + +https://pytest.org/latest/goodpractices.html#integrating-with-setuptools-python-setup-py-test-pytest-runner + +**unittest** + +.. code-block:: python + + + test_suite="tests" + +(does py3 unittest have this??) + + Getting Started -================ +---------------- For anything but a single-file script (and maybe even then): @@ -300,7 +369,7 @@ For anything but a single-file script (and maybe even then): 2. Write a ``setup.py`` -3. ``python setup.py develop`` +3. ``python -m pip install -e .`` 4. Put some tests in ``package/test`` @@ -308,7 +377,7 @@ For anything but a single-file script (and maybe even then): LAB -===== +--- * Create a small package @@ -323,7 +392,7 @@ LAB * If you are ready -- it can be the start of your project package. -(example in ``code/Capitalize``) +(otherwise you may start with the silly code in ``Examples/capitalize``) diff --git a/_sources/packaging.txt b/_sources/packaging.txt new file mode 100644 index 0000000..2a6bdec --- /dev/null +++ b/_sources/packaging.txt @@ -0,0 +1,400 @@ +.. _packaging: + +------------------------- +Building Your Own Package +------------------------- + +The very basics of what you need to know to make your own package. + +.. toctree:: + :maxdepth: 2 + +Why Build a Package? +==================== + +.. rst-class:: left + + There are a bunch of nifty tools that help you build, install and + distribute packages. + + Using a well structured, standard layout for your package makes it + easy to use those tools. + + Even if you never want to give anyone else your code, a well + structured package eases development. + +What is a Package? +-------------------- + +**A collection of modules** + +* ... and the documentation + +* ... and the tests + +* ... and any top-level scripts + +* ... and any data files required + +* ... and a way to build and install it... + +Python packaging tools: +------------------------ + +The ``distutils``:: + + from distutils.core import setup + +Getting klunky, hard to extend, maybe destined for deprication... + +But it gets the job done -- and it does it well for the simple cases. + +``setuptools``: for extra features + +``pip``: for installing packages + +``wheel``: for binary distributions + +Where do I go to figure this out? +----------------------------------- + +This is a really good guide: + +Python Packaging User Guide: + +https://packaging.python.org/en/latest/ + +**Follow it!** + +And a sample project here: + +https://github.com/pypa/sampleproject + +(this has all the complexity you might need...) + + +Basic Package Structure: +------------------------ + +:: + + package_name/ + bin/ + CHANGES.txt + docs/ + LICENSE.txt + MANIFEST.in + README.txt + setup.py + package_name/ + __init__.py + module1.py + module2.py + test/ + __init__.py + test_module1.py + test_module2.py + + +.. nextslide:: + +``CHANGES.txt``: log of changes with each release + +``LICENSE.txt``: text of the license you choose (do choose one!) + +``MANIFEST.in``: description of what non-code files to include + +``README.txt``: description of the package -- should be written in reST (for PyPi): + +(http://docutils.sourceforge.net/rst.html) + +``setup.py``: distutils script for building/installing package. + + +.. nextslide:: + +``bin/``: This is where you put top-level scripts + + ( some folks use ``scripts`` ) + +``docs/``: the documentation + +``package_name/``: The main pacakge -- this is where the code goes. + +``test/``: your unit tests. Options here: + +Put it inside the package -- supports :: + + $ pip install package_name + >> import package_name.test + >> package_name.test.runall() + +Or keep it at the top level. + +The ``setup.py`` File +---------------------- + +Your ``setup.py`` file is what describes your package, and tells the distutils how to pacakge, build and install it + +It is python code, so you can add anything custom you need to it + +But in the simple case, it is essentially declarative. + + +``http://docs.python.org/3/distutils/`` + + +.. nextslide:: + +:: + + from setuptools import setup + + setup( + name='PackageName', + version='0.1.0', + author='An Awesome Coder', + author_email='aac@example.com', + packages=['package_name', 'package_name.test'], + scripts=['bin/script1','bin/script2'], + url='http://pypi.python.org/pypi/PackageName/', + license='LICENSE.txt', + description='An awesome package that does something', + long_description=open('README.txt').read(), + install_requires=[ + "Django >= 1.1.1", + "pytest", + ], + ) + +``setup.cfg`` +-------------- + +``setup.cfg`` provides a way to give the end user some ability to customise the install + +It's an ``ini`` style file:: + + [command] + option=value + ... + +simple to read and write. + +``command`` is one of the Distutils commands (e.g. build_py, install) + +``option`` is one of the options that command supports. + +Note that an option spelled ``--foo-bar`` on the command-line is spelled f``foo_bar`` in configuration files. + + +Running ``setup.py`` +--------------------- + +With a ``setup.py`` script defined, the distutils can do a lot: + +* builds a source distribution (defaults to tar file):: + + python setup.py sdist + python setup.py sdist --format=zip + +* builds binary distributions:: + + python setup.py bdist_rpm + python setup.py bdist_wininst + +(other, more obscure ones, too....) + +But you probably want to use wheel for binary disributions now. + +.. nextslide:: + +* build from source:: + + python setup.py build + +* and install:: + + python setup.py install + +setuptools +----------- + +``setuptools`` is an extension to ``distutils`` that provides a number of extensions:: + + from setuptools import setup + +superset of the ``distutils setup`` + +This buys you a bunch of additional functionality: + + * auto-finding packages + * better script installation + * resource (non-code files) management + * **develop mode** + * a LOT more + +http://pythonhosted.org//setuptools/ + +wheels +------- + +Wheels are a new binary format for packages. + +http://wheel.readthedocs.org/en/latest/ + +Pretty simple, essentially an zip archive of all the stuff that gets put +in + +``site-packages`` + +Can be just pure python or binary with compiled extensions + +Compatible with virtualenv. + +.. nextslide:: + +Building a wheel:: + + python setup.py bdist_wheel + +Create a set of wheels (a wheelhouse):: + + # Build a directory of wheels for pyramid and all its dependencies + pip wheel --wheel-dir=/tmp/wheelhouse pyramid + + # Install from cached wheels + pip install --use-wheel --no-index --find-links=/tmp/wheelhouse pyramid + +``pip install packagename`` will find wheels for Windows and OS-X. + +``pip install --no-use-wheel`` avoids that. + +PyPi +----- + +The Python package index: + +https://pypi.python.org/pypi + +You've all used this -- ``pip install`` searches it. + +To upload your package to PyPi:: + + python setup.py register + + python setup.py sdist bdist_wheel upload + + +http://docs.python.org/2/distutils/packageindex.html + + +Under Development +------------------ + +Develop mode is *really* *really* nice:: + + python setup.py develop + +or:: + + pip install -e ./ + +It puts links into the python installation to your code, so that your package is installed, but any changes will immediately take effect. + +This way all your test code, and client code, etc, can all import your package the usual way. + +No ``sys.path`` hacking + +Good idea to use it for anything more than a single file project. + +(requires ``setuptools``) + +Running tests +------------- + +It can be a good idea to set up yoru tests to be run from ``setup.py`` + +So that you (or your users) can: + +.. code-block:: bash + + $ pip install . + $ python setup.py test + +Do do this, you need to add a ``test_suite`` stanza in setup.py. + +**nose** + +.. code-block:: python + + setup ( + # ... + test_suite = 'nose.collector' + ) + +**pytest** + +.. code-block:: python + + setup( + #..., + setup_requires=['pytest-runner', ...], + tests_require=['pytest', ...], + #..., + ) + +And create an alias into setup.cfg file:: + + [aliases] + test=pytest + +https://pytest.org/latest/goodpractices.html#integrating-with-setuptools-python-setup-py-test-pytest-runner + +**unittest** + +.. code-block:: python + + + test_suite="tests" + +(does py3 unittest have this??) + + +Getting Started +---------------- + +For anything but a single-file script (and maybe even then): + +1. Create the basic package structure + +2. Write a ``setup.py`` + +3. ``python -m pip install -e .`` + +4. Put some tests in ``package/test`` + +5. ``py.test`` or ``nosetests`` + + +LAB +--- + +* Create a small package + + - package structure + + - ``setup.py`` + + - ``python setup.py develop`` + + - ``at least one working test`` + + +* If you are ready -- it can be the start of your project package. + +(otherwise you may start with the silly code in ``Examples/capitalize``) + + + + + diff --git a/_sources/persistance_serialization.rst.txt b/_sources/persistance_serialization.rst.txt new file mode 100644 index 0000000..45745e1 --- /dev/null +++ b/_sources/persistance_serialization.rst.txt @@ -0,0 +1,767 @@ +.. _serialization: + +***************************** +Persistence and Serialization +***************************** + +======== +Overview +======== + +.. rst-class:: left + Persistence and Serialization are closely related. + + *Serialization* means taking a potentially complex data structure and converting it into a single string of bytes. + + https://en.wikipedia.org/wiki/Serialization + + *Persistence* is storing data in a way that it will persist beyond the run-time of your program. + + https://en.wikipedia.org/wiki/Persistence_(computer_science) + + They are closely related, because most forms of persistent storage -- simple text files, databases, etc., require that it be turned into a simple string of bytes first. After all, at the end of the day, everything done with computers is ultimately a serial string of bytes. + + Serialization is also very useful for transmitting information between systems -- over the network, etc. + + +Serialization +------------- + +Today is less about concepts. + +More about learning to use a given module. + +.. rst-class:: medium + + So less talk, more coding. + +Today is focused on methods available in the Python standard library. + +There are third party packages with more options as well. + + +Persistence +----------- + +Persistence is saving your python data structure(s) to disk -- so they +will persist once the python process is finished. + +Any serial form can provide persistence (by dumping/loading it to/from +a file), but not all persistence mechanisms are serial (i.e RDBMS) + +http://wiki.python.org/moin/PersistenceTools + + +======================= +Python Specific Formats +======================= + +.. rst-class:: medium + + These are formats specific to python -- convenient to use, but not useful for interchange with other systems. + + +Python Literals +--------------- + +Putting plain old python literals in your file + +Gives a nice, human-editable form for config files, etc. + +Don't use for untrusted sources!!! + +Good for basic python types. + +(can work for your own classes, too -- if you write a good ``__repr__`` ) + +In theory, ``repr()`` always gives a form that can be re-constructed. + +Often the ``str()`` form works too. + +``pprint`` (pretty print) module can make it easier to read: + +https://docs.python.org/3.5/library/pprint.html + + +Python Literal Example +---------------------- + +.. code-block:: ipython + + # a list of dicts + data = [{'this':5, 'that':4}, {'spam':7, 'eggs':3.4}] + In [51]: s = repr(data) # save a string version: + In [52]: data2 = eval(s) # re-construct with eval: + In [53]: data2 == data # they are equal + Out[53]: True + In [54]: data is data2 # but not the same object + Out[54]: False + + +You can save the string to a file and even use ``import``. + +In fact, using a python file and importing it is a great way to handle configuration for your app -- very powerful and flexible. + +.. nextslide:: + +NOTE: ``eval()`` is **DANGEROUS**: + +Not so bad if you know where your data is coming from, but ``eval()`` will run any code it gets, even: + +.. code-block:: python + + import sys + sys.system('cd /; rm -rf *') + +You really don't want that run on your machine! + +The alternative: + ``ast.literal_eval`` is safer than eval: + + https://docs.python.org/3.5/library/ast.html#ast-helpers + +It will only evaluate literals. + + +pretty print +------------ + +.. code-block:: ipython + + In [68]: data = [{'this': 5, 'that': 4}, {'eggs': 3.4, 'spam': 7}, + {'foo': 86, 'bar': 4.5}, {'fun': 43, 'baz': 6.5}] + In [69]: import pprint + In [71]: repr(data) + Out[71]: "[{'this': 5, 'that': 4}, {'eggs': 3.4, 'spam': 7}, {'foo': 86, 'bar': 4.5}, {'fun': 43, 'baz': 6.5}]" + In [72]: s = pprint.pformat(data) + In [73]: print(s) + [{'that': 4, 'this': 5}, + {'eggs': 3.4, 'spam': 7}, + {'bar': 4.5, 'foo': 86}, + {'baz': 6.5, 'fun': 43}] + +This is a nice option if you want the saved form to be human readable / editable. + +Pickle +------ + +Pickle is a custom binary format for python objects. + +You can essentially dump any python object to disk (or string, or socket, or... + +.. code-block:: ipython + + In [87]: import pickle + In [83]: data + Out[83]: + [{'that': 4, 'this': 5}, + {'eggs': 3.4, 'spam': 7}, + {'bar': 4.5, 'foo': 86}, + {'baz': 6.5, 'fun': 43}] + In [84]: pickle.dump(data, open('data.pkl', 'wb')) + In [85]: data2 = pickle.load(open('data.pkl', 'rb')) + In [86]: data2 == data + Out[86]: True + +https://docs.python.org/3.5/library/pickle.html + +.. nextslide:: + +.. rst-class:: medium + + **Warning** + +The pickle module is **not secure** against erroneous or maliciously constructed data. Never unpickle data received from an untrusted or unauthenticated source. + +``pickle`` is cool because it can serialize virtually ANY object -- including your self-defined classes. + +But to do this, it must run essentially arbitrary code -- so **not safe**. + +Do not use it for receiving data from an external source. + +But you probably won't want to do that anyway -- pickle is python-specific, not very useful for data interchange. + +Shelve +------ + +A "shelf" is a persistent, dictionary-like object. + +(It's also a place you can put a jar of pickles...) + +The values (not the keys!) can be essentially arbitrary Python objects (anything picklable) + +**NOTE:** it will not reflect changes in mutable objects without re-writing them to the db. (or use ``writeback=True``) + +If less that 100s of MB -- just use a dict and pickle it. + +https://docs.python.org/3.5/library/shelve.html + +.. nextslide:: + +``shelve`` presents a ``dict`` interface: + +.. code-block:: ipython + + import shelve + d = shelve.open(filename) + d[key] = data # store data at key + data = d[key] # retrieve a COPY of data at key + del d[key] # delete data stored at key + flag = d.has_key(key) # true if the key exists + d.close() # close it + +(it uses pickle under the hood -- same security issues) + +https://docs.python.org/3.5/library/shelve.html + +LAB +--- + +Here are two datasets embedded in Python: + +:download:`add_book_data.py <../../Examples/persistence/add_book_data.py>` +and +:download:`add_book_data_flat.py <../../Examples/persistence/add_book_data_flat.py>` + +[also available in the class repo: ``Examples/persistence``] + +.. nextslide:: + +They can be loaded with:: + + from add_book_data import AddressBook + +They have address book data -- one with a nested dict, one "flat". Use the nested version for this exercise. + +* Write a module that saves the data as python literals in a file + + - and reads it back in + +* Write a module that saves the data as a pickle in a file + + - and reads it back in + +* Write a module that saves the data in a shelve + + - and accesses it one by one. + +**Write some tests to make sure its working!** + +=================== +Interchange Formats +=================== + +.. rst-class:: medium + + These are formats suitable for interchanging data with other systems -- written in arbitrary other languages. + + In other words: standard formats. + +INI +--- + +INI files + +(the old Windows config files) + +:: + + [Section1] + int = 15 + bool = true + float = 3.1415 + [Section2] + int = 32 + ... + +Good for configuration data, etc. + +ConfigParser +------------ + +The ``configparser`` module provides tools for working with INI files: + +Writing: + +.. code-block:: python + + import configparser + config = configparser.ConfigParser() + config.add_section('Section1') + config.set('Section1', 'an_integer', '15') + config.set('Section1', 'a_boolean', 'true') + config.set('Section1', 'a_float', '3.1415') + # Writing our configuration file to 'example.cfg' + config.write(open('example.cfg', 'w')) + +Note: all keys and values are strings + +.. nextslide:: + +Reading ``ini`` files: + +.. code-block:: python + + >>> config = configparser.ConfigParser() + >>> config.read('example.cfg') + >>> config.sections() + ['Section1'] + >>> config.get('Section1', 'a_float') + '3.1415' + >>> config.items('Section1') + [('an_integer', '15'), ('a_boolean', 'true'), ('a_float', '3.1415')] + + +https://docs.python.org/3.5/library/configparser.html + +CSV +--- + +CSV (Comma Separated Values) format is the most common import and export format for spreadsheets and databases. + +No real standard -- the Python csv package more or less follows MS Excel "standard" (with other "dialects" available) + +Can use delimiters other than commas... (I like tabs better) + +Most useful for simple tabular data + +The CSV module +-------------- + +Reading ``CSV`` files: + +(uses: :download:`eggs.csv <../../Examples/persistence/eggs.csv>`) + +.. code-block:: ipython + + In [14]: import csv + In [17]: spam_reader = csv.reader(open('eggs.csv'), + skipinitialspace=True) + In [19]: for row in spam_reader: + ....: print(row) + ['Spam', ' Spam', ' Spam', ' Spam', ' Spam', ' Baked Beans'] + ['Spam', ' Lovely Spam', ' Wonderful Spam'] + + +The ``csv`` module takes care of string quoting, etc. for you. + +- This is a pretty big deal -- that can be a real pain! + +NOTE: ``skipinitialspace`` is False by default, which can mess up +interpretting quotes correctly. + +https://docs.python.org/3.5/library/csv.html + +.. nextslide:: + +Writing ``CSV`` files: + +.. code-block:: python + + >>> import csv + >>> with open('eggs2.csv', 'w') as outfile: + >>> spam_writer = csv.writer(outfile, + quoting=csv.QUOTE_MINIMAL) + >>> spam_writer.writerow(['Spam'] * 5 + ['Baked Beans']) + >>> spam_writer.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam']) + >>> spam_writer.writerow(['Spam', 'Spam, Wonderful spam..', 'Very-Wonderful Spam']) + + +The ``csv`` module takes care of string quoting, etc. for you. + +You can set the ``quoting`` attribute on the dialect object to control that. + +https://docs.python.org/3.5/library/csv.html + + +JSON +---- + +JSON (JavaScript Object Notation) is a subset of JavaScript syntax used as a lightweight data interchange format. + +**LOTS** of systems can read JSON -- notably browsers... + +Python module has an interface similar to ``pickle`` + +Can handle the standard Python data types + +Specializable encoding/decoding for other types -- but I wouldn't do that! + +Commonly used for configuration files, etc. + +http://www.json.org/ + +https://docs.python.org/3.5/library/json.html + +Python json module +------------------ + +.. code-block:: ipython + + In [93]: import json + In [94]: s = json.dumps(data) + Out[95]: '[{"this": 5, "that": 4}, {"eggs": 3.4, "spam": 7}, + {"foo": 86, "bar": 4.5}, {"fun": 43, "baz": 6.5}]' + In [96]: data2 = json.loads(s) + Out[97]: + [{u'that': 4, u'this': 5}, + {u'eggs': 3.4, u'spam': 7}, + ... + In [98]: data2 == data + Out[98]: True # they are the same + +(also ``json.dump() and json.load()`` for files) + +**NOTE:** JSON is less "rich" than python -- no tuples, no distinction between integers and floats, no comments! And keys can only be strings. + +https://docs.python.org/3.5/library/json.html + +LAB +--- + +Use the same addressbook data: + +:: + + # load with: + from add_book_data import AddressBook + +* Write a module that saves the data as an INI file + + - and reads it back in + +* Write a module that saves the data as a CSV file + + - and reads it back in + +( you'll need the "flat" version for this...) + +* Write a module that saves the data in JSON + + - and reads it back in + +XML +--- + +XML is a standardized version of SGML, designed for use as a data storage / interchange format. + +NOTE: HTML is also SGML, and modern versions conform to the XML standard. + +XML in the python std lib +------------------------- + +``xml.dom`` + +``xml.sax`` + +``xml.parsers.expat`` + +``xml.etree`` + +https://docs.python.org/3.5/library/xml.etree.elementtree.html + +elementtree +----------- + +``elementtree`` is the simplest tool -- maps pretty directly to XML. + +The Element type is a flexible container object, designed to store hierarchical data structures in memory. + +Essentially an in-memory XML -- can be read from/written to XML + +an ``ElementTree`` is an entire XML doc + +an ``Element`` is a node in that tree + +https://docs.python.org/3.5/library/xml.etree.elementtree.html + + +* Write a module that saves the data in XML + + - and reads it back in + + - this gets ugly! + +(NEED a good example here!) + +========= +DataBases +========= + +A database is a system for storing and retrieving data -- usually in a filesystem. + +We usually think RDBMS and SQL -- but there are simpler systems. + +dbm +--- + +``dbm`` is a generic interface to variants of the DBM database + +Suitable for storing data that fits well into a python dict with strings as both keys and values + +Note: dbm will use the dbm system that works on your system -- this may be different on different systems -- so the db files may NOT be compatible! ``whichdb`` will try to figure it out, but it's not guaranteed + +https://docs.python.org/3.5/library/dbm.html + +**NOTE:** dbm is getting pretty old fashioned -- e.g. it doesn't handle Unicode + +It's here for completeness, but there are probably better options! + + +the ``dbm`` module +------------------ + +Writing data: + +.. code-block:: python + + #creating a dbm file: + import dbm + dbm.open(filename, 'n') + +flag options are: + +* 'r' -- Open existing database for reading only (default) +* 'w' -- Open existing database for reading and writing +* 'c' -- Open database for reading and writing, creating it if it doesn’t exist +* 'n' -- Always create a new, empty database, open for reading and writing + +**caution** -- these are different than the file open modes! + +https://docs.python.org/3.5/library/dbm.html + +.. nextslide:: + +``dbm`` provides a dict-like interface: + +.. code-block:: python + + import dbm + db = dbm.open("dbm", "c") + db["first"] = "bruce" + db["second"] = "micheal" + db["third"] = "fred" + db["second"] = "john" #overwrite + db.close() + # read it: + db = dbm.open("dbm", "r") + for key in db.keys(): + print(key, db[key]) + +(a lot like ``shelve``, though theoretically compatible with other systems) + +http://docs.python.org/library/anydbm.html + + +sqlite +------ + +**SQLite:** + +a C library providing a lightweight disk-based single-file database + +Nonstandard variant of the SQL query language + +Very broadly used as as an embedded databases for storing application-specific data etc. + +Firefox plug-in: + +https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/ + + +python sqlite module +-------------------- + +``sqlite3`` Python module wraps C lib -- provides standard DB-API interface + +Allows (and requires) SQL queries + +Can provide high performance, flexible, portable storage for your app + +https://docs.python.org/3.5/library/sqlite3.html + +.. nextslide:: + +Example: + +.. code-block:: python + + import sqlite3 + # open a connection to a db file: + conn = sqlite3.connect('example.db') + # or build one in-memory + conn = sqlite3.connect(':memory:') + # create a cursor + c = conn.cursor() + +https://docs.python.org/3.5/library/sqlite3.html + +.. nextslide:: + +Execute SQL with the cursor: + +.. code-block:: python + + # Create table + c.execute("'CREATE TABLE stocks (date text, trans text, symbol text, qty real, price real)'") + # Insert a row of data + c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)") + # Save (commit) the changes + conn.commit() + # Close the cursor if we are done with it + c.close() + + +https://docs.python.org/3.5/library/sqlite3.html + +.. nextslide:: + +``SELECT`` creates an cursor that can be iterated: + +.. code-block:: python + + >>> for row in c.execute('SELECT * FROM stocks ORDER BY price'): + print row + ('2006-01-05', 'BUY', 'RHAT', 100, 35.14) + ('2006-03-28', 'BUY', 'IBM', 1000, 45.0) + ... + + +Or you can get the rows one by one or in a list: + +.. code-block:: python + + c.fetchone() + c.fetchall() + + +.. nextslide:: + +Good idea to use the DB-API’s parameter substitution: + +.. code-block:: python + + t = (symbol,) + c.execute('SELECT * FROM stocks WHERE symbol=?', t) + print c.fetchone() + # Larger example that inserts many records at a time + purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00), + ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00), + ('2006-04-06', 'SELL', 'IBM', 500, 53.00), + ] + c.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases) + + +http://xkcd.com/327/ + + +DB-API +------ + +The DB-API spec (PEP 249) is a specification for interaction between Python and Relational Databases. + +Support for a large number of third-party Database drivers: + + * MySQL + * PostgreSQL + * Oracle + * MSSQL (?) + * ... + +http://www.python.org/dev/peps/pep-0249} + +LAB Extras: +=========== + +A few more things you could do: + +* Use pickle to save/reload a custom class of yours + (the Circle class from the first quarter?) + +* Try writing a json writer for a non-standard data type: + A custom class, or amore complex built-in? + + + + +============= +Other Options +============= + +There are a lot of other possibilities outside the standard lib. + + +Object-Relation Mappers +----------------------- + +Systems for mapping Python objects to tables + +Saves you writing that glue code (and the SQL) + +Usually deal with mapping to variety of back-ends: + +- test with SQLite, deploy with PostgreSQL + +SQL Alchemy + +- http://www.sqlalchemy.org/ + +Django ORM + +- https://docs.djangoproject.com/en/dev/topics/db/ + + +Object Databases +---------------- + +(we'll be talking more about this in another class: :ref:`nosql`) + +Directly store and retrieve Python Objects. + +Kind of like ``shelve``, but more flexible, and give you searching, etc. + +ZODB: (http://www.zodb.org/) + + +NoSQL +----- +Map-Reduce, etc. + +-- Big deal for "Big Data": Amazon, Google, etc. + +Document-Oriented Storage + +* MongoDB (BSON interface, JSON documents) + +* CouchDB (Apache): + + * JSON documents + + * Javascript querying (MapReduce) + + * HTTP API + + +LAB +--- + +Load data with: + +.. code-block:: python + + from add_book_data import AddressBook + +* Write a module that saves the data in a dbm database + + - and reads it back in + +* Write a module that saves the data in an SQLite database + + - and reads it back in + + - helps to know SQL here... + +Optional: + +* Do the same with a ORM of your choice. + diff --git a/_sources/persistance_serialization.txt b/_sources/persistance_serialization.txt new file mode 100644 index 0000000..45745e1 --- /dev/null +++ b/_sources/persistance_serialization.txt @@ -0,0 +1,767 @@ +.. _serialization: + +***************************** +Persistence and Serialization +***************************** + +======== +Overview +======== + +.. rst-class:: left + Persistence and Serialization are closely related. + + *Serialization* means taking a potentially complex data structure and converting it into a single string of bytes. + + https://en.wikipedia.org/wiki/Serialization + + *Persistence* is storing data in a way that it will persist beyond the run-time of your program. + + https://en.wikipedia.org/wiki/Persistence_(computer_science) + + They are closely related, because most forms of persistent storage -- simple text files, databases, etc., require that it be turned into a simple string of bytes first. After all, at the end of the day, everything done with computers is ultimately a serial string of bytes. + + Serialization is also very useful for transmitting information between systems -- over the network, etc. + + +Serialization +------------- + +Today is less about concepts. + +More about learning to use a given module. + +.. rst-class:: medium + + So less talk, more coding. + +Today is focused on methods available in the Python standard library. + +There are third party packages with more options as well. + + +Persistence +----------- + +Persistence is saving your python data structure(s) to disk -- so they +will persist once the python process is finished. + +Any serial form can provide persistence (by dumping/loading it to/from +a file), but not all persistence mechanisms are serial (i.e RDBMS) + +http://wiki.python.org/moin/PersistenceTools + + +======================= +Python Specific Formats +======================= + +.. rst-class:: medium + + These are formats specific to python -- convenient to use, but not useful for interchange with other systems. + + +Python Literals +--------------- + +Putting plain old python literals in your file + +Gives a nice, human-editable form for config files, etc. + +Don't use for untrusted sources!!! + +Good for basic python types. + +(can work for your own classes, too -- if you write a good ``__repr__`` ) + +In theory, ``repr()`` always gives a form that can be re-constructed. + +Often the ``str()`` form works too. + +``pprint`` (pretty print) module can make it easier to read: + +https://docs.python.org/3.5/library/pprint.html + + +Python Literal Example +---------------------- + +.. code-block:: ipython + + # a list of dicts + data = [{'this':5, 'that':4}, {'spam':7, 'eggs':3.4}] + In [51]: s = repr(data) # save a string version: + In [52]: data2 = eval(s) # re-construct with eval: + In [53]: data2 == data # they are equal + Out[53]: True + In [54]: data is data2 # but not the same object + Out[54]: False + + +You can save the string to a file and even use ``import``. + +In fact, using a python file and importing it is a great way to handle configuration for your app -- very powerful and flexible. + +.. nextslide:: + +NOTE: ``eval()`` is **DANGEROUS**: + +Not so bad if you know where your data is coming from, but ``eval()`` will run any code it gets, even: + +.. code-block:: python + + import sys + sys.system('cd /; rm -rf *') + +You really don't want that run on your machine! + +The alternative: + ``ast.literal_eval`` is safer than eval: + + https://docs.python.org/3.5/library/ast.html#ast-helpers + +It will only evaluate literals. + + +pretty print +------------ + +.. code-block:: ipython + + In [68]: data = [{'this': 5, 'that': 4}, {'eggs': 3.4, 'spam': 7}, + {'foo': 86, 'bar': 4.5}, {'fun': 43, 'baz': 6.5}] + In [69]: import pprint + In [71]: repr(data) + Out[71]: "[{'this': 5, 'that': 4}, {'eggs': 3.4, 'spam': 7}, {'foo': 86, 'bar': 4.5}, {'fun': 43, 'baz': 6.5}]" + In [72]: s = pprint.pformat(data) + In [73]: print(s) + [{'that': 4, 'this': 5}, + {'eggs': 3.4, 'spam': 7}, + {'bar': 4.5, 'foo': 86}, + {'baz': 6.5, 'fun': 43}] + +This is a nice option if you want the saved form to be human readable / editable. + +Pickle +------ + +Pickle is a custom binary format for python objects. + +You can essentially dump any python object to disk (or string, or socket, or... + +.. code-block:: ipython + + In [87]: import pickle + In [83]: data + Out[83]: + [{'that': 4, 'this': 5}, + {'eggs': 3.4, 'spam': 7}, + {'bar': 4.5, 'foo': 86}, + {'baz': 6.5, 'fun': 43}] + In [84]: pickle.dump(data, open('data.pkl', 'wb')) + In [85]: data2 = pickle.load(open('data.pkl', 'rb')) + In [86]: data2 == data + Out[86]: True + +https://docs.python.org/3.5/library/pickle.html + +.. nextslide:: + +.. rst-class:: medium + + **Warning** + +The pickle module is **not secure** against erroneous or maliciously constructed data. Never unpickle data received from an untrusted or unauthenticated source. + +``pickle`` is cool because it can serialize virtually ANY object -- including your self-defined classes. + +But to do this, it must run essentially arbitrary code -- so **not safe**. + +Do not use it for receiving data from an external source. + +But you probably won't want to do that anyway -- pickle is python-specific, not very useful for data interchange. + +Shelve +------ + +A "shelf" is a persistent, dictionary-like object. + +(It's also a place you can put a jar of pickles...) + +The values (not the keys!) can be essentially arbitrary Python objects (anything picklable) + +**NOTE:** it will not reflect changes in mutable objects without re-writing them to the db. (or use ``writeback=True``) + +If less that 100s of MB -- just use a dict and pickle it. + +https://docs.python.org/3.5/library/shelve.html + +.. nextslide:: + +``shelve`` presents a ``dict`` interface: + +.. code-block:: ipython + + import shelve + d = shelve.open(filename) + d[key] = data # store data at key + data = d[key] # retrieve a COPY of data at key + del d[key] # delete data stored at key + flag = d.has_key(key) # true if the key exists + d.close() # close it + +(it uses pickle under the hood -- same security issues) + +https://docs.python.org/3.5/library/shelve.html + +LAB +--- + +Here are two datasets embedded in Python: + +:download:`add_book_data.py <../../Examples/persistence/add_book_data.py>` +and +:download:`add_book_data_flat.py <../../Examples/persistence/add_book_data_flat.py>` + +[also available in the class repo: ``Examples/persistence``] + +.. nextslide:: + +They can be loaded with:: + + from add_book_data import AddressBook + +They have address book data -- one with a nested dict, one "flat". Use the nested version for this exercise. + +* Write a module that saves the data as python literals in a file + + - and reads it back in + +* Write a module that saves the data as a pickle in a file + + - and reads it back in + +* Write a module that saves the data in a shelve + + - and accesses it one by one. + +**Write some tests to make sure its working!** + +=================== +Interchange Formats +=================== + +.. rst-class:: medium + + These are formats suitable for interchanging data with other systems -- written in arbitrary other languages. + + In other words: standard formats. + +INI +--- + +INI files + +(the old Windows config files) + +:: + + [Section1] + int = 15 + bool = true + float = 3.1415 + [Section2] + int = 32 + ... + +Good for configuration data, etc. + +ConfigParser +------------ + +The ``configparser`` module provides tools for working with INI files: + +Writing: + +.. code-block:: python + + import configparser + config = configparser.ConfigParser() + config.add_section('Section1') + config.set('Section1', 'an_integer', '15') + config.set('Section1', 'a_boolean', 'true') + config.set('Section1', 'a_float', '3.1415') + # Writing our configuration file to 'example.cfg' + config.write(open('example.cfg', 'w')) + +Note: all keys and values are strings + +.. nextslide:: + +Reading ``ini`` files: + +.. code-block:: python + + >>> config = configparser.ConfigParser() + >>> config.read('example.cfg') + >>> config.sections() + ['Section1'] + >>> config.get('Section1', 'a_float') + '3.1415' + >>> config.items('Section1') + [('an_integer', '15'), ('a_boolean', 'true'), ('a_float', '3.1415')] + + +https://docs.python.org/3.5/library/configparser.html + +CSV +--- + +CSV (Comma Separated Values) format is the most common import and export format for spreadsheets and databases. + +No real standard -- the Python csv package more or less follows MS Excel "standard" (with other "dialects" available) + +Can use delimiters other than commas... (I like tabs better) + +Most useful for simple tabular data + +The CSV module +-------------- + +Reading ``CSV`` files: + +(uses: :download:`eggs.csv <../../Examples/persistence/eggs.csv>`) + +.. code-block:: ipython + + In [14]: import csv + In [17]: spam_reader = csv.reader(open('eggs.csv'), + skipinitialspace=True) + In [19]: for row in spam_reader: + ....: print(row) + ['Spam', ' Spam', ' Spam', ' Spam', ' Spam', ' Baked Beans'] + ['Spam', ' Lovely Spam', ' Wonderful Spam'] + + +The ``csv`` module takes care of string quoting, etc. for you. + +- This is a pretty big deal -- that can be a real pain! + +NOTE: ``skipinitialspace`` is False by default, which can mess up +interpretting quotes correctly. + +https://docs.python.org/3.5/library/csv.html + +.. nextslide:: + +Writing ``CSV`` files: + +.. code-block:: python + + >>> import csv + >>> with open('eggs2.csv', 'w') as outfile: + >>> spam_writer = csv.writer(outfile, + quoting=csv.QUOTE_MINIMAL) + >>> spam_writer.writerow(['Spam'] * 5 + ['Baked Beans']) + >>> spam_writer.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam']) + >>> spam_writer.writerow(['Spam', 'Spam, Wonderful spam..', 'Very-Wonderful Spam']) + + +The ``csv`` module takes care of string quoting, etc. for you. + +You can set the ``quoting`` attribute on the dialect object to control that. + +https://docs.python.org/3.5/library/csv.html + + +JSON +---- + +JSON (JavaScript Object Notation) is a subset of JavaScript syntax used as a lightweight data interchange format. + +**LOTS** of systems can read JSON -- notably browsers... + +Python module has an interface similar to ``pickle`` + +Can handle the standard Python data types + +Specializable encoding/decoding for other types -- but I wouldn't do that! + +Commonly used for configuration files, etc. + +http://www.json.org/ + +https://docs.python.org/3.5/library/json.html + +Python json module +------------------ + +.. code-block:: ipython + + In [93]: import json + In [94]: s = json.dumps(data) + Out[95]: '[{"this": 5, "that": 4}, {"eggs": 3.4, "spam": 7}, + {"foo": 86, "bar": 4.5}, {"fun": 43, "baz": 6.5}]' + In [96]: data2 = json.loads(s) + Out[97]: + [{u'that': 4, u'this': 5}, + {u'eggs': 3.4, u'spam': 7}, + ... + In [98]: data2 == data + Out[98]: True # they are the same + +(also ``json.dump() and json.load()`` for files) + +**NOTE:** JSON is less "rich" than python -- no tuples, no distinction between integers and floats, no comments! And keys can only be strings. + +https://docs.python.org/3.5/library/json.html + +LAB +--- + +Use the same addressbook data: + +:: + + # load with: + from add_book_data import AddressBook + +* Write a module that saves the data as an INI file + + - and reads it back in + +* Write a module that saves the data as a CSV file + + - and reads it back in + +( you'll need the "flat" version for this...) + +* Write a module that saves the data in JSON + + - and reads it back in + +XML +--- + +XML is a standardized version of SGML, designed for use as a data storage / interchange format. + +NOTE: HTML is also SGML, and modern versions conform to the XML standard. + +XML in the python std lib +------------------------- + +``xml.dom`` + +``xml.sax`` + +``xml.parsers.expat`` + +``xml.etree`` + +https://docs.python.org/3.5/library/xml.etree.elementtree.html + +elementtree +----------- + +``elementtree`` is the simplest tool -- maps pretty directly to XML. + +The Element type is a flexible container object, designed to store hierarchical data structures in memory. + +Essentially an in-memory XML -- can be read from/written to XML + +an ``ElementTree`` is an entire XML doc + +an ``Element`` is a node in that tree + +https://docs.python.org/3.5/library/xml.etree.elementtree.html + + +* Write a module that saves the data in XML + + - and reads it back in + + - this gets ugly! + +(NEED a good example here!) + +========= +DataBases +========= + +A database is a system for storing and retrieving data -- usually in a filesystem. + +We usually think RDBMS and SQL -- but there are simpler systems. + +dbm +--- + +``dbm`` is a generic interface to variants of the DBM database + +Suitable for storing data that fits well into a python dict with strings as both keys and values + +Note: dbm will use the dbm system that works on your system -- this may be different on different systems -- so the db files may NOT be compatible! ``whichdb`` will try to figure it out, but it's not guaranteed + +https://docs.python.org/3.5/library/dbm.html + +**NOTE:** dbm is getting pretty old fashioned -- e.g. it doesn't handle Unicode + +It's here for completeness, but there are probably better options! + + +the ``dbm`` module +------------------ + +Writing data: + +.. code-block:: python + + #creating a dbm file: + import dbm + dbm.open(filename, 'n') + +flag options are: + +* 'r' -- Open existing database for reading only (default) +* 'w' -- Open existing database for reading and writing +* 'c' -- Open database for reading and writing, creating it if it doesn’t exist +* 'n' -- Always create a new, empty database, open for reading and writing + +**caution** -- these are different than the file open modes! + +https://docs.python.org/3.5/library/dbm.html + +.. nextslide:: + +``dbm`` provides a dict-like interface: + +.. code-block:: python + + import dbm + db = dbm.open("dbm", "c") + db["first"] = "bruce" + db["second"] = "micheal" + db["third"] = "fred" + db["second"] = "john" #overwrite + db.close() + # read it: + db = dbm.open("dbm", "r") + for key in db.keys(): + print(key, db[key]) + +(a lot like ``shelve``, though theoretically compatible with other systems) + +http://docs.python.org/library/anydbm.html + + +sqlite +------ + +**SQLite:** + +a C library providing a lightweight disk-based single-file database + +Nonstandard variant of the SQL query language + +Very broadly used as as an embedded databases for storing application-specific data etc. + +Firefox plug-in: + +https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/ + + +python sqlite module +-------------------- + +``sqlite3`` Python module wraps C lib -- provides standard DB-API interface + +Allows (and requires) SQL queries + +Can provide high performance, flexible, portable storage for your app + +https://docs.python.org/3.5/library/sqlite3.html + +.. nextslide:: + +Example: + +.. code-block:: python + + import sqlite3 + # open a connection to a db file: + conn = sqlite3.connect('example.db') + # or build one in-memory + conn = sqlite3.connect(':memory:') + # create a cursor + c = conn.cursor() + +https://docs.python.org/3.5/library/sqlite3.html + +.. nextslide:: + +Execute SQL with the cursor: + +.. code-block:: python + + # Create table + c.execute("'CREATE TABLE stocks (date text, trans text, symbol text, qty real, price real)'") + # Insert a row of data + c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)") + # Save (commit) the changes + conn.commit() + # Close the cursor if we are done with it + c.close() + + +https://docs.python.org/3.5/library/sqlite3.html + +.. nextslide:: + +``SELECT`` creates an cursor that can be iterated: + +.. code-block:: python + + >>> for row in c.execute('SELECT * FROM stocks ORDER BY price'): + print row + ('2006-01-05', 'BUY', 'RHAT', 100, 35.14) + ('2006-03-28', 'BUY', 'IBM', 1000, 45.0) + ... + + +Or you can get the rows one by one or in a list: + +.. code-block:: python + + c.fetchone() + c.fetchall() + + +.. nextslide:: + +Good idea to use the DB-API’s parameter substitution: + +.. code-block:: python + + t = (symbol,) + c.execute('SELECT * FROM stocks WHERE symbol=?', t) + print c.fetchone() + # Larger example that inserts many records at a time + purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00), + ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00), + ('2006-04-06', 'SELL', 'IBM', 500, 53.00), + ] + c.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases) + + +http://xkcd.com/327/ + + +DB-API +------ + +The DB-API spec (PEP 249) is a specification for interaction between Python and Relational Databases. + +Support for a large number of third-party Database drivers: + + * MySQL + * PostgreSQL + * Oracle + * MSSQL (?) + * ... + +http://www.python.org/dev/peps/pep-0249} + +LAB Extras: +=========== + +A few more things you could do: + +* Use pickle to save/reload a custom class of yours + (the Circle class from the first quarter?) + +* Try writing a json writer for a non-standard data type: + A custom class, or amore complex built-in? + + + + +============= +Other Options +============= + +There are a lot of other possibilities outside the standard lib. + + +Object-Relation Mappers +----------------------- + +Systems for mapping Python objects to tables + +Saves you writing that glue code (and the SQL) + +Usually deal with mapping to variety of back-ends: + +- test with SQLite, deploy with PostgreSQL + +SQL Alchemy + +- http://www.sqlalchemy.org/ + +Django ORM + +- https://docs.djangoproject.com/en/dev/topics/db/ + + +Object Databases +---------------- + +(we'll be talking more about this in another class: :ref:`nosql`) + +Directly store and retrieve Python Objects. + +Kind of like ``shelve``, but more flexible, and give you searching, etc. + +ZODB: (http://www.zodb.org/) + + +NoSQL +----- +Map-Reduce, etc. + +-- Big deal for "Big Data": Amazon, Google, etc. + +Document-Oriented Storage + +* MongoDB (BSON interface, JSON documents) + +* CouchDB (Apache): + + * JSON documents + + * Javascript querying (MapReduce) + + * HTTP API + + +LAB +--- + +Load data with: + +.. code-block:: python + + from add_book_data import AddressBook + +* Write a module that saves the data in a dbm database + + - and reads it back in + +* Write a module that saves the data in an SQLite database + + - and reads it back in + + - helps to know SQL here... + +Optional: + +* Do the same with a ORM of your choice. + diff --git a/_sources/profiling.rst.txt b/_sources/profiling.rst.txt new file mode 100644 index 0000000..401ae5a --- /dev/null +++ b/_sources/profiling.rst.txt @@ -0,0 +1,713 @@ +.. _profiling: + +************************* +Performance and Profiling +************************* + +============== +Today's topics +============== + +.. rst-class:: left medium + + - Determining performance objectives + - Measuring performance a.k.a. profiling + - Performance optimizations + +What is Software Profiling +-------------------------- + +The act of using instrumentation to objectively measure the performance +of your application + +"Performance" can be a measure of any of the following: + +- resource use (CPU, memory) +- frequency or duration of function calls +- wall clock execution time of part or all of your application + +Collecting this data involves instrumentating the code. In Python, this +happens at runtime. + +The instrumentation creates overhead, so it has a performance cost + +The output data (a "profile") will be a statistical summary of the +execution of functions + +An optimization strategy +------------------------ + +#. Write the code for maintainability / readability +#. Test for correctness +#. Collect profile data +#. If it is fast enough, quit. Your job here is done. +#. Else optimize the most expensive parts based on profiling data +#. Repeat from 2) + + +.. nextslide:: + +Programmers waste enormous amounts of time thinking about, or +worrying about, the speed of noncritical parts of their programs, +and these attempts at efficiency actually have a strong negative +impact when debugging and maintenance are considered. We should +forget about small efficiencies, say about 97% of the time: + +*premature optimization is the root of all evil.* + +--Donald Knuth + +http://c2.com/cgi/wiki?PrematureOptimization + +http://c2.com/cgi/wiki?ProfileBeforeOptimizing + + +Steps to better performance +--------------------------- + +(In order of importance) + +#. Efficient Algorithms (big O, etc...) +#. Appropriate Python data types, etc. +#. Appropriate Python style +#. Specialized packages (numpy, scipy) +#. Calling external packages +#. Extending with C/C++/Fortran/Cython + + +Big O notation +-------------- + +The efficiency of an algorithm is often described in “big O” notation. + +The letter O is used because the growth rate of a function is also +referred to as Order of the function + +It describes how an algorithm behaves in terms of resource use as a +function of amount of input data + +.. nextslide:: + +O(1) - (Constant performance) Execution time stays constant regardless of how much data is supplied + +- Example: adding to a dict + +O(n) - Time goes up linearly with number of items. + +- Example: scanning lists + +O(n\ :sup:`2`) - Time goes up quadratically with number of items. + +- Example: bubble sort, worst case + +O(log(n)) - goes up with the log of number of items + +- Example: bisection search + + +.. nextslide:: + +.. image:: images/big_o.png +.. :align: right +.. :height: 450px + :alt: big O notation plot + +.. nextslide:: + +**log?** you expect me to remember that math??? + +Let's think about that a bit.... + +Anyone know what a bisection search is? + +Why is that O(log(n))? + +| + +Reference: + +https://wiki.python.org/moin/TimeComplexity + + +Measuring time with a stopwatch +------------------------------- + +One way to measure performance is with a stopwatch. + +Start the clock when a unit of code such as a function begins, and stop +it when the code returns + +This is a the simplest method, and we can instrument our code to start +and stop the clock. + +Like most timing benchmarks, data obtained is valid only for the +particular test environment (machine/OS/Python version..) + +Relative timings may be valid across systems, but can also diverge + +For instance a run on a machine with fast network and slow disk may +produce much different results on a system with slow network and fast +disk + +``time.clock()`` / ``time.time()`` +---------------------------------- + +Using the time module as a profiling decorator + +``time.time()`` returns the unix system time (wall clock time) + +``time.clock()`` returns the CPU time of the current process + +Precision is system dependent + +Quite course, but can capture the big picture + +See ``Examples/profiling/timer/timer_test.py`` + +.. nextslide:: + +.. code-block:: python + + import time + + def timer(func): + def timer(*args, **kwargs): + """a decorator which prints execution time of the decorated function""" + t1 = time.time() + result = func(*args, **kwargs) + t2 = time.time() + print("-- executed %s in %.4f seconds" % (func.func_name, (t2 - t1))) + return result + return timer + + @timer + def expensive_function(): + time.sleep(1) + + @timer + def less_expensive_function(): + time.sleep(.02) + + expensive_function() + less_expensive_function() + +timeit +------ + +Used for testing small bits of code + +Use to test hypotheses about efficiency of algorithms and Python idioms + +Will run the given statement many times and calculate the average +execution time + +Can be run from the command line: + +.. code-block:: python + + python -m timeit '"-".join(str(n) for n in range(100))' + +https://docs.python.org/3.5/library/timeit.html + +See the ``timeit.py`` source: + +https://hg.python.org/cpython/file/3.5/Lib/timeit.py + +``timeit`` command line interface +--------------------------------- + +options + +- ``-nN``: execute the given statement N times in a loop. If this value is + not given, a fitting value is chosen. +- ``-rR``: repeat the loop iteration R times and take the best result. + Default: 3 +- ``-t``: use time.time to measure the time, which is the default on Unix. + This function measures wall time. +- ``-c``: use time.clock to measure the time, which is the default on + Windows and measures wall time. On Unix, resource.getrusage is used + instead and returns the CPU user time. +- ``-pP``: use a precision of P digits to display the timing result. + Default: 3 + +.. code-block:: bash + + $ python -m timeit -n 1000 -t "len([x**2 for x in range(1000)])" + + +.. nextslide:: + +``timeit`` can also be imported as a module + +http://docs.python.org/3/library/timeit.html#timeit.timeit + +.. code-block:: python + + timeit.timeit(stmt='pass', + setup='pass', + timer=, + number=1000000) + +The setup kwarg contains a string of Python code to execute before the +loops start, so that code is not part of the test + +.. code-block:: python + + import timeit + statement = "char in text" + setup_code = """text = "sample string";char = "g" """ + timeit.timeit(statement, setup=setup_code) + + +``timeit`` via iPython magic +---------------------------- + +Note that all that setup_code stuff is kind of a pain. + +iPython has your back (again) + +.. code-block:: ipython + + %timeit pass + + u = None + %timeit u is None + + %timeit -r 4 u == None + + import time + %timeit -n1 time.sleep(2) + + %timeit -n 10000 "f" in "food" + +http://ipython.readthedocs.io/en/stable/interactive/magics.html?#magic-timeit + + +Exercise +-------- +We just tried determining if a character exists in a string: + +.. code-block:: python + + statement = "'f' in 'food'" + timeit.timeit(statement) + +Run timeit with an alternative statement: + +.. code-block:: python + + statement2 = "'food'.find('f') >= 0" + timeit.timeit(statement2) + +Which is faster? Why? + + +Getting more detailed with Profiling +------------------------------------ + +That kind of timing is only useful if you know what part of the code you want to optimize. + +But what if you know your program is "slow", but don't know where is is spending the time? + +**Do not Guess!** -- you will often be wrong, and you don't want to waste time optimizing the wrong thing. + +*Really* -- even very experienced programmers are often wrong about where the bottlenecks are. + +You really need to profile to be sure. + +Also: take into account the entire run-time: does it make sense to optimize an initialization routine that takes a few seconds before a multi-hour run? + +.. nextslide:: + +A profiler takes measurements of runtime performance and summarizes results into a profile report + +Reported metrics could include + +- Memory used over time +- Memory allocated per function +- Frequency of function calls +- Duration of function calls +- Cumulative time spent in subfunction calls + +Python's built-in profilers +--------------------------- + +Python comes with a couple profiling modules + +- profile - older, pure Python. If you need to extend the profiler, + this might be good. Otherwise, it's slow. + +- cProfile - same API as profile, but written in C for less overhead + +**You almost always want to use ``cProfile``** + +https://docs.python.org/3.5/library/profile.html + + +cProfile +-------- + +Can be run as a module on an entire application + +.. code-block:: bash + + python -m cProfile [-o output_file] [-s sort_order] read_bna.py + 11111128 function calls in 8.283 seconds + Ordered by: standard name + + ncalls tottime percall cumtime percall filename:lineno(function) + 1 0.000 0.000 0.000 0.000 integrate.py:1() + 11111110 2.879 0.000 2.879 0.000 integrate.py:1(f) + [....] + +- ncalls: number of calls +- tottime: total time spent in function, excluding time in sub-functions +- percall: tottime / ncalls +- cumtime: total time spent in function, including time in sub-functions +- percall: cumtime / ncalls +- filename:lineno -- location of function + + +Analyzing profile data +---------------------- + +Output to a binary dump with -o + +While doing performance work, save your profiles for comparison later + +This helps ensure that any changes do actually increase performance + +A profile dump file can be read with ``pstats`` + +.. code-block:: bash + + python -m pstats + +Gives you a command line interface + +(help for help...) + +``pstats`` +---------- + +.. code-block:: python + + python -m cProfile -o prof_dump ./read_bna.py + python -m pstats + % read prof_dump + + # show stats: + prof_dump% stats + + # only the top 5 results: + prof_dump% stats 5 + + # sort by cumulative time: + prof_dump% sort cumulative + + # shorten long filenames for display: + prof_dump% strip + # show results again: + prof_dump% stats 5 + +.. nextslide:: + +pstats also has method calls: + +.. code-block:: python + + import pstats + p = pstats.Stats('prof_dump') + p.sort_stats('calls', 'cumulative') + p.print_stats() + + # Output can be restricted via arguments to print_stats(). + # Each restriction is either an integer (to select a count of lines), + # a decimal fraction between 0.0 and 1.0 inclusive (to select a percentage of lines), + # or a regular expression (to pattern match the standard name that is printed. + # If several restrictions are provided, then they are applied sequentially. + + +Analyzing profile data +---------------------- + +Inspect only your local code with regular expression syntax: + +.. code-block:: python + + import pstats + prof = pstats.Stats('prof_dump') + prof.sort_stats('cumulative') + prof.print_stats('^./[a-z]*.py:') + +I tend to write little scripts like this so I don't have to remember the commands. + +Exercise / Example +------------------ + +Real world example: + +``Examples/profiling/bna_reader/read_bna.py`` + +BNA is a (old) text file format for holding geospatial data. + +We were using some old code of mine that read these files, generated an internal data structure of polygons, and rendered them to a PNG. + +As these files got big -- this process started getting really slow. + +I had already optimized the file reading code a lot -- so could we do better? + + - I assumed not + +One of my team ran the profiler and identified the bottleneck -- and yes -- we could do better -- a lot. + +Let's try that out now. + + +============================ +Some other tools to consider +============================ + +.. rst-class:: left + For better visualizing + + For C extensions + + For memory Profiling + + +SNAKEVIZ +-------- + +A graphical profile viewer for Python + +https://jiffyclub.github.io/snakeviz/ + +:: + + pip install snakeviz + +Inspired by "Run Snake Run": http://www.vrplumber.com/programming/runsnakerun/ + +(which only works with Python 2.* for now) + +.. image:: images/snakeviz.png +.. :align: right +.. :height: 450px + :alt: snakeviz visualization + + + +line profiler +------------- + +Thus far, we've seen how to collect data on the performance of functions +as atomic units + +``line_profiler`` is a module for doing line-by-line profiling of functions + +``line_profiler`` ships with its own profiler, ``kernprof.py``. + +Enable line-by-line profiling with -l + +Decorate the function you want to profile with ``@profile`` and run + +.. code-block:: bash + + # the -v option will display the profile data immediately, instead + # of just writing it to .lprof + $ kernprof -l -v integrate_main.py + + # load the output with + $ python -m line_profiler integrate_main.py.lprof + + +https://github.com/rkern/line_profiler + +qcachegrind / kcachegrind +------------------------- + +profiling tool based on Valgrind: + +http://kcachegrind.sourceforge.net/html/Valgrind.html + +a runtime instrumentation framework for Linux/x86 + +Can be used with Python profile data with a profile format conversion + +Doesn't give all the information that a native valgrind run would +provide + +.. code-block:: python + + # convert python profile to calltree format + pip install pyprof2calltree + + python -m cProfile -o dump.profile integrate_main.py + pyprof2calltree -i dump.profile -o dump.callgrind + + +http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindCalltreeFormat + + +Profiling C extensions +---------------------- + +Google Performance Tools: + +https://code.google.com/p/gperftools/ + +can be used to profile C extensions + +Just call ProfilerStart and ProfilerStop with ctypes around the code you +want to profile + +.. nextslide:: + +.. code-block:: python + + import ctypes + + libprof = ctypes.CDLL('/usr/local/lib/libprofiler.0.dylib') + libprof.ProfilerStart('/tmp/out.prof') + import numpy + a=numpy.linspace(0,100) + a*=32432432 + libprof.ProfilerStop('/tmp/out.prof') + +.. code-block:: bash + + # convert the profile to qcachegrind's format with google's pprof tool + $ pprof --callgrind ~/virtualenvs/uwpce/lib/python2.7/site-packages/numpy/core/multiarray.so out.prof > output.callgrind + $ qcachegrind output.callgrind + +memory profilers +---------------- + +There aren't any great ones + +One option is heapy, which comes with Guppy, a Python environment for +memory profiling + +.. code-block:: python + + from guppy import hpy; hp=hpy() + hp.doc.heap + hp.heap() + %run define.py Robot + hp.heap() + +Others: + +https://pypi.python.org/pypi/memory_profiler + +http://mg.pov.lt/objgraph/ + +https://launchpad.net/meliae + +http://pythonhosted.org/Pympler/muppy.html + +http://jmdana.github.io/memprof/ + +============================ +Boosting Python performance +============================ + +There are ways to better structure your Python code to improve performance + +A few key approaches +-------------------- + +- Overhead in function/method runtime lookup can be significant for + small frequent calls. + +- inlining code or caching function references might help. See + ``examples/data_aggregation/agg.py`` + +- Python string handling idioms: use ``"".join(list_of_strings)`` rather + than sequential calls to += See ``examples/strings/str_concat.py`` and + ``str_comprehensions.py`` + +- using list comprehensions, generator expressions, ``or map()`` instead of + for loops can be faster (see ``data_aggregation/loops.py``) + +- Leverage existing domain specific C extension libraries, for instance + Numpy for fast array operations. + +- Rewrite expensive code as C modules. Use ctypes, Cython, SWIG, ... + +http://wiki.python.org/moin/PythonSpeed/PerformanceTips/ + + +Managing memory +--------------- + +Don’t forget memory: + +Processors are fast + +It can take longer to push the memory around than do the computation + +So keep in in mind for big data sets: + +Use the right data structures + +Use efficient algorithms + +Use generators and iterators, rather than lists. + +Use iterators to pull in the data you need from databases, sockets, +files, ... + + +Distraction: pyGame +------------------- + +There is a nice profiling example that uses PyGame: + +http://www.pygame.org/hifi.html + +Which you can install from binaries: + +Windows: +http://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame + +(you want the wheel file for the python you are running: probably cp35) + +Anaconda Python: + +First install miniconda. Then you can install pygame from anaconda.org. + +https://anaconda.org/cogsci + + +A more complex profile +---------------------- + +The amount of data in the previous example is readable, so now we'll +look at the output from a more complex application: +examples/profiling/pygame/swarm.py + +This program consists of calculating the gravitational acceleration of +bodies around a central mass and displaying them + +There are two major consumers of resources: one is our own code +calculating the physics, the other is pygame drawing the results on the +screen + +Our goal is to figure out whether the major bottleneck is in our own +logic or in the pygame operations + +A simple way to get data for our own code is + +.. code-block:: python + + python -m cProfile swarm.py &> /tmp/output.txt + grep swarm.py /tmp/output.txt + + + + + +Questions? +---------- diff --git a/_sources/profiling.txt b/_sources/profiling.txt new file mode 100644 index 0000000..401ae5a --- /dev/null +++ b/_sources/profiling.txt @@ -0,0 +1,713 @@ +.. _profiling: + +************************* +Performance and Profiling +************************* + +============== +Today's topics +============== + +.. rst-class:: left medium + + - Determining performance objectives + - Measuring performance a.k.a. profiling + - Performance optimizations + +What is Software Profiling +-------------------------- + +The act of using instrumentation to objectively measure the performance +of your application + +"Performance" can be a measure of any of the following: + +- resource use (CPU, memory) +- frequency or duration of function calls +- wall clock execution time of part or all of your application + +Collecting this data involves instrumentating the code. In Python, this +happens at runtime. + +The instrumentation creates overhead, so it has a performance cost + +The output data (a "profile") will be a statistical summary of the +execution of functions + +An optimization strategy +------------------------ + +#. Write the code for maintainability / readability +#. Test for correctness +#. Collect profile data +#. If it is fast enough, quit. Your job here is done. +#. Else optimize the most expensive parts based on profiling data +#. Repeat from 2) + + +.. nextslide:: + +Programmers waste enormous amounts of time thinking about, or +worrying about, the speed of noncritical parts of their programs, +and these attempts at efficiency actually have a strong negative +impact when debugging and maintenance are considered. We should +forget about small efficiencies, say about 97% of the time: + +*premature optimization is the root of all evil.* + +--Donald Knuth + +http://c2.com/cgi/wiki?PrematureOptimization + +http://c2.com/cgi/wiki?ProfileBeforeOptimizing + + +Steps to better performance +--------------------------- + +(In order of importance) + +#. Efficient Algorithms (big O, etc...) +#. Appropriate Python data types, etc. +#. Appropriate Python style +#. Specialized packages (numpy, scipy) +#. Calling external packages +#. Extending with C/C++/Fortran/Cython + + +Big O notation +-------------- + +The efficiency of an algorithm is often described in “big O” notation. + +The letter O is used because the growth rate of a function is also +referred to as Order of the function + +It describes how an algorithm behaves in terms of resource use as a +function of amount of input data + +.. nextslide:: + +O(1) - (Constant performance) Execution time stays constant regardless of how much data is supplied + +- Example: adding to a dict + +O(n) - Time goes up linearly with number of items. + +- Example: scanning lists + +O(n\ :sup:`2`) - Time goes up quadratically with number of items. + +- Example: bubble sort, worst case + +O(log(n)) - goes up with the log of number of items + +- Example: bisection search + + +.. nextslide:: + +.. image:: images/big_o.png +.. :align: right +.. :height: 450px + :alt: big O notation plot + +.. nextslide:: + +**log?** you expect me to remember that math??? + +Let's think about that a bit.... + +Anyone know what a bisection search is? + +Why is that O(log(n))? + +| + +Reference: + +https://wiki.python.org/moin/TimeComplexity + + +Measuring time with a stopwatch +------------------------------- + +One way to measure performance is with a stopwatch. + +Start the clock when a unit of code such as a function begins, and stop +it when the code returns + +This is a the simplest method, and we can instrument our code to start +and stop the clock. + +Like most timing benchmarks, data obtained is valid only for the +particular test environment (machine/OS/Python version..) + +Relative timings may be valid across systems, but can also diverge + +For instance a run on a machine with fast network and slow disk may +produce much different results on a system with slow network and fast +disk + +``time.clock()`` / ``time.time()`` +---------------------------------- + +Using the time module as a profiling decorator + +``time.time()`` returns the unix system time (wall clock time) + +``time.clock()`` returns the CPU time of the current process + +Precision is system dependent + +Quite course, but can capture the big picture + +See ``Examples/profiling/timer/timer_test.py`` + +.. nextslide:: + +.. code-block:: python + + import time + + def timer(func): + def timer(*args, **kwargs): + """a decorator which prints execution time of the decorated function""" + t1 = time.time() + result = func(*args, **kwargs) + t2 = time.time() + print("-- executed %s in %.4f seconds" % (func.func_name, (t2 - t1))) + return result + return timer + + @timer + def expensive_function(): + time.sleep(1) + + @timer + def less_expensive_function(): + time.sleep(.02) + + expensive_function() + less_expensive_function() + +timeit +------ + +Used for testing small bits of code + +Use to test hypotheses about efficiency of algorithms and Python idioms + +Will run the given statement many times and calculate the average +execution time + +Can be run from the command line: + +.. code-block:: python + + python -m timeit '"-".join(str(n) for n in range(100))' + +https://docs.python.org/3.5/library/timeit.html + +See the ``timeit.py`` source: + +https://hg.python.org/cpython/file/3.5/Lib/timeit.py + +``timeit`` command line interface +--------------------------------- + +options + +- ``-nN``: execute the given statement N times in a loop. If this value is + not given, a fitting value is chosen. +- ``-rR``: repeat the loop iteration R times and take the best result. + Default: 3 +- ``-t``: use time.time to measure the time, which is the default on Unix. + This function measures wall time. +- ``-c``: use time.clock to measure the time, which is the default on + Windows and measures wall time. On Unix, resource.getrusage is used + instead and returns the CPU user time. +- ``-pP``: use a precision of P digits to display the timing result. + Default: 3 + +.. code-block:: bash + + $ python -m timeit -n 1000 -t "len([x**2 for x in range(1000)])" + + +.. nextslide:: + +``timeit`` can also be imported as a module + +http://docs.python.org/3/library/timeit.html#timeit.timeit + +.. code-block:: python + + timeit.timeit(stmt='pass', + setup='pass', + timer=, + number=1000000) + +The setup kwarg contains a string of Python code to execute before the +loops start, so that code is not part of the test + +.. code-block:: python + + import timeit + statement = "char in text" + setup_code = """text = "sample string";char = "g" """ + timeit.timeit(statement, setup=setup_code) + + +``timeit`` via iPython magic +---------------------------- + +Note that all that setup_code stuff is kind of a pain. + +iPython has your back (again) + +.. code-block:: ipython + + %timeit pass + + u = None + %timeit u is None + + %timeit -r 4 u == None + + import time + %timeit -n1 time.sleep(2) + + %timeit -n 10000 "f" in "food" + +http://ipython.readthedocs.io/en/stable/interactive/magics.html?#magic-timeit + + +Exercise +-------- +We just tried determining if a character exists in a string: + +.. code-block:: python + + statement = "'f' in 'food'" + timeit.timeit(statement) + +Run timeit with an alternative statement: + +.. code-block:: python + + statement2 = "'food'.find('f') >= 0" + timeit.timeit(statement2) + +Which is faster? Why? + + +Getting more detailed with Profiling +------------------------------------ + +That kind of timing is only useful if you know what part of the code you want to optimize. + +But what if you know your program is "slow", but don't know where is is spending the time? + +**Do not Guess!** -- you will often be wrong, and you don't want to waste time optimizing the wrong thing. + +*Really* -- even very experienced programmers are often wrong about where the bottlenecks are. + +You really need to profile to be sure. + +Also: take into account the entire run-time: does it make sense to optimize an initialization routine that takes a few seconds before a multi-hour run? + +.. nextslide:: + +A profiler takes measurements of runtime performance and summarizes results into a profile report + +Reported metrics could include + +- Memory used over time +- Memory allocated per function +- Frequency of function calls +- Duration of function calls +- Cumulative time spent in subfunction calls + +Python's built-in profilers +--------------------------- + +Python comes with a couple profiling modules + +- profile - older, pure Python. If you need to extend the profiler, + this might be good. Otherwise, it's slow. + +- cProfile - same API as profile, but written in C for less overhead + +**You almost always want to use ``cProfile``** + +https://docs.python.org/3.5/library/profile.html + + +cProfile +-------- + +Can be run as a module on an entire application + +.. code-block:: bash + + python -m cProfile [-o output_file] [-s sort_order] read_bna.py + 11111128 function calls in 8.283 seconds + Ordered by: standard name + + ncalls tottime percall cumtime percall filename:lineno(function) + 1 0.000 0.000 0.000 0.000 integrate.py:1() + 11111110 2.879 0.000 2.879 0.000 integrate.py:1(f) + [....] + +- ncalls: number of calls +- tottime: total time spent in function, excluding time in sub-functions +- percall: tottime / ncalls +- cumtime: total time spent in function, including time in sub-functions +- percall: cumtime / ncalls +- filename:lineno -- location of function + + +Analyzing profile data +---------------------- + +Output to a binary dump with -o + +While doing performance work, save your profiles for comparison later + +This helps ensure that any changes do actually increase performance + +A profile dump file can be read with ``pstats`` + +.. code-block:: bash + + python -m pstats + +Gives you a command line interface + +(help for help...) + +``pstats`` +---------- + +.. code-block:: python + + python -m cProfile -o prof_dump ./read_bna.py + python -m pstats + % read prof_dump + + # show stats: + prof_dump% stats + + # only the top 5 results: + prof_dump% stats 5 + + # sort by cumulative time: + prof_dump% sort cumulative + + # shorten long filenames for display: + prof_dump% strip + # show results again: + prof_dump% stats 5 + +.. nextslide:: + +pstats also has method calls: + +.. code-block:: python + + import pstats + p = pstats.Stats('prof_dump') + p.sort_stats('calls', 'cumulative') + p.print_stats() + + # Output can be restricted via arguments to print_stats(). + # Each restriction is either an integer (to select a count of lines), + # a decimal fraction between 0.0 and 1.0 inclusive (to select a percentage of lines), + # or a regular expression (to pattern match the standard name that is printed. + # If several restrictions are provided, then they are applied sequentially. + + +Analyzing profile data +---------------------- + +Inspect only your local code with regular expression syntax: + +.. code-block:: python + + import pstats + prof = pstats.Stats('prof_dump') + prof.sort_stats('cumulative') + prof.print_stats('^./[a-z]*.py:') + +I tend to write little scripts like this so I don't have to remember the commands. + +Exercise / Example +------------------ + +Real world example: + +``Examples/profiling/bna_reader/read_bna.py`` + +BNA is a (old) text file format for holding geospatial data. + +We were using some old code of mine that read these files, generated an internal data structure of polygons, and rendered them to a PNG. + +As these files got big -- this process started getting really slow. + +I had already optimized the file reading code a lot -- so could we do better? + + - I assumed not + +One of my team ran the profiler and identified the bottleneck -- and yes -- we could do better -- a lot. + +Let's try that out now. + + +============================ +Some other tools to consider +============================ + +.. rst-class:: left + For better visualizing + + For C extensions + + For memory Profiling + + +SNAKEVIZ +-------- + +A graphical profile viewer for Python + +https://jiffyclub.github.io/snakeviz/ + +:: + + pip install snakeviz + +Inspired by "Run Snake Run": http://www.vrplumber.com/programming/runsnakerun/ + +(which only works with Python 2.* for now) + +.. image:: images/snakeviz.png +.. :align: right +.. :height: 450px + :alt: snakeviz visualization + + + +line profiler +------------- + +Thus far, we've seen how to collect data on the performance of functions +as atomic units + +``line_profiler`` is a module for doing line-by-line profiling of functions + +``line_profiler`` ships with its own profiler, ``kernprof.py``. + +Enable line-by-line profiling with -l + +Decorate the function you want to profile with ``@profile`` and run + +.. code-block:: bash + + # the -v option will display the profile data immediately, instead + # of just writing it to .lprof + $ kernprof -l -v integrate_main.py + + # load the output with + $ python -m line_profiler integrate_main.py.lprof + + +https://github.com/rkern/line_profiler + +qcachegrind / kcachegrind +------------------------- + +profiling tool based on Valgrind: + +http://kcachegrind.sourceforge.net/html/Valgrind.html + +a runtime instrumentation framework for Linux/x86 + +Can be used with Python profile data with a profile format conversion + +Doesn't give all the information that a native valgrind run would +provide + +.. code-block:: python + + # convert python profile to calltree format + pip install pyprof2calltree + + python -m cProfile -o dump.profile integrate_main.py + pyprof2calltree -i dump.profile -o dump.callgrind + + +http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindCalltreeFormat + + +Profiling C extensions +---------------------- + +Google Performance Tools: + +https://code.google.com/p/gperftools/ + +can be used to profile C extensions + +Just call ProfilerStart and ProfilerStop with ctypes around the code you +want to profile + +.. nextslide:: + +.. code-block:: python + + import ctypes + + libprof = ctypes.CDLL('/usr/local/lib/libprofiler.0.dylib') + libprof.ProfilerStart('/tmp/out.prof') + import numpy + a=numpy.linspace(0,100) + a*=32432432 + libprof.ProfilerStop('/tmp/out.prof') + +.. code-block:: bash + + # convert the profile to qcachegrind's format with google's pprof tool + $ pprof --callgrind ~/virtualenvs/uwpce/lib/python2.7/site-packages/numpy/core/multiarray.so out.prof > output.callgrind + $ qcachegrind output.callgrind + +memory profilers +---------------- + +There aren't any great ones + +One option is heapy, which comes with Guppy, a Python environment for +memory profiling + +.. code-block:: python + + from guppy import hpy; hp=hpy() + hp.doc.heap + hp.heap() + %run define.py Robot + hp.heap() + +Others: + +https://pypi.python.org/pypi/memory_profiler + +http://mg.pov.lt/objgraph/ + +https://launchpad.net/meliae + +http://pythonhosted.org/Pympler/muppy.html + +http://jmdana.github.io/memprof/ + +============================ +Boosting Python performance +============================ + +There are ways to better structure your Python code to improve performance + +A few key approaches +-------------------- + +- Overhead in function/method runtime lookup can be significant for + small frequent calls. + +- inlining code or caching function references might help. See + ``examples/data_aggregation/agg.py`` + +- Python string handling idioms: use ``"".join(list_of_strings)`` rather + than sequential calls to += See ``examples/strings/str_concat.py`` and + ``str_comprehensions.py`` + +- using list comprehensions, generator expressions, ``or map()`` instead of + for loops can be faster (see ``data_aggregation/loops.py``) + +- Leverage existing domain specific C extension libraries, for instance + Numpy for fast array operations. + +- Rewrite expensive code as C modules. Use ctypes, Cython, SWIG, ... + +http://wiki.python.org/moin/PythonSpeed/PerformanceTips/ + + +Managing memory +--------------- + +Don’t forget memory: + +Processors are fast + +It can take longer to push the memory around than do the computation + +So keep in in mind for big data sets: + +Use the right data structures + +Use efficient algorithms + +Use generators and iterators, rather than lists. + +Use iterators to pull in the data you need from databases, sockets, +files, ... + + +Distraction: pyGame +------------------- + +There is a nice profiling example that uses PyGame: + +http://www.pygame.org/hifi.html + +Which you can install from binaries: + +Windows: +http://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame + +(you want the wheel file for the python you are running: probably cp35) + +Anaconda Python: + +First install miniconda. Then you can install pygame from anaconda.org. + +https://anaconda.org/cogsci + + +A more complex profile +---------------------- + +The amount of data in the previous example is readable, so now we'll +look at the output from a more complex application: +examples/profiling/pygame/swarm.py + +This program consists of calculating the gravitational acceleration of +bodies around a central mass and displaying them + +There are two major consumers of resources: one is our own code +calculating the physics, the other is pygame drawing the results on the +screen + +Our goal is to figure out whether the major bottleneck is in our own +logic or in the pygame operations + +A simple way to get data for our own code is + +.. code-block:: python + + python -m cProfile swarm.py &> /tmp/output.txt + grep swarm.py /tmp/output.txt + + + + + +Questions? +---------- diff --git a/_sources/scipy.rst.txt b/_sources/scipy.rst.txt new file mode 100644 index 0000000..4800a80 --- /dev/null +++ b/_sources/scipy.rst.txt @@ -0,0 +1,581 @@ +.. _scipy: + +###################### +Intro to Numpy / Scipy +###################### + + +Scipy +===== + +.. rst-class:: left + + The scipy "Stack" is a collection of core packages used for scientific / numerical computing. + + http://www.scipy.org/stackspec.html + + Many other domain-specific packages area available: The core "stack" is what most people will want, regardless of domain. + +What's in the scipy stack? +-------------------------- + +* Python (http://www.python.org) +* NumPy (http://www.numpy.org) +* SciPy library (http://www.scipy.org) +* Matplotlib (http://matplotlib.org/) +* IPython (http://ipython.org/) +* (now Jupyter: http://jupyter.org/) + +| + +* nose (https://nose.readthedocs.org) +* pandas (http://pandas.pydata.org/) +* Sympy (http://sympy.org/) + +Learning Resources +------------------ + +There are a lot of tutorials, documentation, etc. out there. In this class, we only have a couple hours, so won't get that far. Here are some nice options: + +http://scipy-lectures.github.io/ + +https://github.com/SciTools/courses/blob/master/README.md + +https://github.com/jrjohansson/scientific-python-lectures + +(note: this one does ``import *`` -- don't do that!) + +https://github.com/WeatherGod/AnatomyOfMatplotlib + +http://wiki.scipy.org/Tentative_NumPy_Tutorial + +For those familiar with MATLAB: + +http://wiki.scipy.org/NumPy_for_Matlab_Users + +The Jupyter "notebook" +----------------------- + +We've been using iPython a lot in this class (at least I have) + +It provides a great interactive environment for testing and running +Python code. + +It turns out it has antoher interface: the "notebook" + +The notebook provides a way to intersperse little chunks of code, and text, and images, etc... + +It runs in a browser, you start it up with: + +.. code-block:: bash + + jupyter notebook + +It should start your browser, and show you the notebooks in the dir you started it up in. + +I'll be using it for lots of demos in this class. + +Jupyter +------- + +Let's get the Jupyter notebook installed and running. + +Hopefully:: + + pip install jupyter + +Will do it. + +Then:: + + jupyter notebook + +Should start it up. + + +numpy +===== + +.. rst-class:: left + + numpy is the core package that the rest of the scipy stack is built on. + numpy is really the core of everything. + + All the rest requires an understanding good understanding of what a numpy array is -- so that's mostly what I'll talk about today. + +So what is numpy? +----------------- + +Not just for lots of numbers! +(but it's great for that!) + +http://www.numpy.org/ + + +1) An N-Dimensional array object + + - Really this ``ndarray`` is the core of it all + +2) A whole pile of tools for operations on/with that object. + + +Why numpy? +---------- + +Classic answer: Lots of numbers + + * Faster + * Less memory + * More data types + +Even if you don't have lot of numbers: + + * N-d array slicing + * Vector operations + * Flexible data types + + +Why numpy? +---------- + +Wrapper for a block of memory: + + * Interfacing with C libs + * PyOpenGL + * GDAL + * NetCDF4 + * Shapely + +Image processing: + + * PIL + * WxImage + * ndimage + + +This Talk +---------- + +There are a lot of tutorials and documentation out there. + +So I'm going to spend about an hour or so on the regular old "how do you use it" stuff. + +Then, I'm going to cover a bit about the guts and some advanced issues. + +This is harder to find explanations for -- and will help you understand what's really going on under the hood. + +NOTE: I've been using numpy and its predecessors for long time -- + +so have kind of forgotten what is obvious and what is not -- so: + +**Ask questions** as we go! + + +Getting started +================ + +.. rst-class:: left + + Example code is in the class repo: + + ``/Examples/numpy`` + + Those are a bunch of ipython notebooks. + + Get your command line into that dir, then start up the iPyhton notebook: + + ``$ ipython notebook`` + + This should fie up your browser, and give you a list of notebooks to choose from. + +Array Constructors: +------------------- + +How do you make an array? + +From scratch: ``ones(), zeros(), empty(), arange(), linspace(), logspace()`` + +( Default dtype: ``np.float64`` ) + +From sequences: ``array(), asarray()`` ( Build from any sequence ) + +From binary data: ``fromstring(), frombuffer(), fromfile()`` + +Assorted linear algebra standards: ``eye(), diag()``, etc. + +demo: ``constructors.ipynb`` + + +Indexing and slicing +-------------------- + +How do you get parts of the array out? + +Indexing and slicing much like regular python sequences, but extended to multi-dimensions. + +However: a slice is a "view" on the array -- new object, but shares memory: + +demo: ``slice.ipynb`` + + +Reshaping: +----------- + +numpy arrays have a particular shape. + +But they are really wrappers around a block of data + +So they can be re-shaped -- same data, arranged differently + +demo: ``reshaping.ipynb`` + + +Broadcasting: +------------- + +Element-wise operations among two different rank arrays: + +This is the key power of numpy! + +Simple case: scalar and array: + +.. code-block:: ipython + + In [37]: a + Out[37]: array([1, 2, 3]) + In [38]: a*3 + Out[38]: array([3, 6, 9]) + + +Great for functions of more than one variable on a grid + +code is more concise and runs much faster + +demo: ``broadcasting.ipynb`` + +Fancy Indexing +-------------- + +As we've seen, you can slice and dice ndarrays much like regular python sequences. + +This model is extended to multiple dimensions. + +But it still only lets you extract rectangular blocks of elements. + +For more complex sub-selection: we use "fancy indexing": + +demo: ``fancy_indexing.ipynb`` + + +What is an nd array under the hood? +----------------------------------- + + * N-dimensional (up to 32!) + + * Homogeneous array: + + - Every element is the same type + + (but that type can be a pyObject) + + - Int, float, char -- more exotic types + + - "rank" – number of dimensions + + * Strided data: + + - Describes how to index into block of memory + + - PEP 3118 -- Revising the buffer protocol + + +demo: ``memory_struct.ipynb`` + + +Built-in Data Types +------------------- + +* Signed and unsigned Integers + + - 8, 16, 32, 64 bits + +* Floating Point + + - 32, 64, 96, 128 bits (not all platforms) + +* Complex + + - 64, 128, 192, 256 bits + +* String and unicode + + - Static length + +* Bool -- 8 bit + +* Python Object + + - Really a pointer + +demo: ``object.ipynb`` + + +Text File I/O +-------------- + +Loading from text (CSV, etc): + +* ``np.loadtxt`` +* ``np.genfromtxt`` ( a few more features, and a bit slower ) + +Saving as text (CSV): + +* ``np.savetxt()`` + +Compound dtypes +--------------- + +* Can define any combination of other types + + - Still Homogeneous: Array of structs. + +* Can name the fields + +* Can be like a database table + +* Useful for reading binary data + + +demo: ``dtypes.ipynb`` + + +Numpy Persistence: +------------------ + + * ``np.tofile() / np.fromfile()`` + + - Just the raw bytes, no metadata + + * ``pickle`` + + * ``np.savez()`` -- numpy zip format + + - Compact: binary dump plus metadata + + * netcdf + + - NetCDF4 (https://github.com/Unidata/netcdf4-python) + + * Hdf + + - h5py: http://www.h5py.org/ + + - pytables: http://www.pytables.org/ + + +Stride Tricks +-------------- + +numpy arrays are really wrappers about "strided data" + +This means that there is a single linear block of memory with the +values in it. + +The "strides" describe how that data is arranged to look like an array of more dimensions: 2D, 3D, 4D etc. + +Mostly, numpy handles all this under the hood for you, so you can logically work with the data as though it were multi-dimensional. + +But you can actually manipulate the description of the data, so that it "acts" like it is arranged differently than it is: + +``stride_tricks.ipynb`` + + +Working with compiled code +--------------------------- + + * Some code can't be vectorized + * Interface with existing libraries + +numpy arrays are essentially a wrapper around a C pointer to a block of data -- some tools: + + * C API: you don't want to do that! + * Cython: typed arrays + * Ctypes + * SWIG: numpy.i + * Boost: boost array + * f2py + +We'll get into this more in a later class... + +Example of numpy+cython: https://github.com/cython/cython/wiki/examples-mandelbrot + +Other stuff: +------------ + + * Masked arrays + * Memory-mapped files + * Set operations: unique, etc + * Random numbers + * Polynomials + * FFT + * Sorting and searching + * Linear Algebra + * Statistics + +numpy docs: +----------- + +www.numpy.org + + - Numpy reference Downloads, etc + +www.scipy.org + + - lots of docs + +Scipy cookbook: + + - http://www.scipy.org/Cookbook + +"The Numpy Book" + +http://csc.ucdavis.edu/~chaos/courses/nlp/Software/NumPyBook.pdf + +(old, but written by the primary author -- key stuff in there) + +matplotlib +========== + +.. rst-class:: left + + Matplotlib is the most common plotting library for python. + + * Powerful + * Flexible + * Publication quality + * Primarily 2d graphics (some 3d) + + See the Gallery here: + + http://matplotlib.org/gallery.html + +matplotlib APIs +------------------- + +Matplotlib has essentially 2 different (but related) APIs: + +The "pylab" API: + + * Derived from the MATLAB API, and most suitable for interactive use + +The Object Oriented API: + + * reflects the underlying OO structure of matplolib + * more "pythonic" + * much better suited to embedding plotting in applications + * better suited to re-using code + +I'll introduce the OO API, but you will see a LOT of example code using the interactive "pylab" interface. + +Fortunately, the concepts and most of the commands are the same. + +Tutorial +-------- + +We'll run through a simple tutorial in class: + +``SystemDevelopment2015/Examples/week-05-matplotlib`` + +There are "learner" and instructor notebooks in there. They are identical, but if you use the learner one you can mess with it and not mess up the main one... + +If you really want to use MPL, I suggest you run through a more thorough one to really get an idea how it all works: + +https://github.com/WeatherGod/AnatomyOfMatplotlib + +This one is pretty nice -- but would take the entire class... + +Using numpy arrays when computation isn't critical +-------------------------------------------------- + +numpy arrays are mostly about performance and memory use. + +But you still may want to use them for toher reasons. + +some data naturally is in 2-d or 3-d arrays. + +sometimes you need to work on a sub-view of the data as an independent object. + +For example: A Sudoko game: + + * The board is 9X9 + * Sub-divided into 3X3 squares + * And you need to examine the rows and columns + +Example: ``sudoku-chb.py`` + + +Pandas +======= + +.. rst-class:: left + + Python Data Analysis Library + + Pandas provides high-performance, easy-to-use data structures and data analysis tools for the Python programming language. + + Modeled after R's dataframe concept, it provides some pretty neat tools for doing simple statistical analysis and plotting of larg-ish data sets. + + It's particularly powerful for time series. + + ``http://pandas.pydata.org/`` + +Learning Pandas +---------------- + +The official documentation is excellent, including tutorials: + +http://pandas.pydata.org/pandas-docs/stable/ + +http://pandas.pydata.org/pandas-docs/stable/10min.html + +http://pandas.pydata.org/pandas-docs/stable/tutorials.html + +In addition, there are a large number of tutorials on the web: + +This one is oriented to folks familiar with SQL: + +http://www.gregreda.com/2013/10/26/intro-to-pandas-data-structures/ + +And this is a good one to get started quick: + +http://synesthesiam.com/posts/an-introduction-to-pandas.html + +We'll give that one a shot in class now... + +Scipy +===== + +.. rst-class:: left + + The scipy package itself is a large collection of cool stuff for scientific computing. ( http://docs.scipy.org/doc/scipy/reference/ ) + + You'll see there lots of stuff! If it's at all general purpose for computation, you're likely to find it there. + + Some of the most common sub-packages: + + * Special functions (scipy.special) + * Integration (scipy.integrate) + * Optimization (scipy.optimize) + * Interpolation (scipy.interpolate) + * Fourier Transforms (scipy.fftpack) + * Signal Processing (scipy.signal) + * Linear Algebra (scipy.linalg) + * Spatial data structures and algorithms (scipy.spatial) + * Statistics (scipy.stats) + * Multidimensional image processing (scipy.ndimage) + + + + + + diff --git a/_sources/scipy.txt b/_sources/scipy.txt new file mode 100644 index 0000000..4800a80 --- /dev/null +++ b/_sources/scipy.txt @@ -0,0 +1,581 @@ +.. _scipy: + +###################### +Intro to Numpy / Scipy +###################### + + +Scipy +===== + +.. rst-class:: left + + The scipy "Stack" is a collection of core packages used for scientific / numerical computing. + + http://www.scipy.org/stackspec.html + + Many other domain-specific packages area available: The core "stack" is what most people will want, regardless of domain. + +What's in the scipy stack? +-------------------------- + +* Python (http://www.python.org) +* NumPy (http://www.numpy.org) +* SciPy library (http://www.scipy.org) +* Matplotlib (http://matplotlib.org/) +* IPython (http://ipython.org/) +* (now Jupyter: http://jupyter.org/) + +| + +* nose (https://nose.readthedocs.org) +* pandas (http://pandas.pydata.org/) +* Sympy (http://sympy.org/) + +Learning Resources +------------------ + +There are a lot of tutorials, documentation, etc. out there. In this class, we only have a couple hours, so won't get that far. Here are some nice options: + +http://scipy-lectures.github.io/ + +https://github.com/SciTools/courses/blob/master/README.md + +https://github.com/jrjohansson/scientific-python-lectures + +(note: this one does ``import *`` -- don't do that!) + +https://github.com/WeatherGod/AnatomyOfMatplotlib + +http://wiki.scipy.org/Tentative_NumPy_Tutorial + +For those familiar with MATLAB: + +http://wiki.scipy.org/NumPy_for_Matlab_Users + +The Jupyter "notebook" +----------------------- + +We've been using iPython a lot in this class (at least I have) + +It provides a great interactive environment for testing and running +Python code. + +It turns out it has antoher interface: the "notebook" + +The notebook provides a way to intersperse little chunks of code, and text, and images, etc... + +It runs in a browser, you start it up with: + +.. code-block:: bash + + jupyter notebook + +It should start your browser, and show you the notebooks in the dir you started it up in. + +I'll be using it for lots of demos in this class. + +Jupyter +------- + +Let's get the Jupyter notebook installed and running. + +Hopefully:: + + pip install jupyter + +Will do it. + +Then:: + + jupyter notebook + +Should start it up. + + +numpy +===== + +.. rst-class:: left + + numpy is the core package that the rest of the scipy stack is built on. + numpy is really the core of everything. + + All the rest requires an understanding good understanding of what a numpy array is -- so that's mostly what I'll talk about today. + +So what is numpy? +----------------- + +Not just for lots of numbers! +(but it's great for that!) + +http://www.numpy.org/ + + +1) An N-Dimensional array object + + - Really this ``ndarray`` is the core of it all + +2) A whole pile of tools for operations on/with that object. + + +Why numpy? +---------- + +Classic answer: Lots of numbers + + * Faster + * Less memory + * More data types + +Even if you don't have lot of numbers: + + * N-d array slicing + * Vector operations + * Flexible data types + + +Why numpy? +---------- + +Wrapper for a block of memory: + + * Interfacing with C libs + * PyOpenGL + * GDAL + * NetCDF4 + * Shapely + +Image processing: + + * PIL + * WxImage + * ndimage + + +This Talk +---------- + +There are a lot of tutorials and documentation out there. + +So I'm going to spend about an hour or so on the regular old "how do you use it" stuff. + +Then, I'm going to cover a bit about the guts and some advanced issues. + +This is harder to find explanations for -- and will help you understand what's really going on under the hood. + +NOTE: I've been using numpy and its predecessors for long time -- + +so have kind of forgotten what is obvious and what is not -- so: + +**Ask questions** as we go! + + +Getting started +================ + +.. rst-class:: left + + Example code is in the class repo: + + ``/Examples/numpy`` + + Those are a bunch of ipython notebooks. + + Get your command line into that dir, then start up the iPyhton notebook: + + ``$ ipython notebook`` + + This should fie up your browser, and give you a list of notebooks to choose from. + +Array Constructors: +------------------- + +How do you make an array? + +From scratch: ``ones(), zeros(), empty(), arange(), linspace(), logspace()`` + +( Default dtype: ``np.float64`` ) + +From sequences: ``array(), asarray()`` ( Build from any sequence ) + +From binary data: ``fromstring(), frombuffer(), fromfile()`` + +Assorted linear algebra standards: ``eye(), diag()``, etc. + +demo: ``constructors.ipynb`` + + +Indexing and slicing +-------------------- + +How do you get parts of the array out? + +Indexing and slicing much like regular python sequences, but extended to multi-dimensions. + +However: a slice is a "view" on the array -- new object, but shares memory: + +demo: ``slice.ipynb`` + + +Reshaping: +----------- + +numpy arrays have a particular shape. + +But they are really wrappers around a block of data + +So they can be re-shaped -- same data, arranged differently + +demo: ``reshaping.ipynb`` + + +Broadcasting: +------------- + +Element-wise operations among two different rank arrays: + +This is the key power of numpy! + +Simple case: scalar and array: + +.. code-block:: ipython + + In [37]: a + Out[37]: array([1, 2, 3]) + In [38]: a*3 + Out[38]: array([3, 6, 9]) + + +Great for functions of more than one variable on a grid + +code is more concise and runs much faster + +demo: ``broadcasting.ipynb`` + +Fancy Indexing +-------------- + +As we've seen, you can slice and dice ndarrays much like regular python sequences. + +This model is extended to multiple dimensions. + +But it still only lets you extract rectangular blocks of elements. + +For more complex sub-selection: we use "fancy indexing": + +demo: ``fancy_indexing.ipynb`` + + +What is an nd array under the hood? +----------------------------------- + + * N-dimensional (up to 32!) + + * Homogeneous array: + + - Every element is the same type + + (but that type can be a pyObject) + + - Int, float, char -- more exotic types + + - "rank" – number of dimensions + + * Strided data: + + - Describes how to index into block of memory + + - PEP 3118 -- Revising the buffer protocol + + +demo: ``memory_struct.ipynb`` + + +Built-in Data Types +------------------- + +* Signed and unsigned Integers + + - 8, 16, 32, 64 bits + +* Floating Point + + - 32, 64, 96, 128 bits (not all platforms) + +* Complex + + - 64, 128, 192, 256 bits + +* String and unicode + + - Static length + +* Bool -- 8 bit + +* Python Object + + - Really a pointer + +demo: ``object.ipynb`` + + +Text File I/O +-------------- + +Loading from text (CSV, etc): + +* ``np.loadtxt`` +* ``np.genfromtxt`` ( a few more features, and a bit slower ) + +Saving as text (CSV): + +* ``np.savetxt()`` + +Compound dtypes +--------------- + +* Can define any combination of other types + + - Still Homogeneous: Array of structs. + +* Can name the fields + +* Can be like a database table + +* Useful for reading binary data + + +demo: ``dtypes.ipynb`` + + +Numpy Persistence: +------------------ + + * ``np.tofile() / np.fromfile()`` + + - Just the raw bytes, no metadata + + * ``pickle`` + + * ``np.savez()`` -- numpy zip format + + - Compact: binary dump plus metadata + + * netcdf + + - NetCDF4 (https://github.com/Unidata/netcdf4-python) + + * Hdf + + - h5py: http://www.h5py.org/ + + - pytables: http://www.pytables.org/ + + +Stride Tricks +-------------- + +numpy arrays are really wrappers about "strided data" + +This means that there is a single linear block of memory with the +values in it. + +The "strides" describe how that data is arranged to look like an array of more dimensions: 2D, 3D, 4D etc. + +Mostly, numpy handles all this under the hood for you, so you can logically work with the data as though it were multi-dimensional. + +But you can actually manipulate the description of the data, so that it "acts" like it is arranged differently than it is: + +``stride_tricks.ipynb`` + + +Working with compiled code +--------------------------- + + * Some code can't be vectorized + * Interface with existing libraries + +numpy arrays are essentially a wrapper around a C pointer to a block of data -- some tools: + + * C API: you don't want to do that! + * Cython: typed arrays + * Ctypes + * SWIG: numpy.i + * Boost: boost array + * f2py + +We'll get into this more in a later class... + +Example of numpy+cython: https://github.com/cython/cython/wiki/examples-mandelbrot + +Other stuff: +------------ + + * Masked arrays + * Memory-mapped files + * Set operations: unique, etc + * Random numbers + * Polynomials + * FFT + * Sorting and searching + * Linear Algebra + * Statistics + +numpy docs: +----------- + +www.numpy.org + + - Numpy reference Downloads, etc + +www.scipy.org + + - lots of docs + +Scipy cookbook: + + - http://www.scipy.org/Cookbook + +"The Numpy Book" + +http://csc.ucdavis.edu/~chaos/courses/nlp/Software/NumPyBook.pdf + +(old, but written by the primary author -- key stuff in there) + +matplotlib +========== + +.. rst-class:: left + + Matplotlib is the most common plotting library for python. + + * Powerful + * Flexible + * Publication quality + * Primarily 2d graphics (some 3d) + + See the Gallery here: + + http://matplotlib.org/gallery.html + +matplotlib APIs +------------------- + +Matplotlib has essentially 2 different (but related) APIs: + +The "pylab" API: + + * Derived from the MATLAB API, and most suitable for interactive use + +The Object Oriented API: + + * reflects the underlying OO structure of matplolib + * more "pythonic" + * much better suited to embedding plotting in applications + * better suited to re-using code + +I'll introduce the OO API, but you will see a LOT of example code using the interactive "pylab" interface. + +Fortunately, the concepts and most of the commands are the same. + +Tutorial +-------- + +We'll run through a simple tutorial in class: + +``SystemDevelopment2015/Examples/week-05-matplotlib`` + +There are "learner" and instructor notebooks in there. They are identical, but if you use the learner one you can mess with it and not mess up the main one... + +If you really want to use MPL, I suggest you run through a more thorough one to really get an idea how it all works: + +https://github.com/WeatherGod/AnatomyOfMatplotlib + +This one is pretty nice -- but would take the entire class... + +Using numpy arrays when computation isn't critical +-------------------------------------------------- + +numpy arrays are mostly about performance and memory use. + +But you still may want to use them for toher reasons. + +some data naturally is in 2-d or 3-d arrays. + +sometimes you need to work on a sub-view of the data as an independent object. + +For example: A Sudoko game: + + * The board is 9X9 + * Sub-divided into 3X3 squares + * And you need to examine the rows and columns + +Example: ``sudoku-chb.py`` + + +Pandas +======= + +.. rst-class:: left + + Python Data Analysis Library + + Pandas provides high-performance, easy-to-use data structures and data analysis tools for the Python programming language. + + Modeled after R's dataframe concept, it provides some pretty neat tools for doing simple statistical analysis and plotting of larg-ish data sets. + + It's particularly powerful for time series. + + ``http://pandas.pydata.org/`` + +Learning Pandas +---------------- + +The official documentation is excellent, including tutorials: + +http://pandas.pydata.org/pandas-docs/stable/ + +http://pandas.pydata.org/pandas-docs/stable/10min.html + +http://pandas.pydata.org/pandas-docs/stable/tutorials.html + +In addition, there are a large number of tutorials on the web: + +This one is oriented to folks familiar with SQL: + +http://www.gregreda.com/2013/10/26/intro-to-pandas-data-structures/ + +And this is a good one to get started quick: + +http://synesthesiam.com/posts/an-introduction-to-pandas.html + +We'll give that one a shot in class now... + +Scipy +===== + +.. rst-class:: left + + The scipy package itself is a large collection of cool stuff for scientific computing. ( http://docs.scipy.org/doc/scipy/reference/ ) + + You'll see there lots of stuff! If it's at all general purpose for computation, you're likely to find it there. + + Some of the most common sub-packages: + + * Special functions (scipy.special) + * Integration (scipy.integrate) + * Optimization (scipy.optimize) + * Interpolation (scipy.interpolate) + * Fourier Transforms (scipy.fftpack) + * Signal Processing (scipy.signal) + * Linear Algebra (scipy.linalg) + * Spatial data structures and algorithms (scipy.spatial) + * Statistics (scipy.stats) + * Multidimensional image processing (scipy.ndimage) + + + + + + diff --git a/_sources/syllabus.rst.txt b/_sources/syllabus.rst.txt new file mode 100644 index 0000000..718d1cd --- /dev/null +++ b/_sources/syllabus.rst.txt @@ -0,0 +1,104 @@ +.. _syllabus: + +*************************** +System Development Syllabus +*************************** + +UW Certificate Program in Programming in Python + +Python 300 + +Spring, 2017 + +April 4 through June 6th: Tuesday, 6 PM + + +Brief Course Description +========================= + +Course Objectives +------------------ + +At the end of the class, students will have completed a project of their own choosing and been exposed to a variety of advanced topics about the python programming language. Most programming class coursework involves small, self contained, assignments. While this is useful for learning specific concepts, it is hard to develop an understanding of the issues associated with larger software projects. This class gives students a chance to develop a significant project with the guidance of the instructors. + +Course Website +---------------- +Lecture notes, sample code, etc will be available in the course github project: + +http://UWPCE-PythonCert.github.io/SystemDevelopment + + +Instructors +----------- + +Christopher Barker `PythonCHB@gmail.com` + +Maria McKinley `mariak@mariakathryn.net` + +Technology Requirements +------------------------ + +Students will need a laptop computer with python 3.6.*, a development environment, and the ability to install additional software. + + +Assessment Criteria & Course Expectations +------------------------------------------------ + +Students are required to attend 8 out 10 classes and complete a significant software project in the Python language, either individually or as part of a small group. In addition, each student will be required to present some of their work in progress for an in-class code review in the middle of the quarter. + +Courses in this program are arranged sequentially. To recieve the certificate, students are required to earn a grade of Successful Completion (SC) in this course. Students must have successfully completed all courses in the program to receive a certificate of completion. + + +The class project: +================== + +Each student will develop a substantial project throughout the class. It can be an individual project or a group project with a small group from the class (2-4 students). We strongly suggest that you consider a group project -- it will give you a chance to practice developing with others, as well as give you a built-in way to get code review, folks to bounce ideas off of, etc. + + +Requirements: +------------- + +The project can be anything done primarily in Python: command line utility, desktop GUI, web application, web service, numerical model, smart phone app, you name it. + +The projects should be large enough to take everyone in the group about 8-10 hours a week in addition to class time, but small enough that you can get it to a useful state in 8-9 weeks of the class. + +Each project group will be expected to present their work in one of the last two classes. The presentations should be focused on the software design, rather than the problem solved (though, of course, we'll want to know what problem you solved...) + +We will expect you to use a Revision Control System (likely gitHub), and employ unit testing. + +You should set it up with good package structure -- ready to share and/or deploy. + +The project code should be documented: Sphinx! + +Conform to PEP8 (unless you have a company style instead) + +Use PyChecker and/or PyLint and/or PyFlakes + +*Please have your project selected and be prepared to start right in on it on day one!* + +Typical class: +============== + +Each class will involve a lecture interspersed with in-class exercises about the lecture topic. + +Beginning the fourth week, the final hour or so of the class will consist of code reviews of students' work-in-progress. + +In addition, as we work with you on your projects, we will highlight for the class interesting problems and their solutions that come up in class. + +Schedule +======== + +:ref:`course_outline` + + +Student Resources +================= + +The following link includes student handbooks, services, and policies, and other important information: http://www.pce.uw.edu/resource.aspx . + +Disability Accommodation +============================ + +The University of Washington is committed to providing access and reasonable accommodation in its services, programs, activities, education and employment for individuals with disabilities. For information or to request disability accommodation contact the Disability Services Office at 206.543.6450/V, 206.543.6452/TTY, 206.685.7264 (FAX), or e-mail at dso@u.washington.edu. + + diff --git a/Syllabus.md b/_sources/syllabus.txt similarity index 65% rename from Syllabus.md rename to _sources/syllabus.txt index 95becf8..63b2b2b 100644 --- a/Syllabus.md +++ b/_sources/syllabus.txt @@ -1,43 +1,61 @@ -# UW Certificate Program in Programming in Python +.. _syllabus: + +*************************** +System Development Syllabus +*************************** + +UW Certificate Program in Programming in Python Python 300 -Spring, 2015 -March 25 through May 27th, Tuesday, 6 PM -## Brief Course Description +Spring, 2016 + +March 29 through May 31st, Tuesday, 6 PM + + +Brief Course Description +========================= -### Course Objectives +Course Objectives +------------------ At the end of the class, students will have completed a project of their own choosing and been exposed to a variety of advanced topics about the python programming language. Most programming class coursework involves small, self contained, assignments. While this is useful for learning specific concepts, it is hard to develop an understanding of the issues associated with larger software projects. This class gives students a chance to develop a significant project with the guidance of the instructors. -### Course Website +Course Website +---------------- Lecture notes, sample code, etc will be available in the course github project: -https://github.com/UWPCE-PythonCert/Python300-SystemDevelopmentWithPython-Spring-2014 +http://UWPCE-PythonCert.github.io/SystemDevelopment -## Instructors -Joseph Sheedy `joseph.sheedy@gmail.com` +Instructors +----------- Christopher Barker `PythonCHB@gmail.com` +Maria McKinley `mariak@mariakathryn.net` -## Technology Requirements -Students will need a laptop computer with python 2.7.x , a development environment, and the ability to install additional software. +Technology Requirements +------------------------ +Students will need a laptop computer with python 3.5.*, a development environment, and the ability to install additional software. + +Assessment Criteria & Course Expectations +------------------------------------------------ -##Assessment Criteria & Course Expectations Students are required to attend 8 out 10 classes and complete a significant software project in the Python language, either individually or as part of a small group. In addition, each student will be required to present some of their work in progress code for an in-class code review in teh middl eof the quarter. Courses in this program are arranged sequentially. To recieve the certificate, students are required to earn a grade of Successful Completion (SC) in this course. Students must have successfully completed all courses in the program to receive a certificate of completion. -## The class project: +The class project: +================== Each student will develop a substantial project throughout the class. It can be an individual project or a group project with a small group from the class (2-4 students). We suggest that you strongly consider a group project -- it will give you a chance to practice developing with others, as well as give you a built-in way to get code review, folks to bounce ideas off of, etc. -### Requirements: +Requirements: +------------- The project can be anything done primarily in Python: command line utility, desktop GUI, web application, web service, numerical model, smart phone app, you name it. @@ -57,7 +75,8 @@ Use PyChecker and/or PyLint and/or PyFlakes *Please have your project selected and be prepared to start right in on it on day one!* -## Typical class: +Typical class: +============== Each class will involve a lecture interspersed with in-class exercises about the lecture topic. @@ -65,104 +84,20 @@ Beginning the fourth week, the final hour or so of the class will consist of cod In addition, as we work with you on your projects, we will highlight for the class interesting problems and their solutions that come up in class. +Schedule +======== -# Schedule - -### Week 1 -April 7th -Instructors: Joseph & Chris - -#### Topics -- Class intro -- packaging: Chris -- unit testing and coverage: Joseph -- unicode: Chris - - -### Week 2 -April 14th -Instructor: Chris -(proposals due) -#### Topics -- Weak references -- Documentation (docstrings, sphinx) -- PEP-8 (pylint/pychecker/pyflakes) - - -### Week 3 -April 21st -Instructor: Joseph -#### Topics - -- "advanced OO" - - \__new__(), super() - - type, metaclasses - - Debugging (print, logging, pdb/ipdb, winpdb) - -### Week 4 -April 28th -Instructor: Joseph -#### Topics -- Databases (DB-API w/ sqlite, postgres, mysql) -- Non-relational DBs (ZODB, MongoDB, couchDB, BSDdb, Redis) -- code reviews begin - - -### Week 5 -May 5th -Instructor: Chris - -#### Topics +:ref:`course_outline` -- numpy, scipy, pandas, matplotlib -- ipython/notebook +Student Resources +================= -### Week 6 -May 12th -Instructor: Chris - -#### Topics - -- datetime, time, pytz -- functools -- itertools -- Beautiful idiomatic code - -### Week 7 -May 19th -Instructor: Joseph -#### Topics - -- serialization review / XML -- profiling -- multi-threading/processing - - -### Week 8 -May 26th -Instructor: Chris - -#### Topics - -- C extensions( C API, ctypes, cython) - -### Week 9 -June 2nd - -Student Presentations - --- Joseph and Chris - -### Week 10 -June 9th +The following link includes student handbooks, services, and policies, and other important information: http://www.pce.uw.edu/resource.aspx . -Student Presentations +Disability Accommodation +============================ --- Joseph and Chris +The University of Washington is committed to providing access and reasonable accommodation in its services, programs, activities, education and employment for individuals with disabilities. For information or to request disability accommodation contact the Disability Services Office at 206.543.6450/V, 206.543.6452/TTY, 206.685.7264 (FAX), or e-mail at dso@u.washington.edu. -## Student Resources -The following link includes student handbooks, services, and policies, and other important information: http://www.pce.uw.edu/resource.aspx . -## Disability Accommodation -The University of Washington is committed to providing access and reasonable accommodation in its services, programs, activities, education and employment for individuals with disabilities. For information or to request disability accommodation contact the Disability Services Office at 206.543.6450/V, 206.543.6452/TTY, 206.685.7264 (FAX), or e-mail at dso@u.washington.edu. diff --git a/_sources/testing.rst.txt b/_sources/testing.rst.txt new file mode 100644 index 0000000..695da62 --- /dev/null +++ b/_sources/testing.rst.txt @@ -0,0 +1,777 @@ +.. _testing: + +******* +Testing +******* + +System Development with Python + +Git repository: + +https://github.com/UWPCE-PythonCert/SystemDevelopment + +================ +What is testing? +================ + +.. rst-class:: medium + + Code which runs your application in as close to a real environment as + feasible and validates its behavior + + +Terminology of testing +---------------------- + +- Unit tests +- Integration tests +- High level system tests +- Acceptance tests +- Black box / White box testing + + +"V" model and tests levels +-------------------------- +.. image:: /_static/test_v_model.png + +Unit testing +------------ + +- Test smallest discrete units of source code +- Tests should be independent of each other +- Can separate tests from required resources through fixtures and + mocking +- Automatable +- Integrates with development process + +.. nextslide:: + +What should be tested? +---------------------- + +The percentage of code which gets run in a test is known as the +coverage. + +100% coverage is an ideal to strive for. But the decision on when and +what to test should take into account the volatility of the project. + + +.. nextslide:: + +Unit-testing tools +------------------ + +- unittest, the test framework that ships with Python. Started life as PyUnit. + + http://docs.python.org/3/library/unittest.html + +- nose2, a test runner which integrates with unittest, making it nicer and easier + + http://nose2.readthedocs.org/en/latest/ + +- mock, an object mocking library. Ships with Python 3.3+ + + https://docs.python.org/dev/library/unittest.mock.html + +- pytest, an alternative to unittest, which you should be pretty familiar with now + + http://pytest.org/latest/ + + +About Unit-testing +------------------ + +1. Tests should be independent. +2. Tests do not run in order, which shouldn't matter, see point 1. +3. Test fixtures are available to do any setup/teardown needed for tests. +4. Test behavior not implementation +5. Mocking is available to fake stuff you may not want to run in your tests. + +unittest.TestCase anatomy +------------------------- + +* create a new subclass of unittest.TestCase +* name test methods test\_foo so the test runner finds them +* make calls to the self.assert\* family of methods to validate results + +:: + + import unittest + class TestTest(unittest.TestCase): + + def setUp(self): + self.x = 2 + + def test_add(self): + self.assertEqual(self.x+2, 4) + + def test_len(self): + self.assertEqual(len('foo'), 3) + + if __name__ == '__main__': + unittest.main() + + +Assert Methods +--------------- + +TestCase contains a number of methods named assert\* which can be used +for validation, here are a few common ones:: + + assertEqual(first, second, msg=None) + assertNotEqual(first, second, msg=None) + assertTrue(expr, msg=None) + assertFalse(expr, msg=None) + assertIn(first, second) + assertRaises(exc, fun, msg=None, \*args, \*\*kwargs) + +See a full list at: + +http://docs.python.org/3/library/unittest.html#assert-methods or + +``dir(unittest.TestCase)`` or to get really fancy + +.. code-block:: python + + [print(i) for i in dir(unittest.TestCase) if i.startswith('assert')] + + +Fixtures: Setting up your tests for success +------------------------------------------- + +(or failure!) + +Test fixtures are a fixed baseline for tests to run from consistently, +also known as test context + +Fixtures can be set up fresh before each test, once before each test +case, or before an entire test suite + +unittest provides fixture support via these methods: + +- setUp / tearDown - these are run before and after each test method +- setUpClass / tearDownClass - these are run before/after each TestCase +- setUpModule / tearDownModule - run before/after each TestSuite +- (new in Python 2.7) addCleanup / doCleanups - called after tearDown, + in case a test throws an exception + +============================= +Testing floating point values +============================= + +.. rst-class:: left + + Why can't we just test if .5 == .5 ? + + .. code-block:: ipython + + In [1]: 3 * .15 == .45 + Out[1]: False + + In [2]: 3 * .15 + Out[2]: 0.44999999999999996 + + In [3]: 3 * .15 * 10 / 10 == .45 + Out[3]: True + + There are an infinite number of floating point numbers, so they are + stored as an approximation in computing hardware. + + https://docs.python.org/3/tutorial/floatingpoint.html + +levels of precision of floating point +------------------------------------- + +Floating point numbers are stored in `IEEE +754 `__ 64-bit double +precision format, so 1 bit for the sign, 11 bits for the exponent, and +the remaining 52 for the fraction + +So we can count on up to 16 digits of precision in decimal: + +.. code-block:: ipython + + In [39]: len(str(2**52)) + Out[39]: 16 + + In [40]: .1+.2 + Out[40]: 0.30000000000000004 + + In [41]: len('3000000000000000') + Out[41]: 16 + + # with repeated operations, the errors eventually build up: + # here's multiplying by '1' 10 million times: + In [64]: x=1 + In [69]: for i in range(10000000): x *= (.1 + .2)/.3 + Out [69]: 1.000000002220446 + +assertAlmostEqual +----------------- + +Verifies that two floating point values are close enough to each other. +Add a places keyword argument to specify the number of significant +digits. + +.. code-block:: python + + import unittest + + class TestAlmostEqual(unittest.TestCase): + + def setUp(self): + pass + + def test_floating_point(self): + self.assertEqual(3*.15, .45) + + def test_almost_equal(self): + self.assertAlmostEqual(3*.15, .45, places=7) + + +What is close? +-------------- + +.. rst-class:: medium + + **Warning** + +``assertAlmostEqual`` lets you specify *decimal places*, +i.e. the number of digits after the decimal point. + +This works great for numbers that are about magnitude 1.0 (as above) + +But what if you have numbers that are very large? (or small): + + - ``1.0e22`` + - ``1.0000000000001e22`` + +are they almost equal? + +.. nextslide:: + +Remember that python floating point numbers store the exponent and up +to 16 decimal digits. + +So those two are almost as close as you can get. But: + +.. code-block:: ipython + + In [30]: x = 1e22 + + In [31]: y = 1.0000000000001e22 + + In [32]: '%g'%(y - x) + Out[32]: '1.00034e+09' + +They are different by about a billion! + +In general, we don't want to compare floating point numbers to within a +certain number of decimal places. + +Anyone remember "significant figures" from science classes? + +``isclose()`` +------------- + +Python 3.5 introduced the isclose() function in the math module: + +https://www.python.org/dev/peps/pep-0485/ + +.. code-block:: ipython + + In [39]: import math + + In [40]: x + Out[40]: 1e+22 + + In [41]: y + Out[41]: 1.0000000000001e+22 + + In [42]: math.isclose(x,y) + Out[42]: True + +So this works for any magnitude number. + +.. nextslide:: + +:: + + is_close(a, b, *, rel_tol=1e-09, abs_tol=0.0) -> bool + + Determine whether two floating point numbers are close in value. + + rel_tol + maximum difference for being considered "close", relative to the + magnitude of the input values + abs_tol + maximum difference for being considered "close", regardless of the + magnitude of the input values + + Return True if a is close in value to b, and False otherwise. + +``rel_tol`` essentially specifies how many significant figures you want: +``1e-09`` is 9 significant figures: about half of what floats can store. + +``abs_tol`` is required for comparisons to zero -- nothing is +"relatively close" to zero + +Using ``isclose()`` with ``unittest`` +------------------------------------- + +Ideally, ``TestCase`` would have an ``assertIsClose`` method. +But you can use: + +.. code-block:: python + + import unittest + from math import isclose + + class TestAlmostEqual(unittest.TestCase): + + def test_floating_point(self): + self.assertEqual(3*.15, .45) + + def test_almost_equal(self): + self.assertTrue( isclose( 3*.15, .45, rel_tol=7) ) + +================== +Running your tests +================== + +.. rst-class:: medium + + How do you actually run your tests? + + +running tests in a single module +-------------------------------- + +Call unittest.main() right in your module + +:: + + if __name__ == "__main__": + unittest.main() + + # or from the command line: + python -m unittest test_my_module # with or without .py on end + python -m unittest test_my_module.TestClass # particular class in a module + python -m unittest test_my_module.TestClass.test_method # particular test + + +If it gets cumbersome with many TestCases, organize the tests into a +test suite + +Test Suites +----------- + +Test suites group test cases into a single testable unit + +:: + + import unittest + + from calculator_test import TestCalculatorFunctions + + suite = unittest.TestLoader().loadTestsFromTestCase(TestCalculatorFunctions) + + unittest.TextTestRunner(verbosity=2).run(suite) + + +Tests can also be organized into suites in the + +``if __name__ == "__main__":`` + +block + + +nose2 +----- + +Nose2 is the new nose. Nose is barely being maintained, and directs users to nose2. + +A test runner which autodiscovers test cases + +Nose2 will find tests for you so you can focus on writing tests, not +maintaining test suites + +To find tests, nose2 looks for modules (such as python files) whose names start with ‘test’. In those modules, nose2 will load tests from all unittest.TestCase subclasses, as well as functions whose names start with ‘test’. + +Running your tests is as easy as + +:: + + $ nose2 + + +http://nose2.readthedocs.org/en/latest/getting_started.html#running-tests + + +nose2 plugins +------------- + +Many plugins exist for nose2, such as code coverage: +Some plugins, such as coverage, must be additionally installed +:: + + $ pip install cov-core + # now it can be used + $ nose2 --with-coverage + +.. nextslide:: + +Some of many useful plugins installed by default: + +- Test Generators + + http://nose2.readthedocs.org/en/latest/plugins/generators.html + +- Parameterized Tests + + http://nose2.readthedocs.org/en/latest/plugins/parameters.html + +- Stop after first error or failure -F + +- Drop in to the debugger on failure -D + + +running coverage +---------------- + +Install with ``pip``. Written by Ned Batchelder + +To run coverage on your test suite: + +:: + + coverage run my_program.py arg1 arg2 + +This generates a .coverage file. To analyze it on the console: + +:: + + coverage report + +Else generate an HTML report in the current directory: + +:: + + coverage html + +To find out coverage across the standard library, add -L: + +:: + + -L, --pylib Measure coverage even inside the Python installed + library, which isn't done by default. + + +branch coverage +--------------- + +consider the following code: + +:: + + x = False # 1 + if x: # 2 + print("in branch") # 3 + print("out of branch") # 4 + +We want to make sure the branch is being bypassed correctly in the False +case + +Track which branch destinations were not visited with the --branch +option to run + +:: + + coverage run --branch myprog.py + +http://nedbatchelder.com/code/coverage/branch.html + +Doctests +-------- + +Tests placed in docstrings to demonstrate usage of a component to a +human in a machine testable way + +:: + + def square(x): + """ + Squares x. + + >>> square(2) + 4 + >>> square(-2) + 4 + """ + return x * x + +:: + + python -m doctest -v example.py + +.. nextslide:: + +Now generate documentation, using epydoc for example: + +:: + + $ epydoc example.py + + +http://docs.python.org/3/library/doctest.html + +http://www.python.org/dev/peps/pep-0257/ + +http://epydoc.sourceforge.net/ + +http://sphinx-doc.org/ + +http://www.doxygen.org + + +Test Driven Development (TDD) +----------------------------- + +In TDD, the tests are written the meet the requirements before the code +exists. + +Once the collection of tests passes, the requirement is considered met. + +We don't always want to run the entire test suite. In order to run a +single test with nose: + +:: + + nose2 test_calculator.TestCalculatorFunctions.test_add + + +Exercises +--------- + +- Add unit tests for each method in calculator\_functions.py +- Add fixtures via setUp/tearDown methods and setUpClass/tearDownClass + class methods. Are they behaving how you expect? +- Add additional unit tests for floating point calculations +- Fix any failures in the code +- Add doctests to calculator\_functions.py + + +================ +Context managers +================ + +.. rst-class:: medium + + One more Python feature before getting back to testing... + + the ``with`` statement + + +Context managers via the "with" statement +----------------------------------------- + +If you've been opening files using "with" (and you probably should be), +you have been using context managers: + +:: + + with open("file.txt", "w") as f: + f.write("foo") + + +A context manager is just a class with \_\_enter\_\_ and \_\_exit\_\_ +methods defined to handle setting up and tearing down the context + +Provides generalizable execution contexts in which setup and teardown of +context are executed no matter what happens + +This allows us to do things like setup/teardown and separate out +exception handling code + + +Writing a context manager +------------------------- + +Define \_\_enter\_\_(self) and \_\_exit\_\_(self, type, value, +traceback) on a class + +If \_\_exit\_\_ returns a true value, a caught exception is not +re-raised + +For example: + +.. nextslide:: + +:: + + import os, random, shutil, time + + class TemporaryDirectory(object): + """A context manager for creating a temporary directory + which gets destroyed on context exit""" + def __init__(self,directory): + self.base_directory = directory + + def __enter__(self): + self.directory = os.path.join(self.base_directory, str(random.random())) + return os.makedirs(self.directory) + + def __exit__(self, type, value, traceback): + shutil.rmtree(self.directory) + + with TemporaryDirectory("/tmp/foo") as dir: + with open(os.path.join(dir, "foo.txt"), 'wb') as f: + f.write("foo") + time.sleep(5) + + +http://www.python.org/dev/peps/pep-0343/ + +Context Manager exercise +------------------------ + +Create a context manager which prints information on all exceptions +which occur in the context and continues execution + +:: + + with YourExceptionHandler(): + print("do some stuff here") + 1/0 + + print("should still reach this point") + + +Also see the `contextlib +module `__ + +Why might using a context manager be better than implementing this with +try..except..finally ? + + +.. nextslide:: + +For entire code block, see https://www.python.org/dev/peps/pep-0343/ (Specification) +:: + + with EXPR as VAR: + BLOCK + # vs. + mgr = (EXPR) + exit = type(mgr).__exit__ # Not calling it yet + value = type(mgr).__enter__(mgr) + exc = True + try: + try: + VAR = value # Only if "as VAR" is present + BLOCK + except: + # The exceptional case is handled here + exc = False + if not exit(mgr, *sys.exc_info()): + raise + # The exception is swallowed if exit() returns true + finally: + # The normal and non-local-goto cases are handled here + if exc: + exit(mgr, None, None, None) + + +Now we've got the tools to really test +-------------------------------------- + +Consider the application in the examples/wikidef directory. Give the +command line utility a subject, and it will return a definition. + +:: + + ./define.py Robot + + +How can we test our application code without abusing (and waiting for) +Wikipedia? + +Using Mock objects +------------------ + +Using Mock objects to test an application with service dependencies + +Mock objects replace real objects in your code at runtime during test + +This allows you to test code which calls these objects without having +their actual code run + +Useful for testing objects which depend on unimplemented code, resources +which are expensive, or resources which are unavailable during test +execution + +http://www.voidspace.org.uk/python/mock + +Mocks +----- + +The MagickMock class will keep track of calls to it so we can verify +that the class is being called correctly, without having to execute the +code underneath + +:: + + import mock + + mock_object = mock.MagicMock() + mock_object.foo.return_value = "foo return" + print(mock_object.foo.call_count) + print(mock_object.foo()) + print(mock_object.foo.call_count) + # raise an exception by assigning to the side_effect attribute + mock_object.foo.side_effect = Exception + mock_object.foo() + + +Easy mocking with mock.patch +---------------------------- + +patch acts as a function decorator, class decorator, or a context +manager + +Inside the body of the function or with statement, the target is patched +with a new object. When the function/with statement exits the patch is +undone + + +Using patch +----------- + +:: + + # patch with a decorator + @patch.object(Wikipedia, 'article') + def test_article_success_decorator_mocked(self, mock_method): + article = Definitions.article("Robot") + mock_method.assert_called_once_with("Robot") + + # patch with a context manager + def test_article_success_context_manager_mocked(self): + with patch.object(Wikipedia, 'article') as mock_method: + article = Definitions.article("Robot") + mock_method.assert_called_once_with("Robot") + + +http://www.voidspace.org.uk/python/mock/patch.html + + +Exercises +--------- + +When define.py is given the name of a non-existant article, an exception +is thrown. This exception causes another exception to occur, and the whole thing +is not very readable. Why does this happen? + +Use what you learned last week about exceptions to throw a better exception, and +then add a new test that confirms this behavior. Use mock for your test, so you +are not hammering Wikipedia. diff --git a/_sources/testing.txt b/_sources/testing.txt new file mode 100644 index 0000000..695da62 --- /dev/null +++ b/_sources/testing.txt @@ -0,0 +1,777 @@ +.. _testing: + +******* +Testing +******* + +System Development with Python + +Git repository: + +https://github.com/UWPCE-PythonCert/SystemDevelopment + +================ +What is testing? +================ + +.. rst-class:: medium + + Code which runs your application in as close to a real environment as + feasible and validates its behavior + + +Terminology of testing +---------------------- + +- Unit tests +- Integration tests +- High level system tests +- Acceptance tests +- Black box / White box testing + + +"V" model and tests levels +-------------------------- +.. image:: /_static/test_v_model.png + +Unit testing +------------ + +- Test smallest discrete units of source code +- Tests should be independent of each other +- Can separate tests from required resources through fixtures and + mocking +- Automatable +- Integrates with development process + +.. nextslide:: + +What should be tested? +---------------------- + +The percentage of code which gets run in a test is known as the +coverage. + +100% coverage is an ideal to strive for. But the decision on when and +what to test should take into account the volatility of the project. + + +.. nextslide:: + +Unit-testing tools +------------------ + +- unittest, the test framework that ships with Python. Started life as PyUnit. + + http://docs.python.org/3/library/unittest.html + +- nose2, a test runner which integrates with unittest, making it nicer and easier + + http://nose2.readthedocs.org/en/latest/ + +- mock, an object mocking library. Ships with Python 3.3+ + + https://docs.python.org/dev/library/unittest.mock.html + +- pytest, an alternative to unittest, which you should be pretty familiar with now + + http://pytest.org/latest/ + + +About Unit-testing +------------------ + +1. Tests should be independent. +2. Tests do not run in order, which shouldn't matter, see point 1. +3. Test fixtures are available to do any setup/teardown needed for tests. +4. Test behavior not implementation +5. Mocking is available to fake stuff you may not want to run in your tests. + +unittest.TestCase anatomy +------------------------- + +* create a new subclass of unittest.TestCase +* name test methods test\_foo so the test runner finds them +* make calls to the self.assert\* family of methods to validate results + +:: + + import unittest + class TestTest(unittest.TestCase): + + def setUp(self): + self.x = 2 + + def test_add(self): + self.assertEqual(self.x+2, 4) + + def test_len(self): + self.assertEqual(len('foo'), 3) + + if __name__ == '__main__': + unittest.main() + + +Assert Methods +--------------- + +TestCase contains a number of methods named assert\* which can be used +for validation, here are a few common ones:: + + assertEqual(first, second, msg=None) + assertNotEqual(first, second, msg=None) + assertTrue(expr, msg=None) + assertFalse(expr, msg=None) + assertIn(first, second) + assertRaises(exc, fun, msg=None, \*args, \*\*kwargs) + +See a full list at: + +http://docs.python.org/3/library/unittest.html#assert-methods or + +``dir(unittest.TestCase)`` or to get really fancy + +.. code-block:: python + + [print(i) for i in dir(unittest.TestCase) if i.startswith('assert')] + + +Fixtures: Setting up your tests for success +------------------------------------------- + +(or failure!) + +Test fixtures are a fixed baseline for tests to run from consistently, +also known as test context + +Fixtures can be set up fresh before each test, once before each test +case, or before an entire test suite + +unittest provides fixture support via these methods: + +- setUp / tearDown - these are run before and after each test method +- setUpClass / tearDownClass - these are run before/after each TestCase +- setUpModule / tearDownModule - run before/after each TestSuite +- (new in Python 2.7) addCleanup / doCleanups - called after tearDown, + in case a test throws an exception + +============================= +Testing floating point values +============================= + +.. rst-class:: left + + Why can't we just test if .5 == .5 ? + + .. code-block:: ipython + + In [1]: 3 * .15 == .45 + Out[1]: False + + In [2]: 3 * .15 + Out[2]: 0.44999999999999996 + + In [3]: 3 * .15 * 10 / 10 == .45 + Out[3]: True + + There are an infinite number of floating point numbers, so they are + stored as an approximation in computing hardware. + + https://docs.python.org/3/tutorial/floatingpoint.html + +levels of precision of floating point +------------------------------------- + +Floating point numbers are stored in `IEEE +754 `__ 64-bit double +precision format, so 1 bit for the sign, 11 bits for the exponent, and +the remaining 52 for the fraction + +So we can count on up to 16 digits of precision in decimal: + +.. code-block:: ipython + + In [39]: len(str(2**52)) + Out[39]: 16 + + In [40]: .1+.2 + Out[40]: 0.30000000000000004 + + In [41]: len('3000000000000000') + Out[41]: 16 + + # with repeated operations, the errors eventually build up: + # here's multiplying by '1' 10 million times: + In [64]: x=1 + In [69]: for i in range(10000000): x *= (.1 + .2)/.3 + Out [69]: 1.000000002220446 + +assertAlmostEqual +----------------- + +Verifies that two floating point values are close enough to each other. +Add a places keyword argument to specify the number of significant +digits. + +.. code-block:: python + + import unittest + + class TestAlmostEqual(unittest.TestCase): + + def setUp(self): + pass + + def test_floating_point(self): + self.assertEqual(3*.15, .45) + + def test_almost_equal(self): + self.assertAlmostEqual(3*.15, .45, places=7) + + +What is close? +-------------- + +.. rst-class:: medium + + **Warning** + +``assertAlmostEqual`` lets you specify *decimal places*, +i.e. the number of digits after the decimal point. + +This works great for numbers that are about magnitude 1.0 (as above) + +But what if you have numbers that are very large? (or small): + + - ``1.0e22`` + - ``1.0000000000001e22`` + +are they almost equal? + +.. nextslide:: + +Remember that python floating point numbers store the exponent and up +to 16 decimal digits. + +So those two are almost as close as you can get. But: + +.. code-block:: ipython + + In [30]: x = 1e22 + + In [31]: y = 1.0000000000001e22 + + In [32]: '%g'%(y - x) + Out[32]: '1.00034e+09' + +They are different by about a billion! + +In general, we don't want to compare floating point numbers to within a +certain number of decimal places. + +Anyone remember "significant figures" from science classes? + +``isclose()`` +------------- + +Python 3.5 introduced the isclose() function in the math module: + +https://www.python.org/dev/peps/pep-0485/ + +.. code-block:: ipython + + In [39]: import math + + In [40]: x + Out[40]: 1e+22 + + In [41]: y + Out[41]: 1.0000000000001e+22 + + In [42]: math.isclose(x,y) + Out[42]: True + +So this works for any magnitude number. + +.. nextslide:: + +:: + + is_close(a, b, *, rel_tol=1e-09, abs_tol=0.0) -> bool + + Determine whether two floating point numbers are close in value. + + rel_tol + maximum difference for being considered "close", relative to the + magnitude of the input values + abs_tol + maximum difference for being considered "close", regardless of the + magnitude of the input values + + Return True if a is close in value to b, and False otherwise. + +``rel_tol`` essentially specifies how many significant figures you want: +``1e-09`` is 9 significant figures: about half of what floats can store. + +``abs_tol`` is required for comparisons to zero -- nothing is +"relatively close" to zero + +Using ``isclose()`` with ``unittest`` +------------------------------------- + +Ideally, ``TestCase`` would have an ``assertIsClose`` method. +But you can use: + +.. code-block:: python + + import unittest + from math import isclose + + class TestAlmostEqual(unittest.TestCase): + + def test_floating_point(self): + self.assertEqual(3*.15, .45) + + def test_almost_equal(self): + self.assertTrue( isclose( 3*.15, .45, rel_tol=7) ) + +================== +Running your tests +================== + +.. rst-class:: medium + + How do you actually run your tests? + + +running tests in a single module +-------------------------------- + +Call unittest.main() right in your module + +:: + + if __name__ == "__main__": + unittest.main() + + # or from the command line: + python -m unittest test_my_module # with or without .py on end + python -m unittest test_my_module.TestClass # particular class in a module + python -m unittest test_my_module.TestClass.test_method # particular test + + +If it gets cumbersome with many TestCases, organize the tests into a +test suite + +Test Suites +----------- + +Test suites group test cases into a single testable unit + +:: + + import unittest + + from calculator_test import TestCalculatorFunctions + + suite = unittest.TestLoader().loadTestsFromTestCase(TestCalculatorFunctions) + + unittest.TextTestRunner(verbosity=2).run(suite) + + +Tests can also be organized into suites in the + +``if __name__ == "__main__":`` + +block + + +nose2 +----- + +Nose2 is the new nose. Nose is barely being maintained, and directs users to nose2. + +A test runner which autodiscovers test cases + +Nose2 will find tests for you so you can focus on writing tests, not +maintaining test suites + +To find tests, nose2 looks for modules (such as python files) whose names start with ‘test’. In those modules, nose2 will load tests from all unittest.TestCase subclasses, as well as functions whose names start with ‘test’. + +Running your tests is as easy as + +:: + + $ nose2 + + +http://nose2.readthedocs.org/en/latest/getting_started.html#running-tests + + +nose2 plugins +------------- + +Many plugins exist for nose2, such as code coverage: +Some plugins, such as coverage, must be additionally installed +:: + + $ pip install cov-core + # now it can be used + $ nose2 --with-coverage + +.. nextslide:: + +Some of many useful plugins installed by default: + +- Test Generators + + http://nose2.readthedocs.org/en/latest/plugins/generators.html + +- Parameterized Tests + + http://nose2.readthedocs.org/en/latest/plugins/parameters.html + +- Stop after first error or failure -F + +- Drop in to the debugger on failure -D + + +running coverage +---------------- + +Install with ``pip``. Written by Ned Batchelder + +To run coverage on your test suite: + +:: + + coverage run my_program.py arg1 arg2 + +This generates a .coverage file. To analyze it on the console: + +:: + + coverage report + +Else generate an HTML report in the current directory: + +:: + + coverage html + +To find out coverage across the standard library, add -L: + +:: + + -L, --pylib Measure coverage even inside the Python installed + library, which isn't done by default. + + +branch coverage +--------------- + +consider the following code: + +:: + + x = False # 1 + if x: # 2 + print("in branch") # 3 + print("out of branch") # 4 + +We want to make sure the branch is being bypassed correctly in the False +case + +Track which branch destinations were not visited with the --branch +option to run + +:: + + coverage run --branch myprog.py + +http://nedbatchelder.com/code/coverage/branch.html + +Doctests +-------- + +Tests placed in docstrings to demonstrate usage of a component to a +human in a machine testable way + +:: + + def square(x): + """ + Squares x. + + >>> square(2) + 4 + >>> square(-2) + 4 + """ + return x * x + +:: + + python -m doctest -v example.py + +.. nextslide:: + +Now generate documentation, using epydoc for example: + +:: + + $ epydoc example.py + + +http://docs.python.org/3/library/doctest.html + +http://www.python.org/dev/peps/pep-0257/ + +http://epydoc.sourceforge.net/ + +http://sphinx-doc.org/ + +http://www.doxygen.org + + +Test Driven Development (TDD) +----------------------------- + +In TDD, the tests are written the meet the requirements before the code +exists. + +Once the collection of tests passes, the requirement is considered met. + +We don't always want to run the entire test suite. In order to run a +single test with nose: + +:: + + nose2 test_calculator.TestCalculatorFunctions.test_add + + +Exercises +--------- + +- Add unit tests for each method in calculator\_functions.py +- Add fixtures via setUp/tearDown methods and setUpClass/tearDownClass + class methods. Are they behaving how you expect? +- Add additional unit tests for floating point calculations +- Fix any failures in the code +- Add doctests to calculator\_functions.py + + +================ +Context managers +================ + +.. rst-class:: medium + + One more Python feature before getting back to testing... + + the ``with`` statement + + +Context managers via the "with" statement +----------------------------------------- + +If you've been opening files using "with" (and you probably should be), +you have been using context managers: + +:: + + with open("file.txt", "w") as f: + f.write("foo") + + +A context manager is just a class with \_\_enter\_\_ and \_\_exit\_\_ +methods defined to handle setting up and tearing down the context + +Provides generalizable execution contexts in which setup and teardown of +context are executed no matter what happens + +This allows us to do things like setup/teardown and separate out +exception handling code + + +Writing a context manager +------------------------- + +Define \_\_enter\_\_(self) and \_\_exit\_\_(self, type, value, +traceback) on a class + +If \_\_exit\_\_ returns a true value, a caught exception is not +re-raised + +For example: + +.. nextslide:: + +:: + + import os, random, shutil, time + + class TemporaryDirectory(object): + """A context manager for creating a temporary directory + which gets destroyed on context exit""" + def __init__(self,directory): + self.base_directory = directory + + def __enter__(self): + self.directory = os.path.join(self.base_directory, str(random.random())) + return os.makedirs(self.directory) + + def __exit__(self, type, value, traceback): + shutil.rmtree(self.directory) + + with TemporaryDirectory("/tmp/foo") as dir: + with open(os.path.join(dir, "foo.txt"), 'wb') as f: + f.write("foo") + time.sleep(5) + + +http://www.python.org/dev/peps/pep-0343/ + +Context Manager exercise +------------------------ + +Create a context manager which prints information on all exceptions +which occur in the context and continues execution + +:: + + with YourExceptionHandler(): + print("do some stuff here") + 1/0 + + print("should still reach this point") + + +Also see the `contextlib +module `__ + +Why might using a context manager be better than implementing this with +try..except..finally ? + + +.. nextslide:: + +For entire code block, see https://www.python.org/dev/peps/pep-0343/ (Specification) +:: + + with EXPR as VAR: + BLOCK + # vs. + mgr = (EXPR) + exit = type(mgr).__exit__ # Not calling it yet + value = type(mgr).__enter__(mgr) + exc = True + try: + try: + VAR = value # Only if "as VAR" is present + BLOCK + except: + # The exceptional case is handled here + exc = False + if not exit(mgr, *sys.exc_info()): + raise + # The exception is swallowed if exit() returns true + finally: + # The normal and non-local-goto cases are handled here + if exc: + exit(mgr, None, None, None) + + +Now we've got the tools to really test +-------------------------------------- + +Consider the application in the examples/wikidef directory. Give the +command line utility a subject, and it will return a definition. + +:: + + ./define.py Robot + + +How can we test our application code without abusing (and waiting for) +Wikipedia? + +Using Mock objects +------------------ + +Using Mock objects to test an application with service dependencies + +Mock objects replace real objects in your code at runtime during test + +This allows you to test code which calls these objects without having +their actual code run + +Useful for testing objects which depend on unimplemented code, resources +which are expensive, or resources which are unavailable during test +execution + +http://www.voidspace.org.uk/python/mock + +Mocks +----- + +The MagickMock class will keep track of calls to it so we can verify +that the class is being called correctly, without having to execute the +code underneath + +:: + + import mock + + mock_object = mock.MagicMock() + mock_object.foo.return_value = "foo return" + print(mock_object.foo.call_count) + print(mock_object.foo()) + print(mock_object.foo.call_count) + # raise an exception by assigning to the side_effect attribute + mock_object.foo.side_effect = Exception + mock_object.foo() + + +Easy mocking with mock.patch +---------------------------- + +patch acts as a function decorator, class decorator, or a context +manager + +Inside the body of the function or with statement, the target is patched +with a new object. When the function/with statement exits the patch is +undone + + +Using patch +----------- + +:: + + # patch with a decorator + @patch.object(Wikipedia, 'article') + def test_article_success_decorator_mocked(self, mock_method): + article = Definitions.article("Robot") + mock_method.assert_called_once_with("Robot") + + # patch with a context manager + def test_article_success_context_manager_mocked(self): + with patch.object(Wikipedia, 'article') as mock_method: + article = Definitions.article("Robot") + mock_method.assert_called_once_with("Robot") + + +http://www.voidspace.org.uk/python/mock/patch.html + + +Exercises +--------- + +When define.py is given the name of a non-existant article, an exception +is thrown. This exception causes another exception to occur, and the whole thing +is not very readable. Why does this happen? + +Use what you learned last week about exceptions to throw a better exception, and +then add a new test that confirms this behavior. Use mock for your test, so you +are not hammering Wikipedia. diff --git a/_sources/threading-multiprocessing.rst.txt b/_sources/threading-multiprocessing.rst.txt new file mode 100644 index 0000000..93f0b85 --- /dev/null +++ b/_sources/threading-multiprocessing.rst.txt @@ -0,0 +1,840 @@ +.. _threading: + +############################# +Threading and multiprocessing +############################# + +Threading / multiprocessing +=========================== + +Today's topics: + +- Threading / multiprocessing motivation and options +- threading module +- multiprocessing module +- other options + + +Motivations for parallel execution +---------------------------------- + +- Performance + + - Limited by "Amdahl's Law" + http://en.wikipedia.org/wiki/Amdahl%27s_law + + - CPUs aren't getting much faster + +- Event handling + + - If a system handles asynchronous events, a seperate thread of + execution could handle those events and let other threads do other + work + + - Examples: + + - Network applications + + - User interfaces + +Parallel programming can be hard! + +If your problem can be solved sequentially, consider the costs and +benefits before going parallel. + + +Parallelization strategy for performance +---------------------------------------- + +| 1. Break problem down into chunks +| 2. Execute chunks in parallel +| 3. Reassemble output of chunks into result + +.. image:: images/OPP.0108.gif + :align: right + :height: 450px + :alt: multitasking flow diagram + + +Parallelization strategy for performance +---------------------------------------- + +- Not every problem is parallelizable +- There is an optimal number of threads for each problem in each + environment, so make it tunable +- Working concurrently opens up synchronization issues +- Methods for synchronizing threads: + + - locks + - queues + - signaling/messaging mechanisms + + +Threads versus processes in Python +---------------------------------- + +Threads are lightweight processes_, run in the address space of an OS +process, true OS level threads. + +Therefor, a component of a a process. + +.. _processes: https://en.wikipedia.org/wiki/Light-weight_process + +This allows multiple threads access to data in the same scope. + +Threads can not gain the performance advantage of multiple processors +due to the Global Interpreter Lock (GIL) + +But the GIL is released during IO, allowing IO bound processes to +benefit from threading + +Processes +--------- + +A process contains all the instructions and data required to execute +independently, so processes do not share data! + +Mulitple processes best to speed up CPU bound operations. + +The Python interpreter isn't lightweight! + +Communication between processes can be achieved via: + +``multiprocessing.Queue`` + +``multiprocessing.Pipe`` + +and regular IPC (inter-process communication) + +Data moved between processes must be pickleable + + +GIL +--- + +**Global Interpreter Lock** + +This is a lock which must be obtained by each thread before it can +execute, ensuring thread safety + +.. image:: images/gil.png + :width: 100.0% + +.. nextslide:: + +The GIL is released during IO operations, so threads which spend time +waiting on network or disk access can enjoy performance gains + +The GIL is not unlike multitasking in humans, some things can truly be +done in parallel, others have to be done by time slicing. + +Note that potentially blocking or long-running operations, such as I/O, image processing, and NumPy number crunching, happen outside the GIL. Therefore it is only in multithreaded programs that spend a lot of time inside the GIL, interpreting CPython bytecode, that the GIL becomes a bottleneck. But: it can still cause performance degradation. + +Not only will threads not help cpu-bound problems, but it can actually make things *worse*, especially on multi-core machines! + + +.. nextslide:: + + +Some alternative Python implementations such as Jython and IronPython +have no GIL + +cPython and PyPy have one + +David Beazley's talk on the gil + +- https://www.youtube.com/watch?v=Obt-vMVdM8s + +More about the gil + +- http://wiki.python.org/moin/GlobalInterpreterLock + +- https://docs.python.org/3.5/c-api/init.html#threads + +- http://hg.python.org/cpython/file/05e8dde3229c/Python/pystate.c#l761 + +Posted without comment +---------------------- +.. figure:: images/killGIL.jpg + :class: fill + + +A CPU bound problem +------------------- + +Numerically integrate the function +:math:`y =x^2` from 0 to 10. +http://www.wolframalpha.com/input/?i=x%5E2 + +.. image:: images/x2.png + :height: 400px + +Solution: http://www.wolframalpha.com/input/?i=int(x%5E2,0,10) + +Parallel execution example +-------------------------- + +Consider the following code from: ``Examples/integrate/integrate.py`` + +.. code-block:: python + + def f(x): + return x**2 + + def integrate(f, a, b, N): + s = 0 + dx = (b-a)/N + for i in xrange(N): + s += f(a+i*dx) + return s * dx + +Break down the problem into parallelizable chunks, then add the results +together: + +We can do better than this + +The threading module +-------------------- + +Starting threads doesn't take much: + +.. code-block:: python + + import sys + import threading + import time + + def func(): + for i in xrange(5): + print("hello from thread %s" % threading.current_thread().name) + time.sleep(1) + + threads = [] + for i in xrange(3): + thread = threading.Thread(target=func, args=()) + thread.start() + threads.append(thread) + + +.. nextslide:: + + +- The process will exit when the last non-daemon thread exits. +- A thread can be specified as a daemon thread by setting its daemon + attribute: ``thread.daemon = True`` +- daemon threads get cut off at program exit, without any opportunity + for cleanup. But you don't have to track and manage them. Useful for + things like garbage collection, network keepalives, .. +- You can block and wait for a thread to exit with thread.join() + + +Subclassing Thread +------------------ + +You can adding threading capability to your own classes + +Subclass Thread and implement the run method + + +.. code-block:: python + + import threading + + class MyThread(threading.Thread): + + def run(self): + print("hello from %s" % threading.current_thread().name) + + thread = MyThread() + thread.start() + + +Race Conditions +--------------- + +In the last example we saw threads competing for access to stdout. + +Worse, if competing threads try to update the same value, we might get +an unexpected race condition + +Race conditions occur when multiple statements need to execute +atomically, but get interrupted midway + +See ``Examples/race_condition.py`` + +No race condition +------------------ + ++--------------------+--------------------+--------------------+--------------------+ +| Thread 1 | Thread 2 | | Integer value | ++====================+====================+====================+====================+ +| | | | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| read value | | ← | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| increase value | | | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| write back | | → | 1 | ++--------------------+--------------------+--------------------+--------------------+ +| | read value | ← | 1 | ++--------------------+--------------------+--------------------+--------------------+ +| | increase value | | 1 | ++--------------------+--------------------+--------------------+--------------------+ +| | write back | → | 2 | ++--------------------+--------------------+--------------------+--------------------+ + +Race Condition! +--------------- + ++--------------------+--------------------+--------------------+--------------------+ +| Thread 1 | Thread 2 | | Integer value | ++====================+====================+====================+====================+ +| | | | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| read value | | ← | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| | read value | ← | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| increase value | | | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| | increase value | | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| write back | | → | 1 | ++--------------------+--------------------+--------------------+--------------------+ +| | write back | → | 1 | ++--------------------+--------------------+--------------------+--------------------+ + +http://en.wikipedia.org/wiki/Race_condition + +Deadlocks +--------- + +Synchronization and Critical Sections are used to control race +conditions + +But they introduce other potential problems... + +like: http://en.wikipedia.org/wiki/Deadlock + +"A deadlock is a situation in which two or more competing actions are +each waiting for the other to finish, and thus neither ever does." + +*When two trains approach each other at a crossing, both shall come to a +full stop and neither shall start up again until the other has gone* + +See also *Livelock*: + +*Two people meet in a narrow corridor, and each +tries to be polite by moving aside to let the other pass, but they end +up swaying from side to side without making any progress because they +both repeatedly move the same way at the same time.* + + +Locks +----- + +Lock objects allow threads to control access to a resource until they're +done with it + +This is known as mutual exclusion, often called mutex + +Python 2 has a deprecated module called mutex for this. Use a Lock +instead. + +A Lock has two states: locked and unlocked + +If multiple threads have access to the same Lock, they can police +themselves by calling its ``.acquire()`` and ``.release()`` methods + +If a Lock is locked, .acquire will block until it becomes unlocked + +These threads will wait in line politely for access to the statements in +f() + +.. nextslide:: + +.. code-block:: python + + import threading + import time + + lock = threading.Lock() + + def f(): + lock.acquire() + print("%s got lock" % threading.current_thread().name) + time.sleep(1) + lock.release() + + threading.Thread(target=f).start() + threading.Thread(target=f).start() + threading.Thread(target=f).start() + + +Nonblocking Locking +------------------- + +``.acquire()`` will return True if it successfully acquires a lock + +Its first argument is a boolean which specifies whether a lock should +block or not. The default is ``True`` + +.. code-block:: python + + import threading + lock = threading.Lock() + lock.acquire() + if not lock.acquire(False): + print("couldn't get lock") + lock.release() + if lock.acquire(False): + print("got lock") + + +``threading.RLock`` - Reentrant Lock +------------------------------------ + +Useful for recursive algorithms, a thread-specific count of the locks is +maintained + +A reentrant lock can be acquired multiple times by the same thread + +``Lock.release()`` must be called the same number of times as ``Lock.acquire()`` +by that thread + + +``threading.Semaphore`` +----------------------- + +Like an ``RLock``, but in reverse + +A Semaphore is given an initial counter value, defaulting to 1 + +Each call to ``acquire()`` decrements the counter, ``release()`` increments it + +If ``acquire()`` is called on a Semaphore with a counter of 0, it will block +until the Semaphore counter is greater than 0. + +Useful for controlling the maximum number of threads allowed to access a +resource simultaneously + +http://en.wikipedia.org/wiki/Semaphore_(programming) + +.. image:: images/flags.jpg + :height: 250px + + + +Locking Exercise +---------------- + +In: ``Examples/lock/stdout_writer.py`` + +Multiple threads in the script write to stdout, and their output gets +jumbled + +1. Add a locking mechanism to give each thread exclusive access to + stdout + +2. Try adding a Semaphore to allow 2 threads access at once + + +Managing thread results +----------------------- + +We need a thread safe way of storing results from multiple threads of +execution. That is provided by the Queue module. + +Queues allow multiple producers and multiple consumers to exchange data +safely + +Size of the queue is managed with the maxsize kwarg + +It will block consumers if empty and block producers if full + +If maxsize is less than or equal to zero, the queue size is infinite + +.. nextslide:: + +.. code-block:: python + + from Queue import Queue + q = Queue(maxsize=10) + q.put(37337) + block = True + timeout = 2 + print(q.get(block, timeout)) + +- http://docs.python.org/3/library/threading.html +- http://docs.python.org/3/library/queue.html + +Other Queue types +----------------- + +``Queue.LifoQueue`` + + - Last In, First Out + +``Queue.PriorityQueue`` + + - Lowest valued entries are retrieved first + +One pattern for ``PriorityQueue`` is to insert entries of form data by +inserting the tuple: + +``(priority_number, data)`` + +Threading example +----------------- + +See Examples/threading/integrate_main.py + +.. code-block:: python + + #!/usr/bin/env python + + import argparse + import os + import sys + import threading + import Queue + + sys.path.append(os.path.join(os.path.dirname(__file__), "..")) + from integrate.integrate import integrate, f + from decorators.decorators import timer + +.. nextslide:: + +.. code-block:: python + + @timer + def threading_integrate(f, a, b, N, thread_count=2): + """break work into two chunks""" + N_chunk = int(float(N) / thread_count) + dx = float(b-a) / thread_count + + results = Queue.Queue() + + def worker(*args): + results.put(integrate(*args)) + + threads = [] + for i in xrange(thread_count): + x0 = dx*i + x1 = x0 + dx + thread = threading.Thread(target=worker, args=(f, x0, x1, N_chunk)) + thread.start() + print "Thread %s started" % thread.name + # thread1.join() + return sum( (results.get() for i in xrange(thread_count) )) + +.. nextslide:: + +.. code-block:: python + + if __name__ == "__main__": + parser = argparse.ArgumentParser(description='integrator') + parser.add_argument('a', nargs='?', type=float, default=0.0) + parser.add_argument('b', nargs='?', type=float, default=10.0) + parser.add_argument('N', nargs='?', type=int, default=10**7) + parser.add_argument('thread_count', nargs='?', type=int, default=2) + + args = parser.parse_args() + a = args.a + b = args.b + N = args.N + thread_count = args.thread_count + + print("Numerical solution with N=%(N)d : %(x)f" % \ + {'N': N, 'x': threading_integrate(f, a, b, N, thread_count=thread_count)}) + + +Threading on a CPU bound problem +-------------------------------- + +Try running the code in examples/threading/integrate\_main.py + +It accepts 4 arguments: + +.. code-block:; python + + ./integrate_main.py -h + usage: integrate_main.py [-h] [a] [b] [N] [thread_count] + + integrator + + positional arguments: + a + b + N + thread_count + +``./integrate_main.py 0 10 1000000 4`` + +What happens when you change the thread count? What thread count gives +the maximum speed? + +Multiprocessing +--------------- + +multiprocessing provides an API very similar to threading, so the +transition is easy + +use ``multiprocessing.Process`` instead of ``threading.Thread`` + +.. code-block:: python + + import multiprocessing + import os + import time + + def func(): + print "hello from process %s" % os.getpid() + time.sleep(1) + + proc = multiprocessing.Process(target=func, args=()) + proc.start() + proc = multiprocessing.Process(target=func, args=()) + proc.start() + + +Differences with threading +-------------------------- + +Multiprocessing has its own ``multiprocessing.Queue`` which handles +interprocess communication + +Also has its own versions of ``Lock``, ``RLock``, ``Semaphore`` + +.. code-block:: python + + from multiprocessing import Queue, Lock + +``multiprocessing.Pipe`` for 2-way process communication: + +.. code-block:: python + + from multiprocessing import Pipe + parent_conn, child_conn = Pipe() + child_conn.send("foo") + print parent_conn.recv() + +Pooling +------- + +A processing pool contains worker processes with only a configured +number running at one time + +.. code-block:: python + + from multiprocessing import Pool + pool = Pool(processes=4) + +The Pool module has several methods for adding jobs to the pool + +``apply_async(func[, args[, kwargs[, callback]]])`` + +``map_async(func, iterable[, chunksize[, callback]])`` + + +Pooling example +--------------- + +.. rst-class:: small + + .. code-block:: python + + from multiprocessing import Pool + def f(x): + return x*x + if __name__ == '__main__': + pool = Pool(processes=4) + + result = pool.apply_async(f, (10,)) + print(result.get(timeout=1)) + print(pool.map(f, range(10))) + + it = pool.imap(f, range(10)) + print(it.next()) + print(it.next()) + print(it.next(timeout=1)) + + import time + result = pool.apply_async(time.sleep, (10,)) + print(result.get(timeout=1)) + + http://docs.python.org/3/library/multiprocessing.html#module-multiprocessing.pool + +ThreadPool +---------- + +Threading also has a pool + +Confusingly, it lives in the multiprocessing module + +:: + + from multiprocessing.pool import ThreadPool + pool = ThreadPool(processes=4) + + +Threading versus multiprocessing, networking edition +---------------------------------------------------- + +We're going to test making concurrent connections to a web service in: + +``Examples/server/app.py`` + +It is a WSGI application which can be run with Green Unicorn or another +WSGI server + +``$ gunicorn app:app --bind 0.0.0.0:37337`` + +``client-threading.py`` makes 100 threads to contact the web service + +``client-mp.py`` makes 100 processes to contact the web service + +``client-pooled.py`` creates a ThreadPool + +``client-pooled.py`` contains a results Queue, but doesn't use it. Can you +collect all the output from the pool into a single data structure using +this Queue? + + +Other options +------------- + +Traditionally, concurency has been achieved through multiple process +communication and in-process threads, as we've seen + +Another strategy is through micro-threads, implemented via coroutines +and a scheduler + +A coroutine is a generalization of a subroutine which allows multiple +entry points for suspending and resuming execution + +The threading and the multiprocessing modules follow a preemptive +multitasking model: http://en.wikipedia.org/wiki/Preemption_(computing) + +Coroutine based solutions follow a cooperative multitasking +model: http://en.wikipedia.org/wiki/Computer_multitasking#Cooperative_multitasking.2Ftime-sharing + +A Curious Course on Coroutines and Concurrency + + - http://dabeaz.com/coroutines/ + + - http://en.wikipedia.org/wiki/Coroutine + + +With send(), a generator becomes a coroutine +-------------------------------------------- + +.. rst-class:: small + + .. code-block:: python + + def coroutine(n): + try: + while True: + x = (yield) + print(n+x) + except GeneratorExit: + pass + targets = [ + coroutine(10), + coroutine(20), + coroutine(30), + ] + for target in targets: + target.next() + for i in range(5): + for target in targets: + target.send(i) + + http://dabeaz.com/coroutines/Coroutines.pdf + + +Packages using coroutines for micro threads +------------------------------------------- + +By "jumping" to parallel coroutines, our application can simulate true +threads. + +Creating the scheduler which does the jumping is an exercise for the +reader, but look into these packages which handle the dirty work + +- https://pypi.python.org/pypi/greenlet + + - interface for creating coroutine based microthreads + +- http://eventlet.net/ + + - a concurrent networking library, based on + greenlet. Developed for Second Life + +- http://www.gevent.org + + - forked from eventlet. Built on top of greenlet and libevent, + a portable event loop with strong OS support + +- Python 3.4+ : the asyncio module + + +Distributed programming +----------------------- + +A distributed system is one in which components located on networked +computers communicate and coordinate their actions by passing messages + +There are lots of ways to do this at different layers. MPI, \*-RPC, +Pyro, ... + +Celery +------ + +"Celery is an asynchronous task queue/job queue based on distributed +message passing" + +Provides an API for defining tasks, and retrieving results from those +tasks + +Messages are passed via a "message broker", of which Celery supports +several: + +- RabbitMQ (default) +- Redis +- MongoDB +- Amazon SQS +- ... + +Celery worker processes are run on compute nodes, while the main process +farms jobs out to them: + +http://www.celeryproject.org/ + + +Celery in one minute +-------------------- + +.. code-block:: python + + # tasks.py + + from celery import Celery + + celery = Celery('tasks', backend="amqp", broker='amqp://guest@localhost//') + + @celery.task + def add(x, y): + return x + y + + + % celery -A tasks worker --loglevel=INFO -c 4 + + from tasks import add + result = add.delay(2,3) + print result.get() + +Questions? +---------- + diff --git a/_sources/threading-multiprocessing.txt b/_sources/threading-multiprocessing.txt new file mode 100644 index 0000000..93f0b85 --- /dev/null +++ b/_sources/threading-multiprocessing.txt @@ -0,0 +1,840 @@ +.. _threading: + +############################# +Threading and multiprocessing +############################# + +Threading / multiprocessing +=========================== + +Today's topics: + +- Threading / multiprocessing motivation and options +- threading module +- multiprocessing module +- other options + + +Motivations for parallel execution +---------------------------------- + +- Performance + + - Limited by "Amdahl's Law" + http://en.wikipedia.org/wiki/Amdahl%27s_law + + - CPUs aren't getting much faster + +- Event handling + + - If a system handles asynchronous events, a seperate thread of + execution could handle those events and let other threads do other + work + + - Examples: + + - Network applications + + - User interfaces + +Parallel programming can be hard! + +If your problem can be solved sequentially, consider the costs and +benefits before going parallel. + + +Parallelization strategy for performance +---------------------------------------- + +| 1. Break problem down into chunks +| 2. Execute chunks in parallel +| 3. Reassemble output of chunks into result + +.. image:: images/OPP.0108.gif + :align: right + :height: 450px + :alt: multitasking flow diagram + + +Parallelization strategy for performance +---------------------------------------- + +- Not every problem is parallelizable +- There is an optimal number of threads for each problem in each + environment, so make it tunable +- Working concurrently opens up synchronization issues +- Methods for synchronizing threads: + + - locks + - queues + - signaling/messaging mechanisms + + +Threads versus processes in Python +---------------------------------- + +Threads are lightweight processes_, run in the address space of an OS +process, true OS level threads. + +Therefor, a component of a a process. + +.. _processes: https://en.wikipedia.org/wiki/Light-weight_process + +This allows multiple threads access to data in the same scope. + +Threads can not gain the performance advantage of multiple processors +due to the Global Interpreter Lock (GIL) + +But the GIL is released during IO, allowing IO bound processes to +benefit from threading + +Processes +--------- + +A process contains all the instructions and data required to execute +independently, so processes do not share data! + +Mulitple processes best to speed up CPU bound operations. + +The Python interpreter isn't lightweight! + +Communication between processes can be achieved via: + +``multiprocessing.Queue`` + +``multiprocessing.Pipe`` + +and regular IPC (inter-process communication) + +Data moved between processes must be pickleable + + +GIL +--- + +**Global Interpreter Lock** + +This is a lock which must be obtained by each thread before it can +execute, ensuring thread safety + +.. image:: images/gil.png + :width: 100.0% + +.. nextslide:: + +The GIL is released during IO operations, so threads which spend time +waiting on network or disk access can enjoy performance gains + +The GIL is not unlike multitasking in humans, some things can truly be +done in parallel, others have to be done by time slicing. + +Note that potentially blocking or long-running operations, such as I/O, image processing, and NumPy number crunching, happen outside the GIL. Therefore it is only in multithreaded programs that spend a lot of time inside the GIL, interpreting CPython bytecode, that the GIL becomes a bottleneck. But: it can still cause performance degradation. + +Not only will threads not help cpu-bound problems, but it can actually make things *worse*, especially on multi-core machines! + + +.. nextslide:: + + +Some alternative Python implementations such as Jython and IronPython +have no GIL + +cPython and PyPy have one + +David Beazley's talk on the gil + +- https://www.youtube.com/watch?v=Obt-vMVdM8s + +More about the gil + +- http://wiki.python.org/moin/GlobalInterpreterLock + +- https://docs.python.org/3.5/c-api/init.html#threads + +- http://hg.python.org/cpython/file/05e8dde3229c/Python/pystate.c#l761 + +Posted without comment +---------------------- +.. figure:: images/killGIL.jpg + :class: fill + + +A CPU bound problem +------------------- + +Numerically integrate the function +:math:`y =x^2` from 0 to 10. +http://www.wolframalpha.com/input/?i=x%5E2 + +.. image:: images/x2.png + :height: 400px + +Solution: http://www.wolframalpha.com/input/?i=int(x%5E2,0,10) + +Parallel execution example +-------------------------- + +Consider the following code from: ``Examples/integrate/integrate.py`` + +.. code-block:: python + + def f(x): + return x**2 + + def integrate(f, a, b, N): + s = 0 + dx = (b-a)/N + for i in xrange(N): + s += f(a+i*dx) + return s * dx + +Break down the problem into parallelizable chunks, then add the results +together: + +We can do better than this + +The threading module +-------------------- + +Starting threads doesn't take much: + +.. code-block:: python + + import sys + import threading + import time + + def func(): + for i in xrange(5): + print("hello from thread %s" % threading.current_thread().name) + time.sleep(1) + + threads = [] + for i in xrange(3): + thread = threading.Thread(target=func, args=()) + thread.start() + threads.append(thread) + + +.. nextslide:: + + +- The process will exit when the last non-daemon thread exits. +- A thread can be specified as a daemon thread by setting its daemon + attribute: ``thread.daemon = True`` +- daemon threads get cut off at program exit, without any opportunity + for cleanup. But you don't have to track and manage them. Useful for + things like garbage collection, network keepalives, .. +- You can block and wait for a thread to exit with thread.join() + + +Subclassing Thread +------------------ + +You can adding threading capability to your own classes + +Subclass Thread and implement the run method + + +.. code-block:: python + + import threading + + class MyThread(threading.Thread): + + def run(self): + print("hello from %s" % threading.current_thread().name) + + thread = MyThread() + thread.start() + + +Race Conditions +--------------- + +In the last example we saw threads competing for access to stdout. + +Worse, if competing threads try to update the same value, we might get +an unexpected race condition + +Race conditions occur when multiple statements need to execute +atomically, but get interrupted midway + +See ``Examples/race_condition.py`` + +No race condition +------------------ + ++--------------------+--------------------+--------------------+--------------------+ +| Thread 1 | Thread 2 | | Integer value | ++====================+====================+====================+====================+ +| | | | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| read value | | ← | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| increase value | | | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| write back | | → | 1 | ++--------------------+--------------------+--------------------+--------------------+ +| | read value | ← | 1 | ++--------------------+--------------------+--------------------+--------------------+ +| | increase value | | 1 | ++--------------------+--------------------+--------------------+--------------------+ +| | write back | → | 2 | ++--------------------+--------------------+--------------------+--------------------+ + +Race Condition! +--------------- + ++--------------------+--------------------+--------------------+--------------------+ +| Thread 1 | Thread 2 | | Integer value | ++====================+====================+====================+====================+ +| | | | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| read value | | ← | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| | read value | ← | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| increase value | | | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| | increase value | | 0 | ++--------------------+--------------------+--------------------+--------------------+ +| write back | | → | 1 | ++--------------------+--------------------+--------------------+--------------------+ +| | write back | → | 1 | ++--------------------+--------------------+--------------------+--------------------+ + +http://en.wikipedia.org/wiki/Race_condition + +Deadlocks +--------- + +Synchronization and Critical Sections are used to control race +conditions + +But they introduce other potential problems... + +like: http://en.wikipedia.org/wiki/Deadlock + +"A deadlock is a situation in which two or more competing actions are +each waiting for the other to finish, and thus neither ever does." + +*When two trains approach each other at a crossing, both shall come to a +full stop and neither shall start up again until the other has gone* + +See also *Livelock*: + +*Two people meet in a narrow corridor, and each +tries to be polite by moving aside to let the other pass, but they end +up swaying from side to side without making any progress because they +both repeatedly move the same way at the same time.* + + +Locks +----- + +Lock objects allow threads to control access to a resource until they're +done with it + +This is known as mutual exclusion, often called mutex + +Python 2 has a deprecated module called mutex for this. Use a Lock +instead. + +A Lock has two states: locked and unlocked + +If multiple threads have access to the same Lock, they can police +themselves by calling its ``.acquire()`` and ``.release()`` methods + +If a Lock is locked, .acquire will block until it becomes unlocked + +These threads will wait in line politely for access to the statements in +f() + +.. nextslide:: + +.. code-block:: python + + import threading + import time + + lock = threading.Lock() + + def f(): + lock.acquire() + print("%s got lock" % threading.current_thread().name) + time.sleep(1) + lock.release() + + threading.Thread(target=f).start() + threading.Thread(target=f).start() + threading.Thread(target=f).start() + + +Nonblocking Locking +------------------- + +``.acquire()`` will return True if it successfully acquires a lock + +Its first argument is a boolean which specifies whether a lock should +block or not. The default is ``True`` + +.. code-block:: python + + import threading + lock = threading.Lock() + lock.acquire() + if not lock.acquire(False): + print("couldn't get lock") + lock.release() + if lock.acquire(False): + print("got lock") + + +``threading.RLock`` - Reentrant Lock +------------------------------------ + +Useful for recursive algorithms, a thread-specific count of the locks is +maintained + +A reentrant lock can be acquired multiple times by the same thread + +``Lock.release()`` must be called the same number of times as ``Lock.acquire()`` +by that thread + + +``threading.Semaphore`` +----------------------- + +Like an ``RLock``, but in reverse + +A Semaphore is given an initial counter value, defaulting to 1 + +Each call to ``acquire()`` decrements the counter, ``release()`` increments it + +If ``acquire()`` is called on a Semaphore with a counter of 0, it will block +until the Semaphore counter is greater than 0. + +Useful for controlling the maximum number of threads allowed to access a +resource simultaneously + +http://en.wikipedia.org/wiki/Semaphore_(programming) + +.. image:: images/flags.jpg + :height: 250px + + + +Locking Exercise +---------------- + +In: ``Examples/lock/stdout_writer.py`` + +Multiple threads in the script write to stdout, and their output gets +jumbled + +1. Add a locking mechanism to give each thread exclusive access to + stdout + +2. Try adding a Semaphore to allow 2 threads access at once + + +Managing thread results +----------------------- + +We need a thread safe way of storing results from multiple threads of +execution. That is provided by the Queue module. + +Queues allow multiple producers and multiple consumers to exchange data +safely + +Size of the queue is managed with the maxsize kwarg + +It will block consumers if empty and block producers if full + +If maxsize is less than or equal to zero, the queue size is infinite + +.. nextslide:: + +.. code-block:: python + + from Queue import Queue + q = Queue(maxsize=10) + q.put(37337) + block = True + timeout = 2 + print(q.get(block, timeout)) + +- http://docs.python.org/3/library/threading.html +- http://docs.python.org/3/library/queue.html + +Other Queue types +----------------- + +``Queue.LifoQueue`` + + - Last In, First Out + +``Queue.PriorityQueue`` + + - Lowest valued entries are retrieved first + +One pattern for ``PriorityQueue`` is to insert entries of form data by +inserting the tuple: + +``(priority_number, data)`` + +Threading example +----------------- + +See Examples/threading/integrate_main.py + +.. code-block:: python + + #!/usr/bin/env python + + import argparse + import os + import sys + import threading + import Queue + + sys.path.append(os.path.join(os.path.dirname(__file__), "..")) + from integrate.integrate import integrate, f + from decorators.decorators import timer + +.. nextslide:: + +.. code-block:: python + + @timer + def threading_integrate(f, a, b, N, thread_count=2): + """break work into two chunks""" + N_chunk = int(float(N) / thread_count) + dx = float(b-a) / thread_count + + results = Queue.Queue() + + def worker(*args): + results.put(integrate(*args)) + + threads = [] + for i in xrange(thread_count): + x0 = dx*i + x1 = x0 + dx + thread = threading.Thread(target=worker, args=(f, x0, x1, N_chunk)) + thread.start() + print "Thread %s started" % thread.name + # thread1.join() + return sum( (results.get() for i in xrange(thread_count) )) + +.. nextslide:: + +.. code-block:: python + + if __name__ == "__main__": + parser = argparse.ArgumentParser(description='integrator') + parser.add_argument('a', nargs='?', type=float, default=0.0) + parser.add_argument('b', nargs='?', type=float, default=10.0) + parser.add_argument('N', nargs='?', type=int, default=10**7) + parser.add_argument('thread_count', nargs='?', type=int, default=2) + + args = parser.parse_args() + a = args.a + b = args.b + N = args.N + thread_count = args.thread_count + + print("Numerical solution with N=%(N)d : %(x)f" % \ + {'N': N, 'x': threading_integrate(f, a, b, N, thread_count=thread_count)}) + + +Threading on a CPU bound problem +-------------------------------- + +Try running the code in examples/threading/integrate\_main.py + +It accepts 4 arguments: + +.. code-block:; python + + ./integrate_main.py -h + usage: integrate_main.py [-h] [a] [b] [N] [thread_count] + + integrator + + positional arguments: + a + b + N + thread_count + +``./integrate_main.py 0 10 1000000 4`` + +What happens when you change the thread count? What thread count gives +the maximum speed? + +Multiprocessing +--------------- + +multiprocessing provides an API very similar to threading, so the +transition is easy + +use ``multiprocessing.Process`` instead of ``threading.Thread`` + +.. code-block:: python + + import multiprocessing + import os + import time + + def func(): + print "hello from process %s" % os.getpid() + time.sleep(1) + + proc = multiprocessing.Process(target=func, args=()) + proc.start() + proc = multiprocessing.Process(target=func, args=()) + proc.start() + + +Differences with threading +-------------------------- + +Multiprocessing has its own ``multiprocessing.Queue`` which handles +interprocess communication + +Also has its own versions of ``Lock``, ``RLock``, ``Semaphore`` + +.. code-block:: python + + from multiprocessing import Queue, Lock + +``multiprocessing.Pipe`` for 2-way process communication: + +.. code-block:: python + + from multiprocessing import Pipe + parent_conn, child_conn = Pipe() + child_conn.send("foo") + print parent_conn.recv() + +Pooling +------- + +A processing pool contains worker processes with only a configured +number running at one time + +.. code-block:: python + + from multiprocessing import Pool + pool = Pool(processes=4) + +The Pool module has several methods for adding jobs to the pool + +``apply_async(func[, args[, kwargs[, callback]]])`` + +``map_async(func, iterable[, chunksize[, callback]])`` + + +Pooling example +--------------- + +.. rst-class:: small + + .. code-block:: python + + from multiprocessing import Pool + def f(x): + return x*x + if __name__ == '__main__': + pool = Pool(processes=4) + + result = pool.apply_async(f, (10,)) + print(result.get(timeout=1)) + print(pool.map(f, range(10))) + + it = pool.imap(f, range(10)) + print(it.next()) + print(it.next()) + print(it.next(timeout=1)) + + import time + result = pool.apply_async(time.sleep, (10,)) + print(result.get(timeout=1)) + + http://docs.python.org/3/library/multiprocessing.html#module-multiprocessing.pool + +ThreadPool +---------- + +Threading also has a pool + +Confusingly, it lives in the multiprocessing module + +:: + + from multiprocessing.pool import ThreadPool + pool = ThreadPool(processes=4) + + +Threading versus multiprocessing, networking edition +---------------------------------------------------- + +We're going to test making concurrent connections to a web service in: + +``Examples/server/app.py`` + +It is a WSGI application which can be run with Green Unicorn or another +WSGI server + +``$ gunicorn app:app --bind 0.0.0.0:37337`` + +``client-threading.py`` makes 100 threads to contact the web service + +``client-mp.py`` makes 100 processes to contact the web service + +``client-pooled.py`` creates a ThreadPool + +``client-pooled.py`` contains a results Queue, but doesn't use it. Can you +collect all the output from the pool into a single data structure using +this Queue? + + +Other options +------------- + +Traditionally, concurency has been achieved through multiple process +communication and in-process threads, as we've seen + +Another strategy is through micro-threads, implemented via coroutines +and a scheduler + +A coroutine is a generalization of a subroutine which allows multiple +entry points for suspending and resuming execution + +The threading and the multiprocessing modules follow a preemptive +multitasking model: http://en.wikipedia.org/wiki/Preemption_(computing) + +Coroutine based solutions follow a cooperative multitasking +model: http://en.wikipedia.org/wiki/Computer_multitasking#Cooperative_multitasking.2Ftime-sharing + +A Curious Course on Coroutines and Concurrency + + - http://dabeaz.com/coroutines/ + + - http://en.wikipedia.org/wiki/Coroutine + + +With send(), a generator becomes a coroutine +-------------------------------------------- + +.. rst-class:: small + + .. code-block:: python + + def coroutine(n): + try: + while True: + x = (yield) + print(n+x) + except GeneratorExit: + pass + targets = [ + coroutine(10), + coroutine(20), + coroutine(30), + ] + for target in targets: + target.next() + for i in range(5): + for target in targets: + target.send(i) + + http://dabeaz.com/coroutines/Coroutines.pdf + + +Packages using coroutines for micro threads +------------------------------------------- + +By "jumping" to parallel coroutines, our application can simulate true +threads. + +Creating the scheduler which does the jumping is an exercise for the +reader, but look into these packages which handle the dirty work + +- https://pypi.python.org/pypi/greenlet + + - interface for creating coroutine based microthreads + +- http://eventlet.net/ + + - a concurrent networking library, based on + greenlet. Developed for Second Life + +- http://www.gevent.org + + - forked from eventlet. Built on top of greenlet and libevent, + a portable event loop with strong OS support + +- Python 3.4+ : the asyncio module + + +Distributed programming +----------------------- + +A distributed system is one in which components located on networked +computers communicate and coordinate their actions by passing messages + +There are lots of ways to do this at different layers. MPI, \*-RPC, +Pyro, ... + +Celery +------ + +"Celery is an asynchronous task queue/job queue based on distributed +message passing" + +Provides an API for defining tasks, and retrieving results from those +tasks + +Messages are passed via a "message broker", of which Celery supports +several: + +- RabbitMQ (default) +- Redis +- MongoDB +- Amazon SQS +- ... + +Celery worker processes are run on compute nodes, while the main process +farms jobs out to them: + +http://www.celeryproject.org/ + + +Celery in one minute +-------------------- + +.. code-block:: python + + # tasks.py + + from celery import Celery + + celery = Celery('tasks', backend="amqp", broker='amqp://guest@localhost//') + + @celery.task + def add(x, y): + return x + y + + + % celery -A tasks worker --loglevel=INFO -c 4 + + from tasks import add + result = add.delay(2,3) + print result.get() + +Questions? +---------- + diff --git a/_sources/unicode.rst.txt b/_sources/unicode.rst.txt new file mode 100644 index 0000000..7e7a244 --- /dev/null +++ b/_sources/unicode.rst.txt @@ -0,0 +1,534 @@ + +.. _unicode: + +=================== +Unicode in Python 2 +=================== + +.. rst-class:: medium + + A quick run-down of Unicode, + + its use in Python 2, + + and some of the gotchas that arise. + + | + | - Chris Barker + | + +History +======= + +A bit about where all this mess came from... + + +What the heck is Unicode anyway? +--------------------------------- + +* First there was chaos... + + * Different machines used different encodings + +* Then there was ASCII -- and all was good (7 bit), 127 characters + + * (for English speakers, anyway) + +* But each vendor used the top half (127-255) for different things. + + * MacRoman, Windows 1252, etc... + + * There is now "latin-1", but still a lot of old files around + +* Non-Western European languages required totally incompatible 1-byte encodings + +* No way to mix languages with different alphabets. + + +Enter Unicode +-------------- + +The Unicode idea is pretty simple: + * one "code point" for all characters in all languages + +But how do you express that in bytes? + * Early days: we can fit all the code points in a two byte integer (65536 characters) + + * Turns out that didn't work -- now need 32 bit integer to hold all of unicode "raw" (UTC-4) + +Enter "encodings": + * An encoding is a way to map specific bytes to a code point. + + * Each code point can have one or more bytes. + + +Unicode +-------- + +A good start: + +The Absolute Minimum Every Software Developer Absolutely, +Positively Must Know About Unicode and Character Sets (No Excuses!) + +http://www.joelonsoftware.com/articles/Unicode.html + + +.. nextslide:: + +**Everything is Bytes** + +* If it's on disk or on a network, it's bytes + +* Python provides some abstractions to make it easier to deal with bytes + +**Unicode is a biggie** + +actually, dealing with numbers rather than bytes is big + +-- but we take that for granted + + +Mechanics +========= + +What are strings? +----------------- + +Py2 strings are sequences of bytes + +Unicode strings are sequences of platonic characters + +It's almost one code point per character -- but there are complications +with combined characters: accents, etc. + +Platonic characters cannot be written to disk or network! + +(ANSI: one character == one byte -- so easy!) + + +Strings vs unicode +------------------- + +Python 2 has two types that let you work with text: + +* ``str`` + +* ``unicode`` + +And two ways to work with binary data: + +* ``str`` + +* ``bytes()`` (and ``bytearray``) + +**but:** + +.. code-block:: ipython + + In [86]: str is bytes + Out[86]: True + +``bytes`` is there for py3 compatibility -- but it's good for making your +intentions clear, too. + + +Unicode +-------- + +The ``unicode`` object lets you work with characters + +It has all the same methods as the string object. + +**"encoding"** is converting from a unicode object to bytes + +**"decoding"** is converting from bytes to a unicode object + +(sometimes this feels backwards...) + +And can get even more confusing with py2 strings being *both* text and bytes! + +Using unicode in Py2 +--------------------- + +Built in functions + +.. code-block:: python + + ord() + chr() + unichr() + str() + unicode() + +The codecs module + +.. code-block:: python + + import codecs + codecs.encode() + codecs.decode() + codecs.open() # better to use ``io.open`` + + +Encoding and Decoding +---------------------- + +**Encoding:** text to bytes -- you get a bytes (str) object + +.. code-block:: ipython + + In [17]: u"this".encode('utf-8') + Out[17]: 'this' + + In [18]: u"this".encode('utf-16') + Out[18]: '\xff\xfet\x00h\x00i\x00s\x00' + +**Decoding** bytes to text -- you get a unicode object + +.. code-block:: ipython + + In [2]: text = '\xff\xfe."+"x\x00\xb2\x00'.decode('utf-16') + + In [3]: type(text) + Out[3]: unicode + + In [4]: print text + ∮∫x² + + + +Unicode Literals +------------------ + +1) Use unicode in your source files: + +.. code-block:: python + + # -*- coding: utf-8 -*- + +2) escape the unicode characters: + +.. code-block:: python + + print u"The integral sign: \u222B" + print u"The integral sign: \N{integral}" + +Lots of tables of code points online: + +One example: + http://inamidst.com/stuff/unidata/ + +:download:`hello_unicode.py <./resources/hello_unicode.py>`. + + +Using Unicode +-------------- + +Use ``unicode`` objects in all your code + +**Decode on input** + +**Encode on output** + +Many packages do this for you: *XML processing, databases, ...* + +**Gotcha:** + +Python has a default encoding (usually ascii) + +.. code-block:: ipython + + In [2]: sys.getdefaultencoding() + Out[2]: 'ascii' + +The default encoding will get used in unexpected places! + +Using unicode everywhere +------------------------- + +Python 2.6 and above have a nice feature to make it easier to use unicode everywhere + +.. code-block:: python + + from __future__ import unicode_literals + +After running that line, the ``u''`` is assumed + +.. code-block:: ipython + + In [1]: s = "this is a regular py2 string" + In [2]: print type(s) + + + In [3]: from __future__ import unicode_literals + In [4]: s = "this is now a unicode string" + In [5]: type(s) + Out[5]: unicode + +NOTE: You can still get py2 strings from other sources! + + +Encodings +---------- + +What encoding should I use??? + +There are a lot: + +http://en.wikipedia.org/wiki/Comparison_of_Unicode_encodings + +But only a couple you are likely to need: + +* utf-8 (``*nix``) +* utf-16 (Windows) + +and of course, still the one-bytes ones. + +* ASCII +* Latin-1 + +UTF-8 +------- + +Probably the one you'll use most -- most common in Internet protocols (xml, JSON, etc.) + +Nice properties: + +* ASCII compatible: first 127 characters are the same + +* Any ascii string is a utf-8 string + +* compact for mostly-english text. + +Gotchas: + +* "higher" code points may use more than one byte: up to 4 for one character + +* ASCII compatible means in may work with default encoding in tests -- but then blow up with real data... + +UTF-16 +-------- + +Kind of like UTF-8, except it uses at least 16bits (2 bytes) for each character: not ASCII compatible. + +But is still needs more than two bytes for some code points, so you still can't process + +In C/C++ held in a "wide char" or "wide string". + +MS Windows uses UTF-16, as does (I think) Java. + +UTF-16 criticism +----------------- + +There is a lot of criticism on the net about UTF-16 -- it's kind of the worst of both worlds: + +* You can't assume every character is the same number of bytes +* It takes up more memory than UTF-8 + +`UTF Considered Harmful `_ + +But to be fair: + +Early versions of Unicode: everything fit into two bytes (65536 code points). MS and Java were fairly early adopters, and it seemed simple enough to just use 2 bytes per character. + +When it turned out that 4 bytes were really needed, they were kind of stuck in the middle. + +Latin-1 +-------- + +**NOT Unicode**: + +a 1-byte per char encoding. + +* Superset of ASCII suitable for Western European languages. + +* The most common one-byte per char encoding for European text. + +* Nice property -- every byte value from 0 to 255 is a valid character ( at least in Python ) + +.. nextslide:: + +* You will never get an UnicodeDecodeError if you try to decode arbitrary bytes with latin-1. + +* And it can "round-trip" through a unicode object. + +* Useful if you don't know the encoding -- at least it won't raise an Exception + +* Useful if you need to work with combined text+binary data. + +:download:`latin1_test.py <./resources/latin1_test.py>`. + + +Unicode Docs +-------------- + +Python Docs Unicode HowTo: + +http://docs.python.org/howto/unicode.html + +"Reading Unicode from a file is therefore simple" + +use io.open: + +.. code-block:: python + + from io import open + io.open('unicode.rst', encoding='utf-8') + for line in f: + print repr(line) + +(https://docs.python.org/2/library/io.html#module-interface) + +Encodings Built-in to Python: + http://docs.python.org/2/library/codecs.html#standard-encodings + + +Gotchas in Python 2 +-------------------- + +file names, etc: + +If you pass in unicode, you get unicode + +.. code-block:: ipython + + In [9]: os.listdir('./') + Out[9]: ['hello_unicode.py', 'text.utf16', 'text.utf32'] + + In [10]: os.listdir(u'./') + Out[10]: [u'hello_unicode.py', u'text.utf16', u'text.utf32'] + +Python deals with the file system encoding for you... + +But: some more obscure calls don't support unicode filenames: + +``os.statvfs()`` (http://bugs.python.org/issue18695) + + +.. nextslide:: + +Exception messages: + + * Py2 Exceptions use str when they print messages. + + * But what if you pass in a unicode object? + + * It is encoded with the default encoding. + + * ``UnicodeDecodeError`` Inside an Exception???? + + NOPE: it swallows it instead. + +:download:`exception_test.py <./resources/exception_test.py>`. + +Unicode in Python 3 +---------------------- + +The "string" object is unicode. + +Py3 has two distinct concepts: + +* "text" -- uses the str object (which is always unicode!) +* "binary data" -- uses bytes or bytearray + +Everything that's about text is unicode. + +Everything that requires binary data uses bytes. + +It's all much cleaner. + +(by the way, the recent implementations are very efficient...) + + +Exercises +========= + +Basic Unicode LAB +------------------- + +* Find some nifty non-ascii characters you might use. + + - Create a unicode object with them in two different ways. + - :download:`here <./resources/hello_unicode.py>` is one example + +* Read the contents into unicode objects: + + - :download:`ICanEatGlass.utf8.txt <./resources/ICanEatGlass.utf8.txt>` + - :download:`ICanEatGlass.utf16.txt <./resources/ICanEatGlass.utf16.txt>` + +and / or + + - :download:`text.utf8 <./resources/text.utf8>` + - :download:`text.utf16 <./resources/text.utf16>` + - :download:`text.utf32 <./resources/text.utf32>` + +* write some of the text from the first exercise to file -- read that file back in. + +.. nextslide:: Some Help + +reference: http://inamidst.com/stuff/unidata/ + +NOTE: if your terminal does not support unicode -- you'll get an error trying +to print. Try a different terminal or IDE, or google for a solution. + +Challenge Unicode LAB +---------------------- + +We saw this earlier + +.. code-block:: ipython + + In [38]: u'to \N{INFINITY} and beyond!'.decode('utf-8') + --------------------------------------------------------------------------- + UnicodeEncodeError Traceback (most recent call last) + in () + ----> 1 u'to \N{INFINITY} and beyond!'.decode('utf-8') + + /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/utf_8.pyc in decode(input, errors) + 14 + 15 def decode(input, errors='strict'): + ---> 16 return codecs.utf_8_decode(input, errors, True) + 17 + 18 class IncrementalEncoder(codecs.IncrementalEncoder): + + UnicodeEncodeError: 'ascii' codec can't encode character u'\u221e' in position 3: ordinal not in range(128) + +.. nextslide:: + +But why would you **decode** a unicode object? + +And it should be a no-op -- why the exception? + +And why 'ascii'? I specified 'utf-8'! + +It's there for backward compatibility + +What's happening under the hood + +.. code-block:: python + + u'to \N{INFINITY} and beyond!'.encode().decode('utf-8') + +It encodes with the default encoding (ascii), then decodes + +In this case, it barfs on attempting to encode to 'ascii' + +.. nextslide:: + +So never call decode on a unicode object! + +But what if someone passes one into a function of yours that's expecting a py2 string? + +Type checking and converting -- yeach! + +Read: + +http://axialcorps.com/2014/03/20/unicode-str/ + +See if you can figure out the decorators: + +:download:`unicodify.py <./resources/unicodify.py>`. + + +(This is advanced Python JuJu: Aren't you glad I didn't ask you to write that yourself?) + diff --git a/_sources/unicode.txt b/_sources/unicode.txt new file mode 100644 index 0000000..7e7a244 --- /dev/null +++ b/_sources/unicode.txt @@ -0,0 +1,534 @@ + +.. _unicode: + +=================== +Unicode in Python 2 +=================== + +.. rst-class:: medium + + A quick run-down of Unicode, + + its use in Python 2, + + and some of the gotchas that arise. + + | + | - Chris Barker + | + +History +======= + +A bit about where all this mess came from... + + +What the heck is Unicode anyway? +--------------------------------- + +* First there was chaos... + + * Different machines used different encodings + +* Then there was ASCII -- and all was good (7 bit), 127 characters + + * (for English speakers, anyway) + +* But each vendor used the top half (127-255) for different things. + + * MacRoman, Windows 1252, etc... + + * There is now "latin-1", but still a lot of old files around + +* Non-Western European languages required totally incompatible 1-byte encodings + +* No way to mix languages with different alphabets. + + +Enter Unicode +-------------- + +The Unicode idea is pretty simple: + * one "code point" for all characters in all languages + +But how do you express that in bytes? + * Early days: we can fit all the code points in a two byte integer (65536 characters) + + * Turns out that didn't work -- now need 32 bit integer to hold all of unicode "raw" (UTC-4) + +Enter "encodings": + * An encoding is a way to map specific bytes to a code point. + + * Each code point can have one or more bytes. + + +Unicode +-------- + +A good start: + +The Absolute Minimum Every Software Developer Absolutely, +Positively Must Know About Unicode and Character Sets (No Excuses!) + +http://www.joelonsoftware.com/articles/Unicode.html + + +.. nextslide:: + +**Everything is Bytes** + +* If it's on disk or on a network, it's bytes + +* Python provides some abstractions to make it easier to deal with bytes + +**Unicode is a biggie** + +actually, dealing with numbers rather than bytes is big + +-- but we take that for granted + + +Mechanics +========= + +What are strings? +----------------- + +Py2 strings are sequences of bytes + +Unicode strings are sequences of platonic characters + +It's almost one code point per character -- but there are complications +with combined characters: accents, etc. + +Platonic characters cannot be written to disk or network! + +(ANSI: one character == one byte -- so easy!) + + +Strings vs unicode +------------------- + +Python 2 has two types that let you work with text: + +* ``str`` + +* ``unicode`` + +And two ways to work with binary data: + +* ``str`` + +* ``bytes()`` (and ``bytearray``) + +**but:** + +.. code-block:: ipython + + In [86]: str is bytes + Out[86]: True + +``bytes`` is there for py3 compatibility -- but it's good for making your +intentions clear, too. + + +Unicode +-------- + +The ``unicode`` object lets you work with characters + +It has all the same methods as the string object. + +**"encoding"** is converting from a unicode object to bytes + +**"decoding"** is converting from bytes to a unicode object + +(sometimes this feels backwards...) + +And can get even more confusing with py2 strings being *both* text and bytes! + +Using unicode in Py2 +--------------------- + +Built in functions + +.. code-block:: python + + ord() + chr() + unichr() + str() + unicode() + +The codecs module + +.. code-block:: python + + import codecs + codecs.encode() + codecs.decode() + codecs.open() # better to use ``io.open`` + + +Encoding and Decoding +---------------------- + +**Encoding:** text to bytes -- you get a bytes (str) object + +.. code-block:: ipython + + In [17]: u"this".encode('utf-8') + Out[17]: 'this' + + In [18]: u"this".encode('utf-16') + Out[18]: '\xff\xfet\x00h\x00i\x00s\x00' + +**Decoding** bytes to text -- you get a unicode object + +.. code-block:: ipython + + In [2]: text = '\xff\xfe."+"x\x00\xb2\x00'.decode('utf-16') + + In [3]: type(text) + Out[3]: unicode + + In [4]: print text + ∮∫x² + + + +Unicode Literals +------------------ + +1) Use unicode in your source files: + +.. code-block:: python + + # -*- coding: utf-8 -*- + +2) escape the unicode characters: + +.. code-block:: python + + print u"The integral sign: \u222B" + print u"The integral sign: \N{integral}" + +Lots of tables of code points online: + +One example: + http://inamidst.com/stuff/unidata/ + +:download:`hello_unicode.py <./resources/hello_unicode.py>`. + + +Using Unicode +-------------- + +Use ``unicode`` objects in all your code + +**Decode on input** + +**Encode on output** + +Many packages do this for you: *XML processing, databases, ...* + +**Gotcha:** + +Python has a default encoding (usually ascii) + +.. code-block:: ipython + + In [2]: sys.getdefaultencoding() + Out[2]: 'ascii' + +The default encoding will get used in unexpected places! + +Using unicode everywhere +------------------------- + +Python 2.6 and above have a nice feature to make it easier to use unicode everywhere + +.. code-block:: python + + from __future__ import unicode_literals + +After running that line, the ``u''`` is assumed + +.. code-block:: ipython + + In [1]: s = "this is a regular py2 string" + In [2]: print type(s) + + + In [3]: from __future__ import unicode_literals + In [4]: s = "this is now a unicode string" + In [5]: type(s) + Out[5]: unicode + +NOTE: You can still get py2 strings from other sources! + + +Encodings +---------- + +What encoding should I use??? + +There are a lot: + +http://en.wikipedia.org/wiki/Comparison_of_Unicode_encodings + +But only a couple you are likely to need: + +* utf-8 (``*nix``) +* utf-16 (Windows) + +and of course, still the one-bytes ones. + +* ASCII +* Latin-1 + +UTF-8 +------- + +Probably the one you'll use most -- most common in Internet protocols (xml, JSON, etc.) + +Nice properties: + +* ASCII compatible: first 127 characters are the same + +* Any ascii string is a utf-8 string + +* compact for mostly-english text. + +Gotchas: + +* "higher" code points may use more than one byte: up to 4 for one character + +* ASCII compatible means in may work with default encoding in tests -- but then blow up with real data... + +UTF-16 +-------- + +Kind of like UTF-8, except it uses at least 16bits (2 bytes) for each character: not ASCII compatible. + +But is still needs more than two bytes for some code points, so you still can't process + +In C/C++ held in a "wide char" or "wide string". + +MS Windows uses UTF-16, as does (I think) Java. + +UTF-16 criticism +----------------- + +There is a lot of criticism on the net about UTF-16 -- it's kind of the worst of both worlds: + +* You can't assume every character is the same number of bytes +* It takes up more memory than UTF-8 + +`UTF Considered Harmful `_ + +But to be fair: + +Early versions of Unicode: everything fit into two bytes (65536 code points). MS and Java were fairly early adopters, and it seemed simple enough to just use 2 bytes per character. + +When it turned out that 4 bytes were really needed, they were kind of stuck in the middle. + +Latin-1 +-------- + +**NOT Unicode**: + +a 1-byte per char encoding. + +* Superset of ASCII suitable for Western European languages. + +* The most common one-byte per char encoding for European text. + +* Nice property -- every byte value from 0 to 255 is a valid character ( at least in Python ) + +.. nextslide:: + +* You will never get an UnicodeDecodeError if you try to decode arbitrary bytes with latin-1. + +* And it can "round-trip" through a unicode object. + +* Useful if you don't know the encoding -- at least it won't raise an Exception + +* Useful if you need to work with combined text+binary data. + +:download:`latin1_test.py <./resources/latin1_test.py>`. + + +Unicode Docs +-------------- + +Python Docs Unicode HowTo: + +http://docs.python.org/howto/unicode.html + +"Reading Unicode from a file is therefore simple" + +use io.open: + +.. code-block:: python + + from io import open + io.open('unicode.rst', encoding='utf-8') + for line in f: + print repr(line) + +(https://docs.python.org/2/library/io.html#module-interface) + +Encodings Built-in to Python: + http://docs.python.org/2/library/codecs.html#standard-encodings + + +Gotchas in Python 2 +-------------------- + +file names, etc: + +If you pass in unicode, you get unicode + +.. code-block:: ipython + + In [9]: os.listdir('./') + Out[9]: ['hello_unicode.py', 'text.utf16', 'text.utf32'] + + In [10]: os.listdir(u'./') + Out[10]: [u'hello_unicode.py', u'text.utf16', u'text.utf32'] + +Python deals with the file system encoding for you... + +But: some more obscure calls don't support unicode filenames: + +``os.statvfs()`` (http://bugs.python.org/issue18695) + + +.. nextslide:: + +Exception messages: + + * Py2 Exceptions use str when they print messages. + + * But what if you pass in a unicode object? + + * It is encoded with the default encoding. + + * ``UnicodeDecodeError`` Inside an Exception???? + + NOPE: it swallows it instead. + +:download:`exception_test.py <./resources/exception_test.py>`. + +Unicode in Python 3 +---------------------- + +The "string" object is unicode. + +Py3 has two distinct concepts: + +* "text" -- uses the str object (which is always unicode!) +* "binary data" -- uses bytes or bytearray + +Everything that's about text is unicode. + +Everything that requires binary data uses bytes. + +It's all much cleaner. + +(by the way, the recent implementations are very efficient...) + + +Exercises +========= + +Basic Unicode LAB +------------------- + +* Find some nifty non-ascii characters you might use. + + - Create a unicode object with them in two different ways. + - :download:`here <./resources/hello_unicode.py>` is one example + +* Read the contents into unicode objects: + + - :download:`ICanEatGlass.utf8.txt <./resources/ICanEatGlass.utf8.txt>` + - :download:`ICanEatGlass.utf16.txt <./resources/ICanEatGlass.utf16.txt>` + +and / or + + - :download:`text.utf8 <./resources/text.utf8>` + - :download:`text.utf16 <./resources/text.utf16>` + - :download:`text.utf32 <./resources/text.utf32>` + +* write some of the text from the first exercise to file -- read that file back in. + +.. nextslide:: Some Help + +reference: http://inamidst.com/stuff/unidata/ + +NOTE: if your terminal does not support unicode -- you'll get an error trying +to print. Try a different terminal or IDE, or google for a solution. + +Challenge Unicode LAB +---------------------- + +We saw this earlier + +.. code-block:: ipython + + In [38]: u'to \N{INFINITY} and beyond!'.decode('utf-8') + --------------------------------------------------------------------------- + UnicodeEncodeError Traceback (most recent call last) + in () + ----> 1 u'to \N{INFINITY} and beyond!'.decode('utf-8') + + /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/utf_8.pyc in decode(input, errors) + 14 + 15 def decode(input, errors='strict'): + ---> 16 return codecs.utf_8_decode(input, errors, True) + 17 + 18 class IncrementalEncoder(codecs.IncrementalEncoder): + + UnicodeEncodeError: 'ascii' codec can't encode character u'\u221e' in position 3: ordinal not in range(128) + +.. nextslide:: + +But why would you **decode** a unicode object? + +And it should be a no-op -- why the exception? + +And why 'ascii'? I specified 'utf-8'! + +It's there for backward compatibility + +What's happening under the hood + +.. code-block:: python + + u'to \N{INFINITY} and beyond!'.encode().decode('utf-8') + +It encodes with the default encoding (ascii), then decodes + +In this case, it barfs on attempting to encode to 'ascii' + +.. nextslide:: + +So never call decode on a unicode object! + +But what if someone passes one into a function of yours that's expecting a py2 string? + +Type checking and converting -- yeach! + +Read: + +http://axialcorps.com/2014/03/20/unicode-str/ + +See if you can figure out the decorators: + +:download:`unicodify.py <./resources/unicodify.py>`. + + +(This is advanced Python JuJu: Aren't you glad I didn't ask you to write that yourself?) + diff --git a/_sources/weak_references.rst.txt b/_sources/weak_references.rst.txt new file mode 100644 index 0000000..49d4520 --- /dev/null +++ b/_sources/weak_references.rst.txt @@ -0,0 +1,378 @@ +.. _weak_references: + +********************************************* +Python Memory Management and Weak References +********************************************* + +Chris Barker + +``PythonCHB@gmail.com`` + +``https://github.com/PythonCHB`` + +================== +Memory Management +================== + +.. rst-class:: left + + * You don't want python objects that are no longer in use taking up memory. + * You don't want to keep track of all that yourself. + * Most "scripting languages" or "virtual machines" have some sort of + automated memory management + +.. rst-class:: center medium + + Many ways to do "Garbage Collection" + + +Reference Counting +-------------------- + +How memory is managed is not part of the Python language spec: + * Jython uses the JVM + * Iron Python uses the CLR + - Both are garbage collected + * PyPy uses Minimark_ + + .. _Minimark: https://pypy.readthedocs.org/en/release-2.4.x/garbage_collection.html#minimark-gc + + +The CPython interpreter uses a reference counting scheme: + * Every time there is a new reference to a Python object, its reference + count is increased + * Every time a reference is removed -- the count is decreased + * When the reference count goes to zero: the object is deleted + (memory freed) + + +What makes a reference? +------------------------ + +* Binding to a name:: + + x = an_object + +* Putting it in a container:: + + l.append(an_object) + +* Passing it to a function:: + + func(an_object) + +Most of the time, you don't need to think about this at all. + + +How do I see what's going on? +------------------------------ + +.. code-block:: python + + + import sys + sys.getrefcount(object) + + +**NOTE:** This will always return one more than you'd expect, as passing the object to the function increases its refcount by one: + +.. code-block:: ipython + + In [5]: a = [] + + In [6]: sys.getrefcount(a) + Out[6]: 2 + +The Heisenberg Uncertainty Principle: + - you can't observe it without altering it + + +Playing with References +------------------------ + +(live demo) + +.. code-block:: ipython + + In [7]: a = [] + + In [8]: sys.getrefcount(a) + Out[8]: 2 + + In [9]: b = a + + In [10]: sys.getrefcount(a) + Out[10]: 3 + + In [11]: l = [1,2,3,a] + + In [12]: sys.getrefcount(a) + Out[12]: 4 + +.. nextslide:: + +.. code-block:: ipython + + In [13]: del b + + In [14]: sys.getrefcount(a) + Out[14]: 3 + + + In [15]: del l + + In [16]: sys.getrefcount(a) + Out[16]: 2 + + +.. nextslide:: + + +.. code-block:: ipython + + # function local variables + + In [17]: def test(x): + ....: print "x has a refcount of:", sys.getrefcount(x) + ....: + + In [18]: sys.getrefcount(a) + Out[18]: 2 + + In [19]: test(a) + x has a refcount of: 4 + + In [20]: sys.getrefcount(a) + Out[20]: 2 + + +.. nextslide:: + +.. code-block:: ipython + + In [21]: x = 3 + + In [22]: sys.getrefcount(x) + Out[22]: 428 + +WHOA!! + +(hint: interning....) + + +The Power of Reference Counting +-------------------------------- + + +* You don't need to think about it most of the time. + +* Code that creates objects doesn't need to delete them + +* Objects get deleted right away + + . They can "clean up" on deletion (files, for instance) -- and it will happen right away. + +* Performance is predictable + + +The Limits of Reference Counting +-------------------------------- + +* Performance overhead on all operations. But the big one: + +.. rst-class:: medium + + Circular references + +If a python object somehow references itself -- i.e. it references another object that references the first +object: + +You have a circular reference ... + +=================== +Circular References +=================== + +.. rst-class:: left + + .. code-block:: ipython + + In [8]: l1 = [1,] ; l2 = [2,] + + In [9]: l1.append(l2); l2.append(l1) + + In [10]: l1 + Out[10]: [1, [2, [...]]] + + In [11]: l2 + Out[11]: [2, [1, [...]]] + + In [12]: l1[1] + Out[12]: [2, [1, [...]]] + + In [13]: l2[1][1][1] + Out[13]: [1, [2, [...]]] + +(demo) -- :download:`simple_circular.py <../../Examples/ref_counting/simple_circular.py>` + + +The Garbage Collector +---------------------- + +As of Python 2.0 -- a garbage collector was added. + + - (https://docs.python.org/2/library/gc.html) + +It can find and clean up "unreachable" references. + +It is turned on by default:: + + In [1]: import gc + In [2]: gc.isenabled() + Out[2]: True + +or you can force it:: + + In [4]: gc.collect() + Out[4]: 64 + +But it can be slow, and doesn't always work! + +.. nextslide:: + +How does the garbage collector work? + + * Not a full "mark and sweep" type. + +It searches for reference cycles -- then cleans those up. + + * It doesn't have to bother checking non-container types (ints, strings, etc.) + + * Faster, and not as dependent on having a clear "root" namespace. + +Details here: + +http://arctrix.com/nas/python/gc/ (or in the source!) + +Big issue: classes that define a ``__del__`` method are not cleaned up. + + * ``__del__`` methods often act on references that may no be there if + they are cleaned up in the wrong order. + +NOTE: you can work with gc.garbage() -- but tricky and messy + +===== +Tools +===== + +.. rst-class:: left + + If these objects are no longer "reachable" -- how do you find out what's going on? + + We saw ``sys.getrefcount()`` -- but you need a reference to the object to use it. + + You can see what the refcount is before you delete the last reference, but that isn't always easy. + + +Process Memory Use +------------------- + +A really coarse way to find a memory leak is to see if the process memory +is growing. + +It can be subtle --python (and the OS) do tricks to re-use memory, etc. + +But if you have a "real" leak -- you'll see it. (Example to follow) + +:download:`mem_check.py <../../Examples/ref_counting/memcount.py>` + +provides functions that report the memory use of the current running process. + +(\*nix and Windows code) + +id checks +---------- + +As it happens, the Python ``id()`` function returns a memory address. + +It's really dangerous, but that means we can examine an object if we know +its `id`, even if we don't hold a reference to it. + +Bill Bumgarner wrote a nifty extension module that returns the python +object pointed to by an id (memory address) -- "di": + +http://www.friday.com/bbum/2007/08/24/python-di/ + +I added a function that returns the reference count of an object from its id. + +https://github.com/PythonCHB/di_refcount + +NOTE: it would be a really bad idea to use these in production code! + +Examples +---------- + +:download:`simple_circular_di.py <../../Examples/ref_counting/simple_circular_di.py>` + +uses the ref_by_id() function to see what's going on with a circular +reference and garbage collection. + +More real examples in iPython notebook: + +:download:`CircularReferenceExample.ipynb <../../Examples/ref_counting/CircularReferenceExample.ipynb>` + +Or: :download:`circular.py <../../Examples/ref_counting/circular.py>` + +:download:`memcount.py <../../Examples/ref_counting/memcount.py>` is a test +file that show memory growth if circular references are not cleaned up. + +( :download:`mem_check.py <../../Examples/ref_counting//mem_check.py>` ) +is code that reports process memory use. + +You can find this code in the main repo here: + +https://github.com/UWPCE-PythonCert/SystemDevelopment2015/tree/master/Examples/ref_counting + + +Weak References +----------------- + +For times when you don't want to keep objects alive, Python provides +"weak references" -- we saw this in the examples. + +(https://docs.python.org/2/library/weakref.html) + +1. The built-in containers: + + - ``WeakKeyDictionary`` + + - ``WeakValueDictionary`` + + - ``WeakSet`` + +2. ``Proxy`` objects + + - act much like regular references -- client code doesn't know the difference + +3. ``WeakRef`` objects + + - When you want to control what happens when the referenced object is gone. + +========= +Exercise +========= + +.. rst-class:: left + + Build a "weak cache": + + For large objects that are expensive to create: + + * Use a WeakValueDictionay to hold references to (probably large) objects. + + * When the client requests an object that doesn't exist -- one is created, returned, and cached (weakly). + + * If the object is in the cache, it is returned. + + * when no other references exist to the object, it is NOT retained by the cache. + diff --git a/_sources/weak_references.txt b/_sources/weak_references.txt new file mode 100644 index 0000000..49d4520 --- /dev/null +++ b/_sources/weak_references.txt @@ -0,0 +1,378 @@ +.. _weak_references: + +********************************************* +Python Memory Management and Weak References +********************************************* + +Chris Barker + +``PythonCHB@gmail.com`` + +``https://github.com/PythonCHB`` + +================== +Memory Management +================== + +.. rst-class:: left + + * You don't want python objects that are no longer in use taking up memory. + * You don't want to keep track of all that yourself. + * Most "scripting languages" or "virtual machines" have some sort of + automated memory management + +.. rst-class:: center medium + + Many ways to do "Garbage Collection" + + +Reference Counting +-------------------- + +How memory is managed is not part of the Python language spec: + * Jython uses the JVM + * Iron Python uses the CLR + - Both are garbage collected + * PyPy uses Minimark_ + + .. _Minimark: https://pypy.readthedocs.org/en/release-2.4.x/garbage_collection.html#minimark-gc + + +The CPython interpreter uses a reference counting scheme: + * Every time there is a new reference to a Python object, its reference + count is increased + * Every time a reference is removed -- the count is decreased + * When the reference count goes to zero: the object is deleted + (memory freed) + + +What makes a reference? +------------------------ + +* Binding to a name:: + + x = an_object + +* Putting it in a container:: + + l.append(an_object) + +* Passing it to a function:: + + func(an_object) + +Most of the time, you don't need to think about this at all. + + +How do I see what's going on? +------------------------------ + +.. code-block:: python + + + import sys + sys.getrefcount(object) + + +**NOTE:** This will always return one more than you'd expect, as passing the object to the function increases its refcount by one: + +.. code-block:: ipython + + In [5]: a = [] + + In [6]: sys.getrefcount(a) + Out[6]: 2 + +The Heisenberg Uncertainty Principle: + - you can't observe it without altering it + + +Playing with References +------------------------ + +(live demo) + +.. code-block:: ipython + + In [7]: a = [] + + In [8]: sys.getrefcount(a) + Out[8]: 2 + + In [9]: b = a + + In [10]: sys.getrefcount(a) + Out[10]: 3 + + In [11]: l = [1,2,3,a] + + In [12]: sys.getrefcount(a) + Out[12]: 4 + +.. nextslide:: + +.. code-block:: ipython + + In [13]: del b + + In [14]: sys.getrefcount(a) + Out[14]: 3 + + + In [15]: del l + + In [16]: sys.getrefcount(a) + Out[16]: 2 + + +.. nextslide:: + + +.. code-block:: ipython + + # function local variables + + In [17]: def test(x): + ....: print "x has a refcount of:", sys.getrefcount(x) + ....: + + In [18]: sys.getrefcount(a) + Out[18]: 2 + + In [19]: test(a) + x has a refcount of: 4 + + In [20]: sys.getrefcount(a) + Out[20]: 2 + + +.. nextslide:: + +.. code-block:: ipython + + In [21]: x = 3 + + In [22]: sys.getrefcount(x) + Out[22]: 428 + +WHOA!! + +(hint: interning....) + + +The Power of Reference Counting +-------------------------------- + + +* You don't need to think about it most of the time. + +* Code that creates objects doesn't need to delete them + +* Objects get deleted right away + + . They can "clean up" on deletion (files, for instance) -- and it will happen right away. + +* Performance is predictable + + +The Limits of Reference Counting +-------------------------------- + +* Performance overhead on all operations. But the big one: + +.. rst-class:: medium + + Circular references + +If a python object somehow references itself -- i.e. it references another object that references the first +object: + +You have a circular reference ... + +=================== +Circular References +=================== + +.. rst-class:: left + + .. code-block:: ipython + + In [8]: l1 = [1,] ; l2 = [2,] + + In [9]: l1.append(l2); l2.append(l1) + + In [10]: l1 + Out[10]: [1, [2, [...]]] + + In [11]: l2 + Out[11]: [2, [1, [...]]] + + In [12]: l1[1] + Out[12]: [2, [1, [...]]] + + In [13]: l2[1][1][1] + Out[13]: [1, [2, [...]]] + +(demo) -- :download:`simple_circular.py <../../Examples/ref_counting/simple_circular.py>` + + +The Garbage Collector +---------------------- + +As of Python 2.0 -- a garbage collector was added. + + - (https://docs.python.org/2/library/gc.html) + +It can find and clean up "unreachable" references. + +It is turned on by default:: + + In [1]: import gc + In [2]: gc.isenabled() + Out[2]: True + +or you can force it:: + + In [4]: gc.collect() + Out[4]: 64 + +But it can be slow, and doesn't always work! + +.. nextslide:: + +How does the garbage collector work? + + * Not a full "mark and sweep" type. + +It searches for reference cycles -- then cleans those up. + + * It doesn't have to bother checking non-container types (ints, strings, etc.) + + * Faster, and not as dependent on having a clear "root" namespace. + +Details here: + +http://arctrix.com/nas/python/gc/ (or in the source!) + +Big issue: classes that define a ``__del__`` method are not cleaned up. + + * ``__del__`` methods often act on references that may no be there if + they are cleaned up in the wrong order. + +NOTE: you can work with gc.garbage() -- but tricky and messy + +===== +Tools +===== + +.. rst-class:: left + + If these objects are no longer "reachable" -- how do you find out what's going on? + + We saw ``sys.getrefcount()`` -- but you need a reference to the object to use it. + + You can see what the refcount is before you delete the last reference, but that isn't always easy. + + +Process Memory Use +------------------- + +A really coarse way to find a memory leak is to see if the process memory +is growing. + +It can be subtle --python (and the OS) do tricks to re-use memory, etc. + +But if you have a "real" leak -- you'll see it. (Example to follow) + +:download:`mem_check.py <../../Examples/ref_counting/memcount.py>` + +provides functions that report the memory use of the current running process. + +(\*nix and Windows code) + +id checks +---------- + +As it happens, the Python ``id()`` function returns a memory address. + +It's really dangerous, but that means we can examine an object if we know +its `id`, even if we don't hold a reference to it. + +Bill Bumgarner wrote a nifty extension module that returns the python +object pointed to by an id (memory address) -- "di": + +http://www.friday.com/bbum/2007/08/24/python-di/ + +I added a function that returns the reference count of an object from its id. + +https://github.com/PythonCHB/di_refcount + +NOTE: it would be a really bad idea to use these in production code! + +Examples +---------- + +:download:`simple_circular_di.py <../../Examples/ref_counting/simple_circular_di.py>` + +uses the ref_by_id() function to see what's going on with a circular +reference and garbage collection. + +More real examples in iPython notebook: + +:download:`CircularReferenceExample.ipynb <../../Examples/ref_counting/CircularReferenceExample.ipynb>` + +Or: :download:`circular.py <../../Examples/ref_counting/circular.py>` + +:download:`memcount.py <../../Examples/ref_counting/memcount.py>` is a test +file that show memory growth if circular references are not cleaned up. + +( :download:`mem_check.py <../../Examples/ref_counting//mem_check.py>` ) +is code that reports process memory use. + +You can find this code in the main repo here: + +https://github.com/UWPCE-PythonCert/SystemDevelopment2015/tree/master/Examples/ref_counting + + +Weak References +----------------- + +For times when you don't want to keep objects alive, Python provides +"weak references" -- we saw this in the examples. + +(https://docs.python.org/2/library/weakref.html) + +1. The built-in containers: + + - ``WeakKeyDictionary`` + + - ``WeakValueDictionary`` + + - ``WeakSet`` + +2. ``Proxy`` objects + + - act much like regular references -- client code doesn't know the difference + +3. ``WeakRef`` objects + + - When you want to control what happens when the referenced object is gone. + +========= +Exercise +========= + +.. rst-class:: left + + Build a "weak cache": + + For large objects that are expensive to create: + + * Use a WeakValueDictionay to hold references to (probably large) objects. + + * When the client requests an object that doesn't exist -- one is created, returned, and cached (weakly). + + * If the object is in the cache, it is returned. + + * when no other references exist to the object, it is NOT retained by the cache. + diff --git a/_sources/wxpython.rst.txt b/_sources/wxpython.rst.txt new file mode 100644 index 0000000..52edf15 --- /dev/null +++ b/_sources/wxpython.rst.txt @@ -0,0 +1,835 @@ +.. _wxpython: + +################################# +Desktop GUI development: wxPython +################################# + +Introduction +============ + + +Desktop GUIs: +------------- + +Traditional Graphical User Interface Applications + +Run entirely on local machine -- interactive, interface and logic code in one process. + + +Advantages: + +* Easier to write -- all in one program +* Faster -- data/interface direct communication +* Faster display: direct to screen (or even OpenGL, etc.) +* Runs without network +* Save/Manipulate local files +* Familiar install/start/stop/run, etc. + + +Python Options +-------------- + +Multiple GUI frameworks available: + +* PyGTK +* PyQT / PySide +* TkInter +* wxPython +* PyGame +* Native GUIs: Cocoa (PyObjC), PythonWin +* Kivy for touchscreen (mobile) platforms +* Panda3d for 3D or 2D games +* Some more minor ones... + + +wxPython +========= + +.. rst-class:: left + + Why wxPython? + + * Python wrapper around C++ toolkit (wxWidget) + * wxWidgets is a wrapper around **native** toolkit: + + - Windows: Win32 (64) + - OS-X: Cocoa + - Linux: GTK + + * Native look and feel + * License: (modified) LGPL + + Legacy: it was the best option for me when I first needed something... + + See http://www.wxpython.org for more information + + +Installing +---------- + +wxPython is a big complicated build: + + - can't just ``pip`` or ``setup.py install`` from source. + +This is complicated for py3 -- only available in the pre-release "Phoenix" version. + +see: :ref:`installing_wxpython` for details. + +wxPython on py2: +---------------- + +Windows and OS-X: + + - use the binaries on http://wxpython.org/download.php + +Linux: use your system's package manager + +NOTE: there are some issues with some packages: + +* May be old version + +* May use standard wx build -- more crash prone! + +(some run-time checking turned off) + + +Documentation +------------- + +**Docs and Demos:** download these! + +**wxPython Demo:** run this! + +(Examples of every Widget available) + + +**Primary wx docs:** + +* Written for C++, with Python notes: http://wxpython.org/onlinedocs.php + +* This may help: http://wiki.wxpython.org/C%2B%2BGuideForwxPythoneers + +**Phoenix docs:** + +http://wxpython.org/Phoenix/docs/html/main.html + +**The wxPython wiki:** lots of good stuff here: + +* http://wiki.wxpython.org/ + + +Some starting points +-------------------- + +**How to learn wxPython** + +* http://wiki.wxpython.org/How%20to%20Learn%20wxPython + +**wxPython Style Guide** + +* http://wiki.wxpython.org/wxPython%20Style%20Guide + +**Notes on the Demo:** + +* http://www.blog.pythonlibrary.org/2010/10/15/the-wxpython-demo/ + +**wxpython-users:** mailing list -- great resource (and great community): + +* https://groups.google.com/forum/?fromgroups#!forum/wxpython-users + + +**My own repository of samples:** + +* https://github.com/PythonCHB/wxPythonDemos + +Pythonic code: +-------------- + +Over the years, wxPython has grown a number of things to make it more "pythonic" -- hide some of that C++ legacy + + +Properties: + +The C++ classes are full of getters and setters: + +.. code-block:: python + + wxTextCtrl::SetValue + wxTextCtrl::GetValue + +These methods have been translated into properties for Python + +.. code-block:: python + + MyTextCtrl.Value = some_string + another_string = wxTextCtrl.Value + + +(The "Get/Set" versions are still there, but it's klunkier code) + + +.. nextslide:: + +Other Python options: some specific wx types can be accessed with standard python types: + +``wxPoint`` --- ``(x,y)`` ( tuple ) + + +``wxList`` --- ``[1,2,3]`` (python list) + + +``wxSize`` --- ``(w,h)`` (tuple) + +... + +Using these makes your code cleaner and more pythonic + +Basic Structure +=============== + +.. rst-class:: medium:: + + How is a wxPython app structured? + + + +wx.Window +--------- + +Pretty much everything you see on the screen is a ``wx.Window`` + + +It is the superclass for all the "widgets", "controls", or whatever you want to call them + + +It is essentially a rectangle on the screen that catches events + + +You generally don't use it by itself, though you may derive from it to make a new widget + +| +| + +**Historical Note:** "wxWidgets" was called "wxWindows" -- until Microsoft threatened to sue them. + + +.. nextslide:: + +Since everything is a ``wx.Window``, it's good to know its methods and signature: + +.. code-block:: python + + def __init__(parent, + id=wx.ID_ANY, + pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=0, + name=wx.PanelNameStr) + parent (wx.Window) + id (int) + pos (wx.Point) + size (wx.Size) + style (long) + name (string) + + +.. nextslide:: + +Method types: + +* Appearance: Colors, Fonts, Labels, Styles +* Geometry: Size, Position, IsShown, Move, etc. +* Layout: Sizers, etc. +* Many others! + +http://wxpython.org/Phoenix/docs/html/Window.html#window + + +Event-Driven programming +------------------------ + +On app startup, the ``.MainLoop()`` method is called. + +The mainloop takes control -- monitoring for events, then dispatching them. + +Events can come from the system, or user interaction: keyboard, mouse, etc. + +All the work of your app is done in response to events + +You only need to response to (Bind) the events you care about + +Not so different than a web app, except events are finer-grained + +(every mouse move, etc.) + + +wx.App +------- + +Every wx app has a single ``wx.App`` instance: + +.. code-block:: python + + app = wx.App(False) + frame = DemoFrame(None, title="Micro App") + frame.Show() + app.MainLoop() + + +(the ``False`` means: "don't re-direct stdout to a Window") + +And you almost always start the ``MainLoop`` + +wx.Frame +-------- + +``wx.Frame`` is a "top level" Window: One with a title bar, min-max buttons,etc. + +Most apps have a single ``wx.Frame`` -- central interaction with the app. + +This is where menu bars, etc. are placed, and often the core GUI logic of the app. + +.. code-block:: python + + class TestFrame(wx.Frame): + def __init__(self, *args, **kwargs): + kwargs.setdefault('title', "Simple test App") + wx.Frame.__init__(self, *args, **kwargs) + + +demo: ``Examples/wxpython/basic_app_1.py`` + + +Controls +======== + +.. rst-class:: medium + + Or Widgets or ..... + +Menus +----- + +A ``wx.Frame`` has a menu bar you can add items to: + +.. code-block:: python + + # create the menu bar object + menuBar = wx.MenuBar() + + # add a menu to it + fileMenu = wx.Menu() + + # add an item to the menu + openMenuItem = fileMenu.Append(wx.ID_ANY, "&Open", "Open a file" ) + #bind a handler to the menu event + self.Bind(wx.EVT_MENU, self.onOpen, openMenuItem) + + self.SetMenuBar(menuBar) + + +demo: ``Examples/wxpython/basic_app_2.py`` + + +Event Handlers +-------------- + +Event handlers have a common signature: + +.. code-block:: python + + def onOpen(self, evt=None): + print "open menu selected" + self.app_logic.file_open() + + +The second parameter is the ``wx.Event`` object that initiated the call -- it holds information about the event that can be useful. + + +I like to give the event parameter a default None, so the handler can be called from other parts of the code as well. + + +demo: ``Examples/wxpython/basic_app_2.py`` + + +Common Dialogs +-------------- + +wxPython provides a number of common Dialogs. These wrap the native ones where possible for a native look and feel. + + +* ``wx.MessageDialog`` +* ``wx.ColourDialog`` +* ``wx.FileDialog`` +* ``wx.PageSetupDialog`` +* ``wx.FontDialog`` +* ``wx.DirDialog`` +* ``wx.SingleChoiceDialog`` +* ``wx.TextEntryDialog`` +* ... + + + +These do pretty much what you'd expect... + + +wx.FileDialog +------------- + +Example use of a common dialog: ``wx.FileDialog`` + +.. code-block:: python + + dlg = wx.FileDialog(self, + message="Save file as ...", + defaultDir=os.getcwd(), + defaultFile="", + wildcard=wildcard, + style=wx.SAVE ) + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + else: + print "The file dialog was canceled before anything was selected" + dlg.Destroy() + + + +example: ``Examples\wxpython/basic_app_3.py`` + + +Basic Widgets +------------- + +All the basic widgets (controls) you'd expect are there: + + +* Buttons +* TextCtrl (Text Control) +* Check Boxes +* List Box +* Combo Box +* Slider +* Spin Control +* . . . . . + + +Way too many to list here! + + +See the docs and the Demo to find the one you need + + + +Using a Control +--------------- + +A Button is about as simple as it gets + + +``__init__(parent, id, label="", pos=wx.DefaultPosition, ...)`` + + +Mostly the same as wx.Window, and other controls.... + +.. code-block:: python + + ## add just a single button: + self.theButton = wx.Button(self, label="Push Me") + self.theButton.Bind(wx.EVT_BUTTON, self.onButton) + + ## and give it an event handler + def onButton(self, evt=None): + print "You pushed the button!" + + + +code: ``Examples/wxpython/basic_app_4.py`` + + +wx.Panel +-------- + +A ``wx.Panel`` is a ``wx.Window`` that you can put other controls on + +It supplies nifty things like tab traversal, etc. + + +You *can* put controls right on a ``wx.Frame`` (we just did it), but a ``wx.Panel`` provided extra features, the "normal" look, and helps you organize and re-use your code + +Mostly the same as ``wx.Window``, and other controls... + +.. nextslide:: + +.. code-block:: python + + class ButtonPanel(wx.Panel): + def __init__(self, *args, **kwargs): + wx.Panel.__init__(self, *args, **kwargs) + + self.theButton = wx.Button(self, label="Push Me") + self.theButton.Bind(wx.EVT_BUTTON, self.onButton) + def onButton(self, evt=None): + print("You pushed the button!") + +And use it in the Frame: + +.. code-block:: python + + self.buttonPanel = ButtonPanel(self) + + +code: ``Examples/wxpython/basic_app_5.py`` + + +Control Layout +-------------- + +With more than one control, you need to figure out how to place them +and how big to make them. + +You may have noticed that ``wx.Window`` takes ``pos`` and ``size`` parameters. + +You may have also noticed that I didn't use them. + +Why not? + + +Absolute Positioning +-------------------- + +Specifying the size and location of controls with pixel coordinates. + + +This is a serious pain to do! + + +Though it can be made a lot easier with GUI-building tools... + + +So why not? + + +.. nextslide:: + +When you add or remove a control, the layout changes: + + - recalculate all positions and sizes + +When you change the text on a control the layout changes: + + - recalculate all positions and sizes + +When you try it on another platform the layout changes: + + - recalculate all positions and sizes + +When the user changes default font size, the layout changes: + + - recalculate all positions and sizes + + +Sizers: +------- + +The alternative is "Sizers" + +``wx.Sizer`` is wx's system for automatically determining the size and location of controls + +Instead of thinking in terms of what size and position a given control should be, you think in terms of how they relate to each other: + +"I want a column of buttons all the same size along the left edge of the Panel" + +Sizers capture that logic and compute the sizes and locations for you. + +They will re-size things for you when anything changes: +- adding, removing, changing labels +- re-sizing the Window, etc... + +.. nextslide:: + +Sizers take a while to wrap your brain around... + +But it's worth the learning curve. + +Nice discussion here: + +http://wiki.wxpython.org/UsingSizers + +I have the graphic posted on the wall by my desk... + + +Sizer Example +------------- + +The Basic ``BoxSizer``: + +- Lays out a row or column of controls... + +.. code-block:: python + + Sizer.Add(window, proportion, flag, border) + ## do the layout + S = wx.BoxSizer(wx.VERTICAL) + + S.Add(theButton1, 0, wx.GROW | wx.ALL, 4) + S.Add(theButton2, 0, wx.GROW | wx.ALL, 4) + + self.SetSizerAndFit(S) + + +code: ``Examples/wxpython/basic_app_6.py`` + + +Nested Sizers +------------- + +How do I get them centered both ways? + + - Nest a vertical sizer inside a horizonal one + + - And add stretchable spacers... + + +.. code-block:: python + + buttonSizer = wx.BoxSizer(wx.VERTICAL) + + buttonSizer.Add(theButton1, 0, wx.GROW | wx.ALL, 4) + buttonSizer.Add(theButton2, 0, wx.GROW | wx.ALL, 4) + + mainSizer = wx.BoxSizer(wx.HORIZONTAL) + mainSizer.Add((1,1), 1) # stretchable space + mainSizer.Add(buttonSizer, 0, wx.ALIGN_CENTER) # the sizer with the buttons in it + mainSizer.Add((1,1), 1) # stretchable space + + +Widget Inspection Tool +---------------------- + +How do I keep all this straight? + +The Widget Inspection Tool (WIT) is very handy: + +.. code-block:: python + + app = TestApp(False) + ## set up the WIT -- to help debug sizers + import wx.lib.inspection + wx.lib.inspection.InspectionTool().Show() + app.MainLoop() + + +(you can also bring it up from a menu event, or...) + + +code: ``Examples/wxpython/basic_app_7.py`` + + + +Other Sizers +------------ + +Sizers for laying out stuff in grids... + +``wx.GridSizer`` + +``wx.FlexGridSizer`` + +``wx.GridBagSizer`` + +(you can do it all with a ``GridBagSizer``) + +See the docs for info. + + +Hierarchies... +-------------- + +wxPython has multiple independent hierarchies ... + + +**The nested parent-child relationship:** + +* every ``wx.Window`` has a parent +* every ``wx.Window`` has zero or more children + +**The class Hierarchy** + +* sub classes of ``wx.Window`` +* classes with instances as attributes + +**The Layout Hierarchy** + +* Sizers within Sizers... +* Arbitrarily deep. + +Each of these takes care of different concerns: confusing but powerful. + + +Accessing inputs +---------------- + +Much of the point of a GUI is to collect data from the user. + +So you need to be able to access what s/he has input. + +.. code-block:: python + + ## add a text control: + self.textControl = wx.TextCtrl(self) + + def onGetData(self, evt=None): + print("get data button pressed") + contents = self.textControl.Value + print("the contents are:", contents) + +Most controls have a ``.Value`` property + + +Setting Values +-------------- + +You also want to display data... + +So you need to be able to set the values, too: + +.. code-block:: python + + # and another text control: + self.outTextControl = wx.TextCtrl(self, + style=wx.TE_READONLY) + + def onGetData(self, evt=None): + self.outTextControl.Value = self.inTextControl.Value + + +You can set the ``.Value`` property too... + + +example: ``Examples/wxpython/basic_app8.py`` + + +Code-generated GUIs... +---------------------- + +You shouldn't write the same repetitive code for a GUI... + +You may need to build a GUI to match data at run time. + +Lots of ways to do that with wxPython -- Sizers help a lot. + +Try to do it whenever you find yourself writing repetitive code... + +The key is how to do the event Binding: + +.. code-block:: python + + def OnButton(self, evt): + label = evt.GetEventObject().GetLabel() + + do_somethign_with_label(label) + + +example: ``Examples/wxpython/CalculatorDemo.py`` + + +.. nextslide:: + +The "lambda trick" + + +-- a way to pass custom data to an event handler: + +.. code-block:: python + + for name in ["first", "second", "third"]: + btn = wx.Button(self, label=name) + btn.Bind(wx.EVT_BUTTON, + lambda evt, n=name: self.OnButton(evt, n) ) + .... + + def OnButton(self, Event, name): + print("In OnButton:", name) + + +http://wiki.wxpython.org/Passing%20Arguments%20to%20Callbacks + + +Miscellaneous +============= + +.. rst-class:: medium + + Assorted handy features.... + + +Long Running Tasks +------------------ + +The UI is locked up while an event is being handled. + +So you want all event handlers to run fast. + +But what if there is significant work to do? + +Enter: threading and multi-processing + +But: wxPython is not thread-safe: almost all wx methods must be called from within the same thread. + +Thread-safe operations: Creating and Posting Events + +``wx.CallAfter`` +---------------- + +Easiest way to communicate with threads: + +``wx.CallAfter`` + +Puts an event on the event stack, calls the designated function or method when the stack is cleared: + +.. code-block:: python + + wx.CallAfter(function_to_call, *args, **kwargs) + + # *args, **kwargs are passed on to function_to_call + + +(see also: ``wx.CallLater()``) + + +http://wiki.wxpython.org/LongRunningTasks + + +BILS +---- + +Browser Interface, Local Server + +Web app: Server runs on local machine + +Browser is the interface -- but all running local + +Can wrap the Browser window in a desktop app: Chrome Embedded Framework, wxWebkit, etc. + +Good way to get both a web app and desktop app with one codebase + +Example: Cameo Chemicals + +(PyCon 2009: Browser Interface, Local Server Application) + + +LAB +--- + +Make a very simple address book app: + +1. Really basic data model is in ``address_book_data.py`` +2. Finish the form to edit an entry -- subclass of a ``wx.Panel`` + (``entry_form.py``) +3. The form goes on a ``wx.Frame`` (``address_book_app.py``) +4. Add a way to switch between entries (``switcher.py``) +5. Add a "new record" button +6. Add file-save and file-open menus to the frame +7. Add some validation, better layout, etc.... + +``Examples/wxpython/address_book/`` diff --git a/_sources/wxpython.txt b/_sources/wxpython.txt new file mode 100644 index 0000000..52edf15 --- /dev/null +++ b/_sources/wxpython.txt @@ -0,0 +1,835 @@ +.. _wxpython: + +################################# +Desktop GUI development: wxPython +################################# + +Introduction +============ + + +Desktop GUIs: +------------- + +Traditional Graphical User Interface Applications + +Run entirely on local machine -- interactive, interface and logic code in one process. + + +Advantages: + +* Easier to write -- all in one program +* Faster -- data/interface direct communication +* Faster display: direct to screen (or even OpenGL, etc.) +* Runs without network +* Save/Manipulate local files +* Familiar install/start/stop/run, etc. + + +Python Options +-------------- + +Multiple GUI frameworks available: + +* PyGTK +* PyQT / PySide +* TkInter +* wxPython +* PyGame +* Native GUIs: Cocoa (PyObjC), PythonWin +* Kivy for touchscreen (mobile) platforms +* Panda3d for 3D or 2D games +* Some more minor ones... + + +wxPython +========= + +.. rst-class:: left + + Why wxPython? + + * Python wrapper around C++ toolkit (wxWidget) + * wxWidgets is a wrapper around **native** toolkit: + + - Windows: Win32 (64) + - OS-X: Cocoa + - Linux: GTK + + * Native look and feel + * License: (modified) LGPL + + Legacy: it was the best option for me when I first needed something... + + See http://www.wxpython.org for more information + + +Installing +---------- + +wxPython is a big complicated build: + + - can't just ``pip`` or ``setup.py install`` from source. + +This is complicated for py3 -- only available in the pre-release "Phoenix" version. + +see: :ref:`installing_wxpython` for details. + +wxPython on py2: +---------------- + +Windows and OS-X: + + - use the binaries on http://wxpython.org/download.php + +Linux: use your system's package manager + +NOTE: there are some issues with some packages: + +* May be old version + +* May use standard wx build -- more crash prone! + +(some run-time checking turned off) + + +Documentation +------------- + +**Docs and Demos:** download these! + +**wxPython Demo:** run this! + +(Examples of every Widget available) + + +**Primary wx docs:** + +* Written for C++, with Python notes: http://wxpython.org/onlinedocs.php + +* This may help: http://wiki.wxpython.org/C%2B%2BGuideForwxPythoneers + +**Phoenix docs:** + +http://wxpython.org/Phoenix/docs/html/main.html + +**The wxPython wiki:** lots of good stuff here: + +* http://wiki.wxpython.org/ + + +Some starting points +-------------------- + +**How to learn wxPython** + +* http://wiki.wxpython.org/How%20to%20Learn%20wxPython + +**wxPython Style Guide** + +* http://wiki.wxpython.org/wxPython%20Style%20Guide + +**Notes on the Demo:** + +* http://www.blog.pythonlibrary.org/2010/10/15/the-wxpython-demo/ + +**wxpython-users:** mailing list -- great resource (and great community): + +* https://groups.google.com/forum/?fromgroups#!forum/wxpython-users + + +**My own repository of samples:** + +* https://github.com/PythonCHB/wxPythonDemos + +Pythonic code: +-------------- + +Over the years, wxPython has grown a number of things to make it more "pythonic" -- hide some of that C++ legacy + + +Properties: + +The C++ classes are full of getters and setters: + +.. code-block:: python + + wxTextCtrl::SetValue + wxTextCtrl::GetValue + +These methods have been translated into properties for Python + +.. code-block:: python + + MyTextCtrl.Value = some_string + another_string = wxTextCtrl.Value + + +(The "Get/Set" versions are still there, but it's klunkier code) + + +.. nextslide:: + +Other Python options: some specific wx types can be accessed with standard python types: + +``wxPoint`` --- ``(x,y)`` ( tuple ) + + +``wxList`` --- ``[1,2,3]`` (python list) + + +``wxSize`` --- ``(w,h)`` (tuple) + +... + +Using these makes your code cleaner and more pythonic + +Basic Structure +=============== + +.. rst-class:: medium:: + + How is a wxPython app structured? + + + +wx.Window +--------- + +Pretty much everything you see on the screen is a ``wx.Window`` + + +It is the superclass for all the "widgets", "controls", or whatever you want to call them + + +It is essentially a rectangle on the screen that catches events + + +You generally don't use it by itself, though you may derive from it to make a new widget + +| +| + +**Historical Note:** "wxWidgets" was called "wxWindows" -- until Microsoft threatened to sue them. + + +.. nextslide:: + +Since everything is a ``wx.Window``, it's good to know its methods and signature: + +.. code-block:: python + + def __init__(parent, + id=wx.ID_ANY, + pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=0, + name=wx.PanelNameStr) + parent (wx.Window) + id (int) + pos (wx.Point) + size (wx.Size) + style (long) + name (string) + + +.. nextslide:: + +Method types: + +* Appearance: Colors, Fonts, Labels, Styles +* Geometry: Size, Position, IsShown, Move, etc. +* Layout: Sizers, etc. +* Many others! + +http://wxpython.org/Phoenix/docs/html/Window.html#window + + +Event-Driven programming +------------------------ + +On app startup, the ``.MainLoop()`` method is called. + +The mainloop takes control -- monitoring for events, then dispatching them. + +Events can come from the system, or user interaction: keyboard, mouse, etc. + +All the work of your app is done in response to events + +You only need to response to (Bind) the events you care about + +Not so different than a web app, except events are finer-grained + +(every mouse move, etc.) + + +wx.App +------- + +Every wx app has a single ``wx.App`` instance: + +.. code-block:: python + + app = wx.App(False) + frame = DemoFrame(None, title="Micro App") + frame.Show() + app.MainLoop() + + +(the ``False`` means: "don't re-direct stdout to a Window") + +And you almost always start the ``MainLoop`` + +wx.Frame +-------- + +``wx.Frame`` is a "top level" Window: One with a title bar, min-max buttons,etc. + +Most apps have a single ``wx.Frame`` -- central interaction with the app. + +This is where menu bars, etc. are placed, and often the core GUI logic of the app. + +.. code-block:: python + + class TestFrame(wx.Frame): + def __init__(self, *args, **kwargs): + kwargs.setdefault('title', "Simple test App") + wx.Frame.__init__(self, *args, **kwargs) + + +demo: ``Examples/wxpython/basic_app_1.py`` + + +Controls +======== + +.. rst-class:: medium + + Or Widgets or ..... + +Menus +----- + +A ``wx.Frame`` has a menu bar you can add items to: + +.. code-block:: python + + # create the menu bar object + menuBar = wx.MenuBar() + + # add a menu to it + fileMenu = wx.Menu() + + # add an item to the menu + openMenuItem = fileMenu.Append(wx.ID_ANY, "&Open", "Open a file" ) + #bind a handler to the menu event + self.Bind(wx.EVT_MENU, self.onOpen, openMenuItem) + + self.SetMenuBar(menuBar) + + +demo: ``Examples/wxpython/basic_app_2.py`` + + +Event Handlers +-------------- + +Event handlers have a common signature: + +.. code-block:: python + + def onOpen(self, evt=None): + print "open menu selected" + self.app_logic.file_open() + + +The second parameter is the ``wx.Event`` object that initiated the call -- it holds information about the event that can be useful. + + +I like to give the event parameter a default None, so the handler can be called from other parts of the code as well. + + +demo: ``Examples/wxpython/basic_app_2.py`` + + +Common Dialogs +-------------- + +wxPython provides a number of common Dialogs. These wrap the native ones where possible for a native look and feel. + + +* ``wx.MessageDialog`` +* ``wx.ColourDialog`` +* ``wx.FileDialog`` +* ``wx.PageSetupDialog`` +* ``wx.FontDialog`` +* ``wx.DirDialog`` +* ``wx.SingleChoiceDialog`` +* ``wx.TextEntryDialog`` +* ... + + + +These do pretty much what you'd expect... + + +wx.FileDialog +------------- + +Example use of a common dialog: ``wx.FileDialog`` + +.. code-block:: python + + dlg = wx.FileDialog(self, + message="Save file as ...", + defaultDir=os.getcwd(), + defaultFile="", + wildcard=wildcard, + style=wx.SAVE ) + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + else: + print "The file dialog was canceled before anything was selected" + dlg.Destroy() + + + +example: ``Examples\wxpython/basic_app_3.py`` + + +Basic Widgets +------------- + +All the basic widgets (controls) you'd expect are there: + + +* Buttons +* TextCtrl (Text Control) +* Check Boxes +* List Box +* Combo Box +* Slider +* Spin Control +* . . . . . + + +Way too many to list here! + + +See the docs and the Demo to find the one you need + + + +Using a Control +--------------- + +A Button is about as simple as it gets + + +``__init__(parent, id, label="", pos=wx.DefaultPosition, ...)`` + + +Mostly the same as wx.Window, and other controls.... + +.. code-block:: python + + ## add just a single button: + self.theButton = wx.Button(self, label="Push Me") + self.theButton.Bind(wx.EVT_BUTTON, self.onButton) + + ## and give it an event handler + def onButton(self, evt=None): + print "You pushed the button!" + + + +code: ``Examples/wxpython/basic_app_4.py`` + + +wx.Panel +-------- + +A ``wx.Panel`` is a ``wx.Window`` that you can put other controls on + +It supplies nifty things like tab traversal, etc. + + +You *can* put controls right on a ``wx.Frame`` (we just did it), but a ``wx.Panel`` provided extra features, the "normal" look, and helps you organize and re-use your code + +Mostly the same as ``wx.Window``, and other controls... + +.. nextslide:: + +.. code-block:: python + + class ButtonPanel(wx.Panel): + def __init__(self, *args, **kwargs): + wx.Panel.__init__(self, *args, **kwargs) + + self.theButton = wx.Button(self, label="Push Me") + self.theButton.Bind(wx.EVT_BUTTON, self.onButton) + def onButton(self, evt=None): + print("You pushed the button!") + +And use it in the Frame: + +.. code-block:: python + + self.buttonPanel = ButtonPanel(self) + + +code: ``Examples/wxpython/basic_app_5.py`` + + +Control Layout +-------------- + +With more than one control, you need to figure out how to place them +and how big to make them. + +You may have noticed that ``wx.Window`` takes ``pos`` and ``size`` parameters. + +You may have also noticed that I didn't use them. + +Why not? + + +Absolute Positioning +-------------------- + +Specifying the size and location of controls with pixel coordinates. + + +This is a serious pain to do! + + +Though it can be made a lot easier with GUI-building tools... + + +So why not? + + +.. nextslide:: + +When you add or remove a control, the layout changes: + + - recalculate all positions and sizes + +When you change the text on a control the layout changes: + + - recalculate all positions and sizes + +When you try it on another platform the layout changes: + + - recalculate all positions and sizes + +When the user changes default font size, the layout changes: + + - recalculate all positions and sizes + + +Sizers: +------- + +The alternative is "Sizers" + +``wx.Sizer`` is wx's system for automatically determining the size and location of controls + +Instead of thinking in terms of what size and position a given control should be, you think in terms of how they relate to each other: + +"I want a column of buttons all the same size along the left edge of the Panel" + +Sizers capture that logic and compute the sizes and locations for you. + +They will re-size things for you when anything changes: +- adding, removing, changing labels +- re-sizing the Window, etc... + +.. nextslide:: + +Sizers take a while to wrap your brain around... + +But it's worth the learning curve. + +Nice discussion here: + +http://wiki.wxpython.org/UsingSizers + +I have the graphic posted on the wall by my desk... + + +Sizer Example +------------- + +The Basic ``BoxSizer``: + +- Lays out a row or column of controls... + +.. code-block:: python + + Sizer.Add(window, proportion, flag, border) + ## do the layout + S = wx.BoxSizer(wx.VERTICAL) + + S.Add(theButton1, 0, wx.GROW | wx.ALL, 4) + S.Add(theButton2, 0, wx.GROW | wx.ALL, 4) + + self.SetSizerAndFit(S) + + +code: ``Examples/wxpython/basic_app_6.py`` + + +Nested Sizers +------------- + +How do I get them centered both ways? + + - Nest a vertical sizer inside a horizonal one + + - And add stretchable spacers... + + +.. code-block:: python + + buttonSizer = wx.BoxSizer(wx.VERTICAL) + + buttonSizer.Add(theButton1, 0, wx.GROW | wx.ALL, 4) + buttonSizer.Add(theButton2, 0, wx.GROW | wx.ALL, 4) + + mainSizer = wx.BoxSizer(wx.HORIZONTAL) + mainSizer.Add((1,1), 1) # stretchable space + mainSizer.Add(buttonSizer, 0, wx.ALIGN_CENTER) # the sizer with the buttons in it + mainSizer.Add((1,1), 1) # stretchable space + + +Widget Inspection Tool +---------------------- + +How do I keep all this straight? + +The Widget Inspection Tool (WIT) is very handy: + +.. code-block:: python + + app = TestApp(False) + ## set up the WIT -- to help debug sizers + import wx.lib.inspection + wx.lib.inspection.InspectionTool().Show() + app.MainLoop() + + +(you can also bring it up from a menu event, or...) + + +code: ``Examples/wxpython/basic_app_7.py`` + + + +Other Sizers +------------ + +Sizers for laying out stuff in grids... + +``wx.GridSizer`` + +``wx.FlexGridSizer`` + +``wx.GridBagSizer`` + +(you can do it all with a ``GridBagSizer``) + +See the docs for info. + + +Hierarchies... +-------------- + +wxPython has multiple independent hierarchies ... + + +**The nested parent-child relationship:** + +* every ``wx.Window`` has a parent +* every ``wx.Window`` has zero or more children + +**The class Hierarchy** + +* sub classes of ``wx.Window`` +* classes with instances as attributes + +**The Layout Hierarchy** + +* Sizers within Sizers... +* Arbitrarily deep. + +Each of these takes care of different concerns: confusing but powerful. + + +Accessing inputs +---------------- + +Much of the point of a GUI is to collect data from the user. + +So you need to be able to access what s/he has input. + +.. code-block:: python + + ## add a text control: + self.textControl = wx.TextCtrl(self) + + def onGetData(self, evt=None): + print("get data button pressed") + contents = self.textControl.Value + print("the contents are:", contents) + +Most controls have a ``.Value`` property + + +Setting Values +-------------- + +You also want to display data... + +So you need to be able to set the values, too: + +.. code-block:: python + + # and another text control: + self.outTextControl = wx.TextCtrl(self, + style=wx.TE_READONLY) + + def onGetData(self, evt=None): + self.outTextControl.Value = self.inTextControl.Value + + +You can set the ``.Value`` property too... + + +example: ``Examples/wxpython/basic_app8.py`` + + +Code-generated GUIs... +---------------------- + +You shouldn't write the same repetitive code for a GUI... + +You may need to build a GUI to match data at run time. + +Lots of ways to do that with wxPython -- Sizers help a lot. + +Try to do it whenever you find yourself writing repetitive code... + +The key is how to do the event Binding: + +.. code-block:: python + + def OnButton(self, evt): + label = evt.GetEventObject().GetLabel() + + do_somethign_with_label(label) + + +example: ``Examples/wxpython/CalculatorDemo.py`` + + +.. nextslide:: + +The "lambda trick" + + +-- a way to pass custom data to an event handler: + +.. code-block:: python + + for name in ["first", "second", "third"]: + btn = wx.Button(self, label=name) + btn.Bind(wx.EVT_BUTTON, + lambda evt, n=name: self.OnButton(evt, n) ) + .... + + def OnButton(self, Event, name): + print("In OnButton:", name) + + +http://wiki.wxpython.org/Passing%20Arguments%20to%20Callbacks + + +Miscellaneous +============= + +.. rst-class:: medium + + Assorted handy features.... + + +Long Running Tasks +------------------ + +The UI is locked up while an event is being handled. + +So you want all event handlers to run fast. + +But what if there is significant work to do? + +Enter: threading and multi-processing + +But: wxPython is not thread-safe: almost all wx methods must be called from within the same thread. + +Thread-safe operations: Creating and Posting Events + +``wx.CallAfter`` +---------------- + +Easiest way to communicate with threads: + +``wx.CallAfter`` + +Puts an event on the event stack, calls the designated function or method when the stack is cleared: + +.. code-block:: python + + wx.CallAfter(function_to_call, *args, **kwargs) + + # *args, **kwargs are passed on to function_to_call + + +(see also: ``wx.CallLater()``) + + +http://wiki.wxpython.org/LongRunningTasks + + +BILS +---- + +Browser Interface, Local Server + +Web app: Server runs on local machine + +Browser is the interface -- but all running local + +Can wrap the Browser window in a desktop app: Chrome Embedded Framework, wxWebkit, etc. + +Good way to get both a web app and desktop app with one codebase + +Example: Cameo Chemicals + +(PyCon 2009: Browser Interface, Local Server Application) + + +LAB +--- + +Make a very simple address book app: + +1. Really basic data model is in ``address_book_data.py`` +2. Finish the form to edit an entry -- subclass of a ``wx.Panel`` + (``entry_form.py``) +3. The form goes on a ``wx.Frame`` (``address_book_app.py``) +4. Add a way to switch between entries (``switcher.py``) +5. Add a "new record" button +6. Add file-save and file-open menus to the frame +7. Add some validation, better layout, etc.... + +``Examples/wxpython/address_book/`` diff --git a/_static/Diamond_inheritance.png b/_static/Diamond_inheritance.png new file mode 100644 index 0000000..dac9434 Binary files /dev/null and b/_static/Diamond_inheritance.png differ diff --git a/_static/Greenwich_clock.jpg b/_static/Greenwich_clock.jpg new file mode 100644 index 0000000..d1fd94a Binary files /dev/null and b/_static/Greenwich_clock.jpg differ diff --git a/_static/ajax-loader.gif b/_static/ajax-loader.gif new file mode 100644 index 0000000..61faf8c Binary files /dev/null and b/_static/ajax-loader.gif differ diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000..7ed0e58 --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,632 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 170px; +} + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +dl { + margin-bottom: 15px; +} + +dd p { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, .highlighted { + background-color: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +div.code-block-caption { + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +div.code-block-caption + div > div.highlight > pre { + margin-top: 0; +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + padding: 1em 1em 0; +} + +div.literal-block-wrapper div.highlight { + margin: 0; +} + +code.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +code.descclassname { + background-color: transparent; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: relative; + left: 0px; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/color_git_prompt.png b/_static/color_git_prompt.png new file mode 100644 index 0000000..b85ab44 Binary files /dev/null and b/_static/color_git_prompt.png differ diff --git a/_static/comment-bright.png b/_static/comment-bright.png new file mode 100644 index 0000000..15e27ed Binary files /dev/null and b/_static/comment-bright.png differ diff --git a/_static/comment-close.png b/_static/comment-close.png new file mode 100644 index 0000000..4d91bcf Binary files /dev/null and b/_static/comment-close.png differ diff --git a/_static/comment.png b/_static/comment.png new file mode 100644 index 0000000..dfbc0cb Binary files /dev/null and b/_static/comment.png differ diff --git a/_static/css/badge_only.css b/_static/css/badge_only.css new file mode 100644 index 0000000..6362912 --- /dev/null +++ b/_static/css/badge_only.css @@ -0,0 +1,2 @@ +.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} +/*# sourceMappingURL=badge_only.css.map */ diff --git a/_static/css/theme.css b/_static/css/theme.css new file mode 100644 index 0000000..c1631d8 --- /dev/null +++ b/_static/css/theme.css @@ -0,0 +1,5 @@ +*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,.rst-content code,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,.rst-content .toctree-wrapper p.caption,h3{orphans:3;widows:3}h2,.rst-content .toctree-wrapper p.caption,h3{page-break-after:avoid}}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.6.3");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2?v=4.6.3") format("woff2"),url("../fonts/fontawesome-webfont.woff?v=4.6.3") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.6.3") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.wy-menu-vertical li span.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-left.toctree-expand,.wy-menu-vertical li.current>a span.fa-pull-left.toctree-expand,.rst-content .fa-pull-left.admonition-title,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content dl dt .fa-pull-left.headerlink,.rst-content p.caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.rst-content code.download span.fa-pull-left:first-child,.fa-pull-left.icon{margin-right:.3em}.fa.fa-pull-right,.wy-menu-vertical li span.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-right.toctree-expand,.wy-menu-vertical li.current>a span.fa-pull-right.toctree-expand,.rst-content .fa-pull-right.admonition-title,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content dl dt .fa-pull-right.headerlink,.rst-content p.caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.rst-content code.download span.fa-pull-right:first-child,.fa-pull-right.icon{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.wy-menu-vertical li span.pull-left.toctree-expand,.wy-menu-vertical li.on a span.pull-left.toctree-expand,.wy-menu-vertical li.current>a span.pull-left.toctree-expand,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.rst-content p.caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.rst-content code.download span.pull-left:first-child,.pull-left.icon{margin-right:.3em}.fa.pull-right,.wy-menu-vertical li span.pull-right.toctree-expand,.wy-menu-vertical li.on a span.pull-right.toctree-expand,.wy-menu-vertical li.current>a span.pull-right.toctree-expand,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.rst-content p.caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.rst-content code.download span.pull-right:first-child,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li span.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .rst-content p.caption .headerlink,.rst-content p.caption a .headerlink,a .rst-content tt.download span:first-child,.rst-content tt.download a span:first-child,a .rst-content code.download span:first-child,.rst-content code.download a span:first-child,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .btn span.toctree-expand,.btn .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .btn span.toctree-expand,.btn .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .btn span.toctree-expand,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .rst-content p.caption .headerlink,.rst-content p.caption .btn .headerlink,.btn .rst-content tt.download span:first-child,.rst-content tt.download .btn span:first-child,.btn .rst-content code.download span:first-child,.rst-content code.download .btn span:first-child,.btn .icon,.nav .fa,.nav .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .nav span.toctree-expand,.nav .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .nav span.toctree-expand,.nav .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .nav span.toctree-expand,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .rst-content p.caption .headerlink,.rst-content p.caption .nav .headerlink,.nav .rst-content tt.download span:first-child,.rst-content tt.download .nav span:first-child,.nav .rst-content code.download span:first-child,.rst-content code.download .nav span:first-child,.nav .icon{display:inline}.btn .fa.fa-large,.btn .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .btn span.fa-large.toctree-expand,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .btn .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .btn span.fa-large:first-child,.btn .rst-content code.download span.fa-large:first-child,.rst-content code.download .btn span.fa-large:first-child,.btn .fa-large.icon,.nav .fa.fa-large,.nav .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .nav span.fa-large.toctree-expand,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .nav .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.nav .rst-content code.download span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .btn span.fa-spin.toctree-expand,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .btn .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .btn span.fa-spin:first-child,.btn .rst-content code.download span.fa-spin:first-child,.rst-content code.download .btn span.fa-spin:first-child,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .nav span.fa-spin.toctree-expand,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .nav .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.nav .rst-content code.download span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.wy-menu-vertical li span.btn.toctree-expand:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.rst-content p.caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.rst-content code.download span.btn:first-child:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.wy-menu-vertical li span.btn.toctree-expand:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content p.caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.rst-content code.download span.btn:first-child:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li .btn-mini span.toctree-expand:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .rst-content p.caption .headerlink:before,.rst-content p.caption .btn-mini .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.rst-content tt.download .btn-mini span:first-child:before,.btn-mini .rst-content code.download span:first-child:before,.rst-content code.download .btn-mini span:first-child:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a{color:#2980B9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27AE60}.wy-tray-container li.wy-tray-item-info{background:#2980B9}.wy-tray-container li.wy-tray-item-warning{background:#E67E22}.wy-tray-container li.wy-tray-item-danger{background:#E74C3C}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27AE60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980B9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27AE60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#E74C3C !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#E67E22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980B9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9B59B6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980B9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980B9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 .3125em 0;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#E74C3C}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{float:left;display:block;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:.34375em .625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129FEA}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#E74C3C;border:1px solid #E74C3C}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#E74C3C}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#E74C3C}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type="radio"][disabled],input[type="checkbox"][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{width:36px;height:12px;margin:12px 0;position:relative;border-radius:4px;background:#ccc;cursor:pointer;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.wy-switch:before{position:absolute;content:"";display:block;width:18px;height:18px;border-radius:4px;background:#999;left:-3px;top:-3px;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.wy-switch:after{content:"false";position:absolute;left:48px;display:block;font-size:12px;color:#ccc}.wy-switch.active{background:#1e8449}.wy-switch.active:before{left:24px;background:#27AE60}.wy-switch.active:after{content:"true"}.wy-switch.disabled,.wy-switch.active.disabled{cursor:not-allowed}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#E74C3C}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #E74C3C}.wy-control-group.wy-control-group-error textarea{border:solid 1px #E74C3C}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27AE60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#E74C3C}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#E67E22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980B9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980B9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9B59B6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#E67E22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980B9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27AE60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#E74C3C !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,.rst-content .toctree-wrapper p.caption,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2,.rst-content .toctree-wrapper p.caption{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt,.rst-content code{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;color:#E74C3C;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content ol.arabic li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9B59B6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#EAF2F5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs li code,.wy-breadcrumbs li .rst-content tt,.rst-content .wy-breadcrumbs li tt{padding:5px;border:none;background:none}.wy-breadcrumbs li code.literal,.wy-breadcrumbs li .rst-content tt.literal,.rst-content .wy-breadcrumbs li tt.literal{color:#404040}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;margin-bottom:0;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#555;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li code,.wy-menu-vertical li .rst-content tt,.rst-content .wy-menu-vertical li tt{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li span.toctree-expand{display:block;float:left;margin-left:-1.2em;font-size:0.8em;line-height:1.6em;color:#4d4d4d}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.on a:hover span.toctree-expand,.wy-menu-vertical li.current>a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand{display:block;font-size:0.8em;line-height:1.6em;color:#333}.wy-menu-vertical li.toctree-l1.current li.toctree-l2>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>ul{display:none}.wy-menu-vertical li.toctree-l1.current li.toctree-l2.current>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>ul{display:block}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{display:block;background:#c9c9c9;padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l2 span.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3{font-size:0.9em}.wy-menu-vertical li.toctree-l3.current>a{background:#bdbdbd;padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{display:block;background:#bdbdbd;padding:.4045em 5.663em;border-top:none;border-bottom:none}.wy-menu-vertical li.toctree-l3 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l3 span.toctree-expand{color:#969696}.wy-menu-vertical li.toctree-l4{font-size:0.9em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover span.toctree-expand{color:#b3b3b3}.wy-menu-vertical a:active{background-color:#2980B9;cursor:pointer;color:#fff}.wy-menu-vertical a:active span.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980B9;text-align:center;padding:.809em;display:block;color:#fcfcfc;margin-bottom:.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em auto;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-side-nav-search>a img.logo,.wy-side-nav-search .wy-dropdown>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search>a.icon img.logo,.wy-side-nav-search .wy-dropdown>a.icon img.logo{margin-top:0.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:normal;color:rgba(255,255,255,0.3)}.wy-nav .wy-menu-vertical header{color:#2980B9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980B9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980B9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}footer span.commit code,footer span.commit .rst-content tt,.rst-content footer span.commit tt{padding:0px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:1em;background:none;border:none;color:#999}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:before,.rst-breadcrumbs-buttons:after{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-side-scroll{width:auto}.wy-side-nav-search{width:auto}.wy-menu.wy-menu-vertical{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version span.toctree-expand,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content p.caption .headerlink,.rst-content p.caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content img{max-width:100%;height:auto !important}.rst-content .highlight>pre{line-height:normal}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure p.caption{font-style:italic}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img,.rst-content .section>a>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content .toctree-wrapper p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content .toctree-wrapper p.caption .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after,.rst-content p.caption .headerlink:after{visibility:visible;content:"";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content .toctree-wrapper p.caption:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink,.rst-content p.caption:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#F1C40F;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.docutils.citation tt,.rst-content table.docutils.citation code,.rst-content table.docutils.footnote tt,.rst-content table.docutils.footnote code{color:#555}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt,.rst-content tt,.rst-content code{color:#000;padding:2px 5px}.rst-content tt big,.rst-content tt em,.rst-content tt big,.rst-content code big,.rst-content tt em,.rst-content code em{font-size:100% !important;line-height:normal}.rst-content tt.literal,.rst-content tt.literal,.rst-content code.literal{color:#E74C3C}.rst-content tt.xref,a .rst-content tt,.rst-content tt.xref,.rst-content code.xref,a .rst-content tt,a .rst-content code{font-weight:bold;color:#404040}.rst-content a tt,.rst-content a tt,.rst-content a code{color:#2980B9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980B9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:#555}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) code{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) code.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27AE60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}.rst-content tt.download,.rst-content code.download{background:inherit;padding:inherit;font-weight:normal;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content tt.download span:first-child,.rst-content code.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{margin-right:4px}.rst-content .guilabel{border:1px solid #7fbbe3;background:#e7f2fa;font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center}@font-face{font-family:"Inconsolata";font-style:normal;font-weight:400;src:local("Inconsolata"),local("Inconsolata-Regular"),url(../fonts/Inconsolata-Regular.ttf) format("truetype")}@font-face{font-family:"Inconsolata";font-style:normal;font-weight:700;src:local("Inconsolata Bold"),local("Inconsolata-Bold"),url(../fonts/Inconsolata-Bold.ttf) format("truetype")}@font-face{font-family:"Lato";font-style:normal;font-weight:400;src:local("Lato Regular"),local("Lato-Regular"),url(../fonts/Lato-Regular.ttf) format("truetype")}@font-face{font-family:"Lato";font-style:normal;font-weight:700;src:local("Lato Bold"),local("Lato-Bold"),url(../fonts/Lato-Bold.ttf) format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:400;src:local("Roboto Slab Regular"),local("RobotoSlab-Regular"),url(../fonts/RobotoSlab-Regular.ttf) format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:700;src:local("Roboto Slab Bold"),local("RobotoSlab-Bold"),url(../fonts/RobotoSlab-Bold.ttf) format("truetype")} +/*# sourceMappingURL=theme.css.map */ diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000..8163495 --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,287 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + */ +jQuery.urldecode = function(x) { + return decodeURIComponent(x).replace(/\+/g, ' '); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s == 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node) { + if (node.nodeType == 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { + var span = document.createElement("span"); + span.className = className; + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this); + }); + } + } + return this.each(function() { + highlight(this); + }); +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated == 'undefined') + return string; + return (typeof translated == 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated == 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) == 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this == '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + $(document).keyup(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box or textarea + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { + switch (event.keyCode) { + case 37: // left + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + case 39: // right + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); \ No newline at end of file diff --git a/_static/down-pressed.png b/_static/down-pressed.png new file mode 100644 index 0000000..5756c8c Binary files /dev/null and b/_static/down-pressed.png differ diff --git a/_static/down.png b/_static/down.png new file mode 100644 index 0000000..1b3bdad Binary files /dev/null and b/_static/down.png differ diff --git a/_static/dst_fb_graph.png b/_static/dst_fb_graph.png new file mode 100644 index 0000000..92898de Binary files /dev/null and b/_static/dst_fb_graph.png differ diff --git a/_static/dst_sf_graph.png b/_static/dst_sf_graph.png new file mode 100644 index 0000000..73723a7 Binary files /dev/null and b/_static/dst_sf_graph.png differ diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/_static/file.png differ diff --git a/_static/flake8_output.png b/_static/flake8_output.png new file mode 100644 index 0000000..dc44e48 Binary files /dev/null and b/_static/flake8_output.png differ diff --git a/_static/fonts/Inconsolata-Bold.ttf b/_static/fonts/Inconsolata-Bold.ttf new file mode 100644 index 0000000..809c1f5 Binary files /dev/null and b/_static/fonts/Inconsolata-Bold.ttf differ diff --git a/_static/fonts/Inconsolata-Regular.ttf b/_static/fonts/Inconsolata-Regular.ttf new file mode 100644 index 0000000..fc981ce Binary files /dev/null and b/_static/fonts/Inconsolata-Regular.ttf differ diff --git a/_static/fonts/Lato-Bold.ttf b/_static/fonts/Lato-Bold.ttf new file mode 100644 index 0000000..1d23c70 Binary files /dev/null and b/_static/fonts/Lato-Bold.ttf differ diff --git a/_static/fonts/Lato-Regular.ttf b/_static/fonts/Lato-Regular.ttf new file mode 100644 index 0000000..0f3d0f8 Binary files /dev/null and b/_static/fonts/Lato-Regular.ttf differ diff --git a/_static/fonts/RobotoSlab-Bold.ttf b/_static/fonts/RobotoSlab-Bold.ttf new file mode 100644 index 0000000..df5d1df Binary files /dev/null and b/_static/fonts/RobotoSlab-Bold.ttf differ diff --git a/_static/fonts/RobotoSlab-Regular.ttf b/_static/fonts/RobotoSlab-Regular.ttf new file mode 100644 index 0000000..eb52a79 Binary files /dev/null and b/_static/fonts/RobotoSlab-Regular.ttf differ diff --git a/_static/fonts/fontawesome-webfont.eot b/_static/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..c7b00d2 Binary files /dev/null and b/_static/fonts/fontawesome-webfont.eot differ diff --git a/_static/fonts/fontawesome-webfont.svg b/_static/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..8b66187 --- /dev/null +++ b/_static/fonts/fontawesome-webfont.svg @@ -0,0 +1,685 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/_static/fonts/fontawesome-webfont.ttf b/_static/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..f221e50 Binary files /dev/null and b/_static/fonts/fontawesome-webfont.ttf differ diff --git a/_static/fonts/fontawesome-webfont.woff b/_static/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..6e7483c Binary files /dev/null and b/_static/fonts/fontawesome-webfont.woff differ diff --git a/_static/git_another_commit_on_branch.png b/_static/git_another_commit_on_branch.png new file mode 100644 index 0000000..7ef6a06 Binary files /dev/null and b/_static/git_another_commit_on_branch.png differ diff --git a/_static/git_checkout_branch.png b/_static/git_checkout_branch.png new file mode 100644 index 0000000..dab12bd Binary files /dev/null and b/_static/git_checkout_branch.png differ diff --git a/_static/git_checkout_master.png b/_static/git_checkout_master.png new file mode 100644 index 0000000..dc245a7 Binary files /dev/null and b/_static/git_checkout_master.png differ diff --git a/_static/git_commit_on_branch.png b/_static/git_commit_on_branch.png new file mode 100644 index 0000000..23143d7 Binary files /dev/null and b/_static/git_commit_on_branch.png differ diff --git a/_static/git_head.png b/_static/git_head.png new file mode 100644 index 0000000..c48c40e Binary files /dev/null and b/_static/git_head.png differ diff --git a/_static/git_master_branch.png b/_static/git_master_branch.png new file mode 100644 index 0000000..9c4aeb8 Binary files /dev/null and b/_static/git_master_branch.png differ diff --git a/_static/git_merge_commit.png b/_static/git_merge_commit.png new file mode 100644 index 0000000..2df3d2d Binary files /dev/null and b/_static/git_merge_commit.png differ diff --git a/_static/git_new_branch.png b/_static/git_new_branch.png new file mode 100644 index 0000000..a0a4ef4 Binary files /dev/null and b/_static/git_new_branch.png differ diff --git a/_static/git_new_commit.png b/_static/git_new_commit.png new file mode 100644 index 0000000..012eb84 Binary files /dev/null and b/_static/git_new_commit.png differ diff --git a/_static/git_new_commit_on_master.png b/_static/git_new_commit_on_master.png new file mode 100644 index 0000000..6c25e51 Binary files /dev/null and b/_static/git_new_commit_on_master.png differ diff --git a/_static/git_simple_timeline.png b/_static/git_simple_timeline.png new file mode 100644 index 0000000..465f546 Binary files /dev/null and b/_static/git_simple_timeline.png differ diff --git a/_static/jquery-1.11.1.js b/_static/jquery-1.11.1.js new file mode 100644 index 0000000..d4b67f7 --- /dev/null +++ b/_static/jquery-1.11.1.js @@ -0,0 +1,10308 @@ +/*! + * jQuery JavaScript Library v1.11.1 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-05-01T17:42Z + */ + +(function( global, factory ) { + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper window is present, + // execute the factory and get jQuery + // For environments that do not inherently posses a window with a document + // (such as Node.js), expose a jQuery-making factory as module.exports + // This accentuates the need for the creation of a real window + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +// + +var deletedIds = []; + +var slice = deletedIds.slice; + +var concat = deletedIds.concat; + +var push = deletedIds.push; + +var indexOf = deletedIds.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + +var + version = "1.11.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android<4.1, IE<9 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/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(); + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return 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 just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // 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 ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + slice: function() { + return this.pushStack( 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] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: deletedIds.sort, + splice: deletedIds.splice +}; + +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; + + // skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // 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 ( i === length ) { + 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 + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + // 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 ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + 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 && + !hasOwn.call(obj, "constructor") && + !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 ( support.ownLast ) { + for ( key in obj ) { + return 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 || hasOwn.call( obj, key ); + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; + }, + + // 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; + }, + + // Support: Android<4.1, IE<9 + trim: 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 { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( indexOf ) { + return 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 len = +second.length, + j = 0, + i = first.length; + + while ( j < len ) { + first[ i++ ] = second[ j++ ]; + } + + // Support: IE<9 + // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) + if ( len !== len ) { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // 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 new values + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return 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 = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( 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; + }, + + now: function() { + return +( new Date() ); + }, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); + +// 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 ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v1.10.19 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-04-18 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // 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(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + 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#" ), + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // 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 + "*" ), + + 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" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + 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<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + 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 (jQuery #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 ) && testContext( 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 ); +} + +/** + * 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 + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 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]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== strundefined && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +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; +}; + +/** + * 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 hasCompare, + doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + // 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 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", function() { + setDocument(); + }, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", function() { + setDocument(); + }); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* 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 = rnative.test( doc.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 = rnative.test( 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, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowclip^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // 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: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + 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 = rnative.test( (matches = docElem.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 + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + 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 + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // 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 || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + 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 ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +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 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + 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 + while ( (node = elem[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 (jQuery #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[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[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // 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 negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + 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; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // 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; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// 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 ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( 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 oldCache, outerCache, + newCache = [ 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 ( (oldCache = outerCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + outerCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + 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 multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +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 ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context !== document && context; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (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; + } + } + + // 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, match /* 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 ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is no seed and 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; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + 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 ) && testContext( 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 if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome<14 +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// 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( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +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; + + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// 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 ( risSimple.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; + }); +} + +jQuery.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; + })); +}; + +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; + }, + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + 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; + } +}); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + // 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-]*))$/, + + init = jQuery.fn.init = function( selector, context ) { + 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 + // Intentionally let the error be thrown if parseHTML is not present + 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 typeof rootjQuery.ready !== "undefined" ? + rootjQuery.ready( selector ) : + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.extend({ + 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; + } +}); + +jQuery.fn.extend({ + 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; + } + } + }); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + 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)) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); + }, + + // 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 ) { + return this.pushStack( + jQuery.unique( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + 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 ); + }; +}); +var rnotwhite = (/\S+/g); + + + +// 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( 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 ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + 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 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[ tuple[ 0 ] + "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 = 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 ? 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(); + } +}); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend({ + // 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.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +}); + +/** + * Clean-up method for dom ready events + */ +function detach() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } +} + +/** + * The ready event handler and self cleanup method + */ +function completed() { + // 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(); + } +} + +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 ); +}; + + +var strundefined = typeof undefined; + + + +// Support: IE<9 +// Iteration over object's inherited properties before its own +var i; +for ( i in jQuery( support ) ) { + break; +} +support.ownLast = i !== "0"; + +// Note: most support tests are defined in their respective modules. +// false until the test is run +support.inlineBlockNeedsLayout = false; + +// Execute ASAP in case we need to set body.style.zoom +jQuery(function() { + // Minified: var a,b,c,d + var val, div, body, container; + + body = document.getElementsByTagName( "body" )[ 0 ]; + if ( !body || !body.style ) { + // Return for frameset docs that don't have a body + return; + } + + // Setup + div = document.createElement( "div" ); + container = document.createElement( "div" ); + container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; + body.appendChild( container ).appendChild( div ); + + if ( typeof div.style.zoom !== 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.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1"; + + support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; + if ( val ) { + // 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 ); +}); + + + + +(function() { + var div = document.createElement( "div" ); + + // Execute the test only if not already executed in another module. + if (support.deleteExpando == null) { + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + } + + // Null elements to avoid leaks in IE. + div = null; +})(); + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( elem ) { + var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ], + nodeType = +elem.nodeType || 1; + + // Do not set data on non-element DOM nodes because it will not be cleared (#8335). + return nodeType !== 1 && nodeType !== 9 ? + false : + + // Nodes accept data unless otherwise specified; rejection can be conditional + !noData || noData !== true && elem.getAttribute("classid") === noData; +}; + + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +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; +} + +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 ] = 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 ( 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 (space-suffixed to avoid Object.prototype collisions) + // throw uncatchable exceptions if you attempt to set expando properties + noData: { + "applet ": true, + "embed ": true, + // ...but Flash objects (which have this classid) *can* 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 ); + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var i, name, data, + elem = this[0], + attrs = elem && elem.attributes; + + // 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" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ 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 ) ) : undefined; + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + + +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 ); + }); + }, + 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 pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( 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 ); + }; + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.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; +}; +var rcheckableType = (/^(?:checkbox|radio)$/i); + + + +(function() { + // Minified: var a,b,c + var input = document.createElement( "input" ), + div = document.createElement( "div" ), + fragment = document.createDocumentFragment(); + + // Setup + div.innerHTML = "
a"; + + // 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; + + // 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>"; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + input.type = "checkbox"; + input.checked = true; + fragment.appendChild( input ); + support.appendChecked = input.checked; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE6-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // #11217 - WebKit loses check when the name is after the checked attribute + fragment.appendChild( div ); + div.innerHTML = ""; + + // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 + // old WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.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() + support.noCloneEvent = true; + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Execute the test only if not already executed in another module. + if (support.deleteExpando == null) { + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + } +})(); + + +(function() { + var i, eventName, + div = document.createElement( "div" ); + + // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event) + for ( i in { submit: true, change: true, focusin: true }) { + eventName = "on" + i; + + if ( !(support[ i + "Bubbles" ] = eventName in window) ) { + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + div.setAttribute( eventName, "t" ); + support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false; + } + } + + // Null elements to avoid leaks in IE. + div = null; +})(); + + +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|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 !== 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( 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( 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 = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = 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 && handle.apply && jQuery.acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === 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 = 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 ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + 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 ] === 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.defaultPrevented === undefined && + // Support: IE < 9, Android < 4.0 + src.returnValue === false ? + 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() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, 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 ( !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 ( !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 ( !support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + jQuery._removeData( doc, fix ); + } else { + jQuery._data( doc, fix, attaches ); + } + } + }; + }); +} + +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 ); + } + } +}); + + +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: 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; + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== 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 ( rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? 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 ( !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 ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && 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.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( 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 ( (!support.noCloneEvent || !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 ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !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 ( !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 = 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 !== strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + deletedIds.push( id ); + } + } + } + } + } +}); + +jQuery.fn.extend({ + text: function( value ) { + return 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 ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + 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 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 ) && + ( support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( 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 arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = 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" && + !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 ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, 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 ) { + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + 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; + } +}); + +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() + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + + +var iframe, + elemdisplay = {}; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? + + // Use of this method is a temporary fix (more like optmization) until something better comes along, + // since it was removed from specification and supported only in FF + style.display : jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function 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( " + +
<iframe src="http://player.vimeo.com/video/1063136?title=0&amp;byline=0&amp;portrait=0" width="400" height="225" frameborder="0"></iframe>
+ + +
+

Digging Deeper

+

If you want to learn about making your own themes, extending deck.js, and more, check out the documentation.

+
+ + +
+ + +
+ + +

+ + / + +

+ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/html_slides/coverage/jquery.min.js b/html_slides/coverage/jquery.min.js new file mode 100644 index 0000000..76d21a4 --- /dev/null +++ b/html_slides/coverage/jquery.min.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-1.10.2.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.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%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t +}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},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(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x(" + +
<iframe src="http://player.vimeo.com/video/1063136?title=0&amp;byline=0&amp;portrait=0" width="400" height="225" frameborder="0"></iframe>
+ + +
+

Digging Deeper

+

If you want to learn about making your own themes, extending deck.js, and more, check out the documentation.

+
+ + +
+ + +
+ + +

+ + / + +

+ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/html_slides/deckjs/jquery.min.js b/html_slides/deckjs/jquery.min.js new file mode 100644 index 0000000..76d21a4 --- /dev/null +++ b/html_slides/deckjs/jquery.min.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-1.10.2.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.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%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t +}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},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(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x(" + +
<iframe src="http://player.vimeo.com/video/1063136?title=0&amp;byline=0&amp;portrait=0" width="400" height="225" frameborder="0"></iframe>
+ + +
+

Digging Deeper

+

If you want to learn about making your own themes, extending deck.js, and more, check out the documentation.

+
+ + +
+ + +
+ + +

+ + / + +

+ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/html_slides/pep8/jquery.min.js b/html_slides/pep8/jquery.min.js new file mode 100644 index 0000000..76d21a4 --- /dev/null +++ b/html_slides/pep8/jquery.min.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-1.10.2.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.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%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t +}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},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(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("