├── .gitignore ├── Procfile ├── README.md ├── config.py ├── posts ├── code.md ├── docs.md ├── docs │ ├── alternatives.md │ ├── performance.md │ ├── retries.md │ └── security.md └── profiles │ ├── how-active-campaign-uses-rest-hooks.md │ ├── how-emma-uses-rest-hooks.md │ ├── how-formstack-uses-rest-hooks.md │ ├── how-podio-uses-rest-hooks.md │ ├── how-shopify-uses-rest-hooks.md │ ├── how-wufoo-uses-rest-hooks.md │ └── how-zapier-uses-rest-hooks.md ├── requirements.txt ├── resthooks.py ├── runtime.txt ├── static ├── css │ ├── common.css │ ├── font.css │ ├── pygments.css │ └── reset.css ├── favicon.ico ├── img │ ├── activecampaign.png │ ├── asana.png │ ├── emma.png │ ├── formstack.png │ ├── glass.png │ ├── hook_diagram.png │ ├── logo.xcf │ ├── pattern.png │ ├── pattern.xcf │ ├── podio.png │ ├── polling.png │ ├── rest.png │ ├── shopify.png │ ├── stripe.png │ ├── subscription_handshake_delayed_diagram.png │ ├── subscription_handshake_diagram.png │ ├── webhooks.png │ ├── wufoo.png │ └── zapier.png └── js │ ├── jquery.js │ ├── jquery.sharrre-1.3.4.min.js │ └── underscore.js └── templates ├── 404.html ├── index.html └── post.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn resthooks:app -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | _____ ______ _____ _______ _ _ ____ ____ _ __ _____ ____ _____ _____ 3 | | __ \ | ____| / ____||__ __|| | | | / __ \ / __ \ | |/ / / ____| / __ \ | __ \ / ____| 4 | | |__) || |__ | (___ | | | |__| || | | || | | || ' / | (___ | | | || |__) || | __ 5 | | _ / | __| \___ \ | | | __ || | | || | | || < \___ \ | | | || _ / | | |_ | 6 | | | \ \ | |____ ____) | | | | | | || |__| || |__| || . \ ____) |_| |__| || | \ \ | |__| | 7 | |_| \_\|______||_____/ |_| |_| |_| \____/ \____/ |_|\_\|_____/(_)\____/ |_| \_\ \_____| 8 | 9 | ``` 10 | 11 | REST Hooks are a lightweight subscription layer on top of your existing REST API. 12 | 13 | The real-time web is already here, but REST APIs haven't kept up. Many major players have already standardized upon subscription webhooks. REST Hooks are a way to consolidate that momentum and push it to a broarder audience. 14 | 15 | For more information, code examples, libraries, and company profiles check out [http://resthooks.org](http://resthooks.org). 16 | 17 | [http://resthooks.org](http://resthooks.org) an initiative by [Zapier](https://zapier.com) 2013-2017. 18 | 19 | ## How to run locally. 20 | 21 | Clone the repo and hop into the correct directory. 22 | 23 | ``` 24 | git clone git@github.com:zapier/resthooks.git && cd resthooks 25 | ``` 26 | 27 | Next, you'll want to make a virtual environment (we recommend using 28 | virtualenvwrapper but you could skip this) and then install dependencies: 29 | 30 | ``` 31 | mkvirtualenv resthooks -p python2.7 32 | pip install -r requirements.txt 33 | ``` 34 | 35 | Now you can run the server! 36 | 37 | ``` 38 | foreman run web 39 | # or 40 | python resthooks.py 41 | ``` 42 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | DEBUG = True 2 | FLATPAGES_ROOT = "posts" 3 | FLATPAGES_EXTENSION = ".md" 4 | STATIC_URL = "/static" 5 | POSTS_PER_PAGE = 6 -------------------------------------------------------------------------------- /posts/code.md: -------------------------------------------------------------------------------- 1 | title: Code 2 | author: Zapier 3 | date: 2013-08-27 4 | 5 | 6 | ### [Home](/) > {{ title }} 7 | 8 | ## Python/Django 9 | 10 | [django-rest-hooks](https://github.com/zapier/django-rest-hooks) is a complete solution that requires only a handful of configuration tasks: 11 | 12 | 1. Add `'rest_hooks'` to installed apps in settings.py. 13 | 2. Define your `HOOK_EVENTS` in settings.py. 14 | 3. Start sending hooks! 15 | 16 | It also comes with a [live CRM demo application](http://demo.resthooks.org/) ([code available on GitHub](https://github.com/zapier/resthookdemo)). You can [read more about django-rest-hooks here](https://github.com/zapier/django-rest-hooks). 17 | 18 | 19 | ## Ruby/Rails 20 | 21 | This [Rails library](https://github.com/net-engine/resthooks) offers a similarly simple sample application for Ruby on Rails that you can use when designing your application. 22 | 23 | 24 | ## Node.js/Sails 25 | 26 | [This application](https://github.com/zapier/node-resthooksdemo) 27 | provides a demonstration of using RESTHooks in conjunction with a 28 | [Sails](http://sailsjs.org/#!) 29 | based CRM application that leverages Waterline ORM's Lifecycle Callbacks 30 | to notify subscriptions. You can find the relevant documentation to get 31 | started 32 | [here](https://github.com/zapier/node-resthooksdemo/blob/master/README.md). 33 | 34 | -------------------------------------------------------------------------------- /posts/docs.md: -------------------------------------------------------------------------------- 1 | title: Documentation 2 | author: Zapier 3 | date: 2013-08-27 4 | 5 | ### [Home](/) > Docs 6 | 7 | ## What are REST Hooks? What are they not? 8 | 9 | REST Hooks itself is **not** a specification, it is a **collection of patterns** that treat webhooks like subscriptions. These subscriptions are manipulated via a REST API just like any other resource. That's it. Really. 10 | 11 | In this documentation you'll find information on **creating the minimum implementation** as well as diving deeper into specific features that cover quite a few topics and their **best practices** within the pattern of REST Hooks. You can pick and choose from these topics and implement what makes sense for your API. 12 | 13 | ## Minimum Implementation Walkthrough 14 | 15 | To comply with the REST Hooks pattern, there is a minimum set of pieces you'll need to add to have a working implementation. This article will outline those minimums: 16 | 17 | 1. Mechanism to store subscriptions 18 | 2. Mechanism to modify subscriptions via API 19 | 3. List and implement event types 20 | 4. Mechanism to send hooks 21 | 22 | Retries, intent and identity verification, batching and other components are optional and vary wildly between implementations. 23 | 24 | 25 | ### Mechanism to store subscriptions 26 | 27 | Highly dependent on your database selection, this first requirement is the foundation by which the subscriptions are managed. At the most basic level, a persisted subscription only really needs the following fields: 28 | 29 | 1. An event name or names the subscription includes 30 | 2. A parent user or account relationship 31 | 3. A target URL to send the payloads 32 | 4. (optional) Active vs. inactive state ([read more about security](/docs/security/)) 33 | 34 | It is wise to make sure you index both the event and user relationship so subscription lookups are performant. 35 | 36 | 37 | ### Mechanism to modify subscriptions via API 38 | 39 | Also dependent on your API implementation, this allows anyone with normal API access to manipulate their subscriptions like any other resource on your API. For example, if you already have a REST API, the most common and logical solution is another resource: 40 | 41 | Method | Route | About 42 | --------|-------------------------------|---------------------- 43 | GET | /api/v1/subscription/ | list subscriptions 44 | POST | /api/v1/subscription/ | create a subscription 45 | GET | /api/v1/subscription/:id/ | get a subscription 46 | PUT | /api/v1/subscription/:id/ | update a subscription 47 | DELETE | /api/v1/subscription/:id/ | delete a subscription 48 | 49 | These would simply manipulate the resources like any other REST endpoint, but with the added benefit that subscriptions have one side effect: their existence will cause webhooks be sent to the target URL when an event happens for that account. 50 | 51 | This is an improvement over manually managed webhooks URLs as it gives interested third party applications the opportunity to dynamically create and update integrations that perform in near-real time functions. 52 | 53 | 54 | ### List and implement event types. 55 | 56 | Next up would be enumerating and implementing each event type you'd like your REST Hooks subscription system to support. Each event type needs two things: 57 | 58 | 1. A name (use the noun.verb dot syntax, IE: contact.create or lead.delete). 59 | 2. A payload template (simply mirror the representation from your standard API). 60 | 61 | For starters, we recommend create, update and delete events on your most popular resources. For example, if you had contact and deal resources, you'd define and implement contact.create, contact.update, contact.delete, deal.create, etc… events. 62 | 63 | The payload that you build for each record in your REST API would match exactly your API's representation of the same object. That makes it easy to map REST resources to hook resources and vice versa. 64 | 65 | 66 | ### Mechanism to send hooks 67 | 68 | Now that you have all the other pieces in place, the only thing left to do is add the actual mechanism to POST or otherwise deliver the payload for each event to the proper target URL for each matching subscription. 69 | 70 | 1. Compiling and POSTing the combined payload for the triggering resource and hook resource. 71 | 2. Handling responses like 410 Gone and optionally retrying connection or other 4xx/5xx errors. 72 | 73 | We generally recommend some sort of delayed task implementation (we detail more [performance considerations here](/docs/performance/)) but in its simplest form inline hook delivery will get the job done. 74 | 75 | ## Other Topics 76 | 77 | 1. [Security Best Practices](/docs/security/) - Patterns to verify intent and identity for hooks 78 | 2. [Performance Best Practices](/docs/performance/) - Patterns for ensure your code is performant 79 | 3. [Delivery Failure & Retries](/docs/retries/) - Patterns to ensure delivery is complete 80 | 4. [Alternatives to REST Hooks](/docs/alternatives/) - Other real-time patterns in use around the web 81 | 82 | 83 | -------------------------------------------------------------------------------- /posts/docs/alternatives.md: -------------------------------------------------------------------------------- 1 | title: Alternatives 2 | author: Zapier 3 | date: 2013-08-27 4 | 5 | 6 | ### [Home](/) > [Docs](/docs/) > {{ title }} 7 | 8 | When it comes to real-time communication, the collection of patterns surrounding REST Hooks do not stand alone. There are other options, many of them are quite mature and are in production across a handful of vendors. 9 | 10 | 11 | ## Long-polling or Comet 12 | 13 | Long-polling is a bit of a clever hack on normal polling. It is more efficient than repeatedly asking "any new data? any new data?" every couple minutes with a brand new GET request because it just leaves the first GET request open until there is new data. After receiving that new data and closing the original connection, the client initiates another connection for the next bit of information. 14 | 15 | Long polling between servers generally requires a daemon that monitors and manages long-lived connections and processes. Languages that are inherently asynchronous (like Javascript or Go) make this easier, but it is still quite a bit of state to manage on both client and server. Unfortunately, long polling isn't *much* of an improvement over regular polling. However, long polling is used in production in a few places on the web and most existing infrastructures have quite a bit of common ground. 16 | 17 | Comet is a loose collection of patterns around long polling and other browser workarounds (for example, hidden iframes long polling for changes, passing them to the parent). Now that browsers mostly support websockets, Comet is becoming less and less common. 18 | 19 | The use of long polling and Comet is discouraged, existing implementations at Facebook, Twitter and elsewhere are already deprecated and pending removal. 20 | 21 | 22 | ## Websockets 23 | 24 | Websockets are extremely flexible and create a persistent connection ready for full two way communication. They are fairly lightweight and have mature libraries in most languages. 25 | 26 | However, like long polling, they require another piece of infrastructure that monitors and manages these long-lived connections and processes. Languages that aren't inherently asynchronous will need specialized libraries to deal with threads and extra processes. It's also not very common today and doesn't share a lot of common ground with existing infrastructure. 27 | 28 | Websockets also require a minimum level of resources at scale. Websocket connections that are hibernating or otherwise silent still need to be maintained which require at least a small portion of resources. 29 | 30 | Websockets and similar constructs are wonderful for APIs where real-time is business critical (perhaps for real-time stock updates). For the average CRUD-style web application, webhooks are a more natural way to approximate less chatty communications. 31 | 32 | 33 | ## Classic webhooks 34 | 35 | Classic, or user-managed, webhooks get closer to the ideal event notification system. The server POSTs a payload to some user-defined URL. There is almost zero overhead on the client side; they just handle the POST requests like normal. 36 | 37 | Webhooks reuse tons of existing architecture: all notifications are HTTP-based and most web services have quite a bit of expertise making and handling such requests. However, they have one big drawback: they are a very poor experience for the user. Copy-pasting obscure URLs between two services' settings pages is a major hurdle. 38 | 39 | 40 | ## XMPP 41 | 42 | The Extensible Messaging and Presence Protocol (XMPP) is one popular protocol that while originally developed and used as part of Jabber for chat-based applications, it is also used for bidirectional communication between servers. The binding method, [Bidirectional-streams Over Synchronous HTTP (BOSH)](http://xmpp.org/extensions/xep-0124.html), allows for messages to be pushed to subscribers as soon as they become available by emulating persistent, long-lived connections while under the hood they still use HTTP polling but in an optimized manner. 43 | 44 | Messages sent between XMPP aware nodes are in xml although they may additionally include different data formats embedded within them (such is the case with the `` node). A major strength that users gain by using XMPP is that they can rely on its long history and well defined open standard when building XMPP aware applications. This also means several good libraries exist for it and consumer applications that may not have a public address or are behind a firewall can still communicate with other servers. 45 | 46 | On the downside, users might be a little overwhelmed with the complexities involved with implementing the specification and may find the protocol, while lightweight, simply not well suited to their needs. Also, unlike REST Hooks where HTTP traffic only occurs when notifications are sent, XMPP has ongoing traffic to maintain the connection (albeit lightweight). 47 | 48 | 49 | ## New protocol or specification 50 | 51 | There have been proposals for extending the HTTP specification itself with SUBSCRIBE and UNSUBSCRIBE methods (via WebDAV). Partially due to the fact that it wasn't adopted into the core specifications, they have not caught on and only a handful of services implement it. Unfortunately, the red tape and bureaucracy involved to officially include subscription HTTP methods are prohibitive. 52 | 53 | There have also been a handful of HTTP Subscription specifications that live within the existing HTTP specification itself. For example, using an X-Callback header to define callbacks during a POST to an /api/subscription endpoint. This closely approximates the REST Hooks ethos, with the minor variation in that the request body should define the subscription resource (just like the rest of your REST API). 54 | 55 | -------------------------------------------------------------------------------- /posts/docs/performance.md: -------------------------------------------------------------------------------- 1 | title: Performance 2 | author: Zapier 3 | date: 2013-08-27 4 | 5 | 6 | ### [Home](/) > [Docs](/docs/) > {{ title }} 7 | 8 | Properly implemented and adopted, REST Hooks can result in 66x load reduction on API servers (based on comparing polling vs. webhook implementations within Zapier). 9 | 10 | The architecture required to run REST Hooks, however, can break away from a standard setup for a simple CRUD app, which can have only HTTP and database servers. This article will cover basic patterns to work within this architecture and ways to extend architecture to better suit hook delivery. 11 | 12 | You have three options (all outlined below in more details) 13 | 14 | 1. Easy but Inefficient (Using Inline HTTP Requests) 15 | 2. Cheap Hack (Using a SQL-based Queue) 16 | 3. Elegant and Scalable (Using a Proper Queue) 17 | 18 | The primary consideration is the hook delivery mechanism. This encapsulates both the query for existing subscriptions for an event and user as well as the actual HTTP call to deliver the hook payload itself (as well as handle any logic surrounding failures, retries or security). 19 | 20 | 21 | ### Easy but Inefficient (Using Inline HTTP Requests) 22 | 23 | A simplistic implementation might just do the following any time an event might be triggered: 24 | 25 | 1. An event is triggered by a user 26 | 2. Look up any existing subscriptions for the particular event and user 27 | 3. Loop over existing subscriptions and POST the payload (very slow) 28 | 4. Perform any cleanup, failure, or retry logic 29 | 5. Return a response to the user 30 | 31 | If you are using a synchronous programming language like PHP, Python, Ruby, etc. these actions can block. If your user is saving a form to edit the underlying record triggering the event and has a handful of subscriptions, this can delay the response by several seconds or more! 32 | 33 | However, if you are using a language with asynchronous capabilities like Javascript, Go, etc. these actions can be pushed off onto the event loop or into a coroutine, which can be a great way to get non-blocking performance. 34 | 35 | The same can be done (with care) for other languages using threads (Zapier's django-rest-hooks implementation does this by default in Python). But, you should probably look into using a message queue under these circumstances. 36 | 37 | 38 | ### Cheap Hack (Using a SQL-based Queue) or Elegant and Scalable (Using a Proper Queue) 39 | 40 | Depending on your existing architecture, adding a delayed task execution for delivering hooks could be as simple as defining a new task for whatever queueing system you already have in place. On the other hand, it may require additional infrastructure (like adding Redis, ActiveMQ, RabbitMQ, or Gearman). If adding another piece of tech isn't an option, you could always implement a queue via a database table and some cron jobs (this doesn't scale very well, but does work!). 41 | 42 | Most languages have libraries to handle task queues making this very easy (besides, task queues themselves are good practices). 43 | 44 | The basic idea is: 45 | 46 | 1. An event is triggered by a user 47 | 2. Insert a task to deliver hooks for the event and user (very fast) 48 | 3. Return a response to the user 49 | 4. (Inside the task) Look up any existing subscriptions for the particular event and user 50 | 5. (Inside the task) Loop over existing subscriptions and POST the payload (very slow) 51 | 6. (Inside the task) Perform any cleanup, failure, or retry logic 52 | 53 | Because step 4 and 5 are moved into a task that is running in the background, it no longer blocks our response to the user. This maintains a high level of responsiveness for the user, increases likeliness of task success (especially when isolating and fanning out new tasks for each event and subscription) and parallelizes tasks, delivering them faster. 54 | 55 | 56 | ## Batching 57 | 58 | Sending a large number of hooks at the same time for very similar or duplicate events is a behavior that may be unwanted. For example, if a user makes a dozen fast edits to a single record, a naive implementation would send a dozen "item updated" event hooks. This example also illustrates the importance of delivery order: if the most recent event isn't the last one received, the client's state will be incorrect. 59 | 60 | Another example is when a user does a mass edit or import task that touches hundreds, thousands or even *millions* of records. A naive implementation would simply attempt to do an equal number of hooks. 61 | 62 | There are a few ways to handle this: 63 | 64 | 1. Debouncing or deduplicating update events: only send the most recent event hook instead of several events of very similar type. 65 | 2. Batching massive jobs: instead of sending thousands of individual hooks, bundle them up into a single hook. 66 | 67 | Both options can add quite a bit of overhead due to the nature of event aggregation and delayed releasing. For example, #1 would have to hold all update events for some time period (maybe 15 seconds or so) to give the user a chance to edit and issue another update event to delay it once again. Once the delay expires, the hook is delivered for the most recent event. 68 | 69 | 70 | ## Skinny Payloads 71 | 72 | Another option (especially within the context of massive batching or special systems like file syncing) would be sending more lightweight payloads. The most extreme would be the unique ID for the changed resource, on the other end of the spectrum, you'd send an entire snapshot of the newly changed resource. 73 | 74 | A skinny payload has two general benefits for performance: 75 | 76 | 1. Less outbound bandwidth consumed for delivering payloads 77 | 2. Less internal resources consumed to process payloads 78 | -------------------------------------------------------------------------------- /posts/docs/retries.md: -------------------------------------------------------------------------------- 1 | title: Delivery Failure & Retries 2 | author: Zapier 3 | date: 2013-08-27 4 | 5 | 6 | ### [Home](/) > [Docs](/docs/) > {{ title }} 7 | 8 | REST Hooks alleviates consumers from having to continuously poll for changes by having REST Hook providers push new data to them when it becomes available, but what happens when those consumers themselves are unavailable? 9 | 10 | Even the most well designed infrastructure will sooner or later experience an outage of some form and without any retry mechanism in place, consumers will miss updates they are interested in. So when providing a REST Hooks base interface we implement retries to ensure consumers get the updates they are subscribing to. 11 | 12 | 13 | ### When Do We Retry? 14 | 15 | There are several instances when a Sender should retry to ensure 16 | delivery. A response status code of 2xx indicating that the action 17 | requested by the Sender was received, understood, accepted and processed 18 | successfully should be treated as a message delivery acknowledgment. No 19 | need to redeliver sent messages in those cases. 20 | 21 | Responses in the 3xx range can be a little tricky. The Sender may 22 | opt to dynamically update hook subscriptions based on what it may 23 | receive along with these status codes. However, the 24 | the Sender may decide to either retry or fail when these occur and insist that the 25 | hook subscription be updated through the API. 26 | 27 | When a Receiver sends a response status code in the 4xx range the Sender 28 | should usually attempt to retry. Services can sometimes have temporary 29 | Not Found (404) or failed authentication errors (401), so status codes 30 | like these shouldn't immediately mark the subscription as bad until it 31 | can be proven that the Receiver's endpoint has a consistent 404 over 32 | time. An exception to this could be a 410 (Gone) status code which indicates 33 | that the Receiver should immediately cancel the subscription as the 34 | resource is no longer available. Again, it is all up to the Sender to 35 | decide how this is handled. 36 | 37 | Since response status codes in the 5xx range always indicate an internal 38 | failure of some sort, a retry should definitely be scheduled when they 39 | occur. Likewise, a network issue such as a connection reset or connection 40 | timeout fall into unforeseen circumstances and should be retried, as well. 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
ResponseRetry?
2xx
3xx
4xx
410
5xx
Timed Out
72 | 73 | ### Exponential Back Off 74 | 75 | We've covered when a Sender should retry, but how to we keep from 76 | retrying indefinitely? If the Sender has hooks being sent frequently to 77 | a Receiver and it goes dark, the retries will start building up and 78 | expotentially increase the throughput to the Receiver. Over time the 79 | number of requests being sent will be increase to the point that the 80 | Receiver might get so overwhelmed when it comes back up that 81 | it might go down again. Ahhhh!!! 82 | 83 | One recommended solution is to implement an exponential back off policy. 84 | For example, hooks sent to a Receiver that begin getting failed response 85 | status codes should trigger retries five seconds later, then thirty 86 | seconds, five minutes, an hour and so on. The Sender keeps increasing 87 | the interval since the last failed retry until hooks haven't been 88 | successful for a certain period of time such as a day. Determining the 89 | right interval to retry largely depends on the the service level 90 | agreement between the Sender and Receiver. After a certain period of 91 | time or maximum number of retries the subscription may be marked as inactive or removed. 92 | 93 | This leaves a lot ambiguity. How do we let the Receiver know when the 94 | subscription is cancelled? How do we implement the retry mechanism? Or 95 | if hooks represent state changes for a resource on the Sender's end how 96 | can the Receiver make sure that messages received out of order don't 97 | lead to an invalid state? 98 | 99 | 100 | ### Ensuring Ordered Delivery 101 | 102 | In some situations, the hooks sent to a consumer may be dependent on 103 | some ordering. For example, what if a Sender sends hooks for several 104 | events on the following object: 105 | 106 | 1. Create object 107 | 2. Update object 108 | 3. Update object again 109 | 4. Delete object 110 | 111 | If the Receiver is down and misses the first create message but comes up 112 | in time to catch the delete and then receives the retried hook for the 113 | create event it may consider the resource as having just been created. 114 | 115 | One solution is to have each message include a sequence ID of some sort. 116 | This would mean that it is up to the Receiver to implement a 117 | [Resequencer](http://www.enterpriseintegrationpatterns.com/Resequencer.html) 118 | to put hooks received out of order back in the correct order. 119 | 120 | ### Variation: Claim Check 121 | 122 | An alternative to the exponential back off policy is to implement a 123 | [Claim Check pattern](http://eaipatterns.com/StoreInLibrary.html) for 124 | messages that were not received successfully by the Receiver. In this 125 | scenario, failed hooks are stored in a special holding area until 126 | claimed by the Receiver. Receivers claim the hooks by fetching from a 127 | URL specified by the Sender. 128 | 129 | One straightforward way to implement Claim Checks is to provide the URL 130 | up front that Receivers can check for a log of all failed hook 131 | notifications. This allows Receivers the freedom to retrieve this URL any time they think they have missed a hook. 132 | 133 | Another pattern is to stop sending hook notifications to the Sender 134 | after a certain number of failures and begin periodically sending a hook 135 | notification with a URL that can be used to retrieve missed messages. The Sender keeps sending this hook at regular intervals until it is either claimed or expires. 136 | 137 | Retrieving messages with a claim check can be a manual process on the Receiver's side, or a release mechanism on the Sender's side. In the first case, the Receiver needs to go out and fetch those messages from the Sender itself. In the second instance, requesting the claim URL instructs the Sender to release the missed messages and resend them. 138 | 139 | -------------------------------------------------------------------------------- /posts/docs/security.md: -------------------------------------------------------------------------------- 1 | title: Security 2 | author: Zapier 3 | date: 2013-08-27 4 | 5 | 6 | ### [Home](/) > [Docs](/docs/) > {{ title }} 7 | 8 | Security in REST Hooks takes a slightly different approach than typical API access. While the security mechanisms described below can still use your existing authentication process, there are some extra steps that can be taken to ensure a completely secure channel. 9 | 10 | ## Use Existing API Authentication 11 | 12 | The first step to a secure REST Hook implementation is to make use of the existing authentication mechanism for the Sender's API. When setting up the subscription, the API calls to do the handshake should include the standard authentication mechanism (Basic Auth, API key, OAuth access token, etc.). 13 | 14 | There are two broad patterns to ensure the integrity of your hooks: 15 | 16 | 1. Be very careful who is allowed to receive hooks (identity confirmation, signatures, etc.) 17 | 2. Don't send any sensitive information at all (notification only, skinny payloads) 18 | 19 | 20 | ## Identity Confirmation 21 | 22 | To ensure that a Receiver actually intends to receive hooks from a Sender and that the hooks are really from the Sender, it is often wise to have some checks in place to confirm both intent and legitimacy. 23 | 24 | 25 | ### Confirming Receiver's Intent to Subscribe 26 | 27 | To ensure that a Receiver actually intends to receive hooks from a Sender, a subscription uses a temporary secret in the initial handshake. 28 | 29 | When the Sender responds to the Receiver's first POST request, the response includes a `X-Hook-Secret` header that has a unique string as its value. A unique value should be computed for each new subscription. 30 | 31 | Upon receiving the secret from the Sender, the Receiver needs to do one of two things: 32 | 33 | ### Option 1 (Immediate Confirmation) 34 | 35 | ![Subscription Handshake Diagram]({{STATIC_URL}}/img/subscription_handshake_diagram.png) 36 | 37 | In this case, the Receiver returns a `200` response with the secret included in the `X-Hook-Secret` header. 38 | 39 | ### Option 2 (Delayed Confirmation) 40 | 41 | ![Delayed Subscription Handshake Diagram]({{STATIC_URL}}/img/subscription_handshake_delayed_diagram.png) 42 | 43 | In this case, the Receiver returns a `200` response without the secret, then later sends another request to the Sender with the secret in the header. 44 | 45 | In the first case, the Sender will activate the subscription immediately and begin sending updates. In the second case, the Sender should treat the subscription as inactive and not provide updates. Once the Receiver sends the second request to confirm the secret, the Sender can consider the subscription active. 46 | 47 | ### Confirming Hook Legitimacy 48 | 49 | To prove the authenticity of subsequent messages, the Sender can use a shared secret (think API key or OAuth client_secret). 50 | 51 | For each message, the Sender signs the message by computing an HMAC of the shared secret plus request body and placing the signature in the `X-Hook-Signature` header. The Receiver then verifies the signature to know that the message is authentic. Verification is as simple as computing the same HMAC and comparing it to the `X-Hook-Signature` header value. 52 | 53 | ![Example Hook with Sender Signature]({{STATIC_URL}}/img/hook_diagram.png) 54 | 55 | ## Skinny Payloads 56 | 57 | Another way to secure messages is to replace the payload with a URL or unique ID for the resource rather than a full representation of the resource. Under this paradigm, the hook acts more like a "notification," telling the Receiver to make the necessary API calls to complete the transaction. The benefit of this approach is that the Receiver must make an authenticated API call to obtain data, so access can be regulated via normal means. 58 | 59 | -------------------------------------------------------------------------------- /posts/profiles/how-active-campaign-uses-rest-hooks.md: -------------------------------------------------------------------------------- 1 | title: How ActiveCampaign Uses REST Hooks 2 | author: Interview with Matt Thommes 3 | date: 2013-08-26 4 | comments: false 5 | 6 | ### [Home](/) > {{ title }} 7 | 8 |
We got to the point where polling would sometimes disrupt service.
9 | 10 | 11 | 12 | ## About ActiveCampaign 13 | 14 | ActiveCampaign is a portable, easy Email Marketing platform targeted at small and medium size businesses. Create email marketing and HTML newsletter campaigns in minutes. Powerful email marketing software features with free premium templates. 15 | 16 | ## Case Study 17 | 18 | ActiveCampaign has been committed to developers from the very beginning. They built their REST API 3-5 years ago to begin fostering a developer community and since then they've grown their platform to the point where load from API polling would sometimes disrupt service. 19 | 20 | About a year ago, ActiveCampaign implemented REST Hooks. Their server load has been reduced and they get fewer support tickets regarding polling. 21 | 22 | Let's take a look at how exactly ActiveCampaign uses REST Hooks. 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
REST hook featureImplemented?
Webhooks
Subscription based
Intent verification
Identity verification
Skinny payloads
Retries
Batching
Order of delivery
62 | 63 | ## REST Hooks 64 | 65 | ActiveCampaign's REST API supports several methods for interacting with webhooks. They offer an endpoint to create, list, delete, and review specific hooks. Hooks can be triggered by several things including: 66 | 67 | 1. Changes to a contact (subscribe, bounce, etc.) 68 | 2. Changes by an admin user (adding a contact manually) 69 | 3. Events by the system (automated emails, etc.) 70 | 4. Events through the API (adding a contact via the API) 71 | 72 | ## Fat Payloads 73 | 74 | ActiveCampaign sends all their hooks over the wire with full API responses, meaning you do not need to perform another API call in order to get usable data. 75 | 76 | ## Retries 77 | 78 | While ActiveCampaign's API does not retry failed hooks, they will store a delivered status which you can request specifically by hitting their hook list endpoint. This could be used to periodically verify you've received all hooks or to access old hooks. 79 | 80 | ## Inline Unsubscribe 81 | 82 | If your hook endpoint returns a `410` response, then ActiveCampaign will automatically delete and clean-up the hook for you. 83 | 84 | ## ActiveCampaign Resources 85 | 86 | [ActiveCampaign Home Page](http://www.activecampaign.com/) 87 | [API Documentation](http://www.activecampaign.com/api) 88 | [REST Hooks Documentation](http://www.activecampaign.com/api/webhooks.php) -------------------------------------------------------------------------------- /posts/profiles/how-emma-uses-rest-hooks.md: -------------------------------------------------------------------------------- 1 | title: How Emma Uses REST Hooks 2 | author: Interview with Alex Ezell 3 | date: 2013-08-26 4 | comments: false 5 | 6 | ### [Home](/) > {{ title }} 7 | 8 |
Web services would just come to a crawl because [partners] were doing 1000 requests per second.
9 | 10 | 11 | 12 | ## About Emma 13 | 14 | Emma is an email marketing platform. With Emma you can design stylish email campaigns with easy-to-use features, send those campaigns to customers and collect statistics about how well they perform. 15 | 16 | ## Case Study 17 | 18 | Emma got into the API space early. They've had some version of their API for almost 7 or 8 years now. Since then, the API landscape has changed dramatically. 19 | 20 | Their original SOAP version was replaced a few years ago with a modern REST API. Only a few months after launching their REST API, they added REST hook support based on initial feedback. 21 | 22 | Over the last year or so, they've expanded their webhook coverage significantly. The biggest advantages of REST Hooks for Emma is how granular and simple they are. 23 | 24 | Hooks allow Emma to avoid complex integrations with partners. Before, integrations with partners would take months and Emma would spend a lot of time guiding developers through setting up polling infrastructure and other details. 25 | 26 | With REST Hooks, Emma can point to their lighter weight, more granular hook based system for getting at very specific information, like email bounces. 27 | 28 | Emma targets small and medium size companies which often don't have dedicated IT teams or people with domain knowledge about APIs. REST Hooks dramatically simplify how much time and effort they need to put into 3rd party developer support. 29 | 30 | Let's take a look at how Emma uses REST Hooks. 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
REST hook featureImplemented?
Webhooks
Subscription based
Intent verification
Identitiy verification
Skinny payloads
Retries
Batching
Order of delivery
70 | 71 | ## REST Hooks 72 | 73 | Emma's REST API supports several methods for interacting with webhooks. Classically, you can create, update, view, and delete hooks through their REST API. Additionally, there is an endpoint to access a list of available events which the user could choose from. 74 | 75 | ## Skinny Payloads 76 | 77 | Emma sends all their hooks as skinny payloads. This means the hook payload itself simply contains IDs and references back to their REST API. This is a useful security mechanism because it guarentees that the receiver of the hook has a valid API key before allowing access to any sensitive business data. 78 | 79 | ## Emma Resources 80 | 81 | [Emma Home Page](http://www.myemma.com/) 82 | [API Documentation](http://api.myemma.com/api/) 83 | [REST Hooks Documentation](http://api.myemma.com/api/external/webhooks.html) -------------------------------------------------------------------------------- /posts/profiles/how-formstack-uses-rest-hooks.md: -------------------------------------------------------------------------------- 1 | title: How Formstack Uses REST Hooks 2 | author: Interview with Brandon Peters 3 | date: 2013-08-27 4 | comments: false 5 | 6 | ### [Home](/) > {{ title }} 7 | 8 |
We did notice a huge drop in the amount of server resources dedicated towards our API.
9 | 10 | 11 | 12 | ## About Formstack 13 | 14 | Formstack is an Online HTML Form Builder that lets you create all types of online forms. Build order forms, contact forms, registration forms, & online surveys. 15 | 16 | ## Case Study 17 | 18 | Formstack built their original API when their product launched but adoption was so-so and lots of developers were hitting rate-limits. 19 | 20 | While refreshing their API to be more usable about a year ago, the Formstack team made sure to include REST Hooks support. Not only did it provide a much better user experience for those consuming the API, but it also gave an outlet for users demanding more integrations. 21 | 22 | Even better, after adopting REST Hooks they saw a significant drop in the amount of server resources needed to power their API, giving them improved performance to go with the improved user experience. 23 | 24 | Let's take a look at how exactly Formstack uses REST Hooks. 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
REST hook featureImplemented?
Webhooks
Subscription based
Intent verification
Identity verification
Skinny payloads
Retries
Batching
Order of delivery
64 | 65 | ## REST Hooks 66 | 67 | Formstack is a classic example of REST Hooks. They have a REST componenet to subscibe, access and unsubscribe from hooks. Addionally, they offer many toggles which can be used to make REST Hooks more secure from the receiver's point of view. 68 | 69 | ## Identity Verification 70 | 71 | Formstack uses a handshake key to identify they are a verified sender. This handshake key is set up specified by the receiver when the subscription is set up for the first time. 72 | 73 | ## Skinny Payloads 74 | 75 | Formstack requires you to specify wether you'd like skinny or fat payloads when you subscribe to receive hooks. You can send along a true of false value for `append_data` when subscribing. 76 | 77 | ## Formstack Resources 78 | 79 | [Formstack Home Page](http://www.Formstack.com/) 80 | [API Documentation](https://www.formstack.com/developers/api) 81 | [REST Hooks Documentation](https://www.formstack.com/developers/webhooks) -------------------------------------------------------------------------------- /posts/profiles/how-podio-uses-rest-hooks.md: -------------------------------------------------------------------------------- 1 | title: How Podio Uses REST Hooks 2 | author: Interview with Andreas Pedersen 3 | date: 2013-09-03 4 | comments: false 5 | 6 | ### [Home](/) > {{ title }} 7 | 8 |
Our partners can provision new customers much more efficiently. That's something our partners are very very happy about.
9 | 10 | 11 | 12 | ## About Podio 13 | 14 | Online work platform for collaboration and project management in one central place with tasks, calendar, contacts, activity stream and the ability to build business apps. 15 | 16 | ## Case Study 17 | 18 | Podio built their original REST API right along side their product. Following modern product techniques, their frontend, iOS and Android apps all consume the exact same API that is exposed to 3rd party developers. 19 | 20 | Even at the beginning, Podio was looking to scale: they implemented REST Hooks and rate limitting with an eye towards the future. 21 | 22 | Some of Podio's most useful endpoints are those with filters which also happen to be the most expensive to generate. REST Hooks workaround this limitation. They enable both the data developers want as well as lighter server load. 23 | 24 | REST Hooks (subscription webhooks) actually happened accidentally. Because all of Podio's frontends consume the same API, REST Hooks were a necessity for dogfooding. 25 | 26 | In time they learned this was a great decision after hearing from elated partners and 3rd party developers. 27 | 28 | Let's take a look at the details about how Podio uses REST Hooks. 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
REST hook featureImplemented?
Webhooks
Subscription based
Intent verification
Identitiy verification
Skinny payloads
Retries
Batching
Order of delivery
68 | 69 | ## REST Hooks 70 | 71 | Podio REST Hooks can actually be created in one of two ways. They can be created as classic REST Hooks programatically via the REST API or the user can define webhooks within Podio's developer tools. 72 | 73 | Following Podio's flexible model, there are several "nouns" and "verbs" you can subscribe to which are documented [here](https://developers.podio.com/doc/hooks). 74 | 75 | ## Intent Verifications 76 | 77 | Podio enforces intent when subscribing to new hook endpoints. Every new hook must be verified. A notification will be send to the new hook URL and it is expected to return a special `code` along with a 2xx response. 78 | 79 | Over time, hooks must continually respond with 2xx. If there are 50 consecutive non-2xx responses, the hook will become inactive and have to be re-activated either by the user or by the application via a teardown/setup cycle. 80 | 81 | ## Skinny Payloads 82 | 83 | Podio only sends along a lightweight payload that contains the `id` of the item in question and a webhook `id`. Developers should turn around and call the Podio API to access the full data set. This is a security feature which enforced that developers have valid access tokens to consume hook based content. 84 | 85 | 86 | ## Order of Delivery 87 | 88 | Podio sends along a webhook `id` which can be used to order incoming requests. Even though order is specified, it's really not required because Podio has skinny payloads. You'll always need to fetch the most recently resprentation of a resource after receiving a hook, making ordering implications moot. 89 | 90 | ## Podio Resources 91 | 92 | [Podio Home Page](http://podio.com/) 93 | [API Documentation](https://developers.podio.com/doc) 94 | [REST Hooks Documentation](https://developers.podio.com/doc/hooks) -------------------------------------------------------------------------------- /posts/profiles/how-shopify-uses-rest-hooks.md: -------------------------------------------------------------------------------- 1 | title: How Shopify Uses REST Hooks 2 | author: Interview with David Underwood 3 | date: 2013-09-18 4 | comments: false 5 | 6 | ### [Home](/) > {{ title }} 7 | 8 |
We were seeing really complex Shopify add-on setup intructions and our hook subscription mechanism solved that.
9 | 10 | 11 | 12 | ## About Shopfy 13 | 14 | Shopify is a powerful ecommerce website solution that allows you to sell online by providing everything you need to create an online store. 15 | 16 | ## Case Study 17 | 18 | Shopify launches their original API in 2009 in response to developers wanting to integrate with their Shopify online stores. Shopify is a Ruby on Rails shop which made it easy to spin up their original API. 19 | 20 | REST Hooks were added after the original launch. Ecommerce is very real-time so an API that developers had to poll against was a serious drawback. Additionally, because so many Shopify customers are not developers, it had to be very user friendly to set up hook subscriptions. This is what REST Hooks offers. 21 | 22 | In addition to the user-facing benefits, Shopify was able to reduce their server load dramatically. 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
REST hook featureImplemented?
Webhooks
Subscription based
Intent verification
Identity verification
Skinny payloads
Retries
Batching
Order of delivery
62 | 63 | ## REST Hooks 64 | 65 | Shopify offers the full gamut of hook subscription management. They have about 12 different hooks developers [can subscribe to](http://docs.shopify.com/api/webhook). They offer endpoints on their REST API to list hook subscriptions, receive counts (useful for verification), and create new webhook subscriptions. 66 | 67 | Additionally, Shopify still lets developers and users create REST Hook subscriptions via their user intercace -- an increasingly common pattern for services with large user bases. 68 | 69 | ## Retries 70 | 71 | Shopify has implemented a 10-second timeout period and a retry period for subscriptions. They wait 10 seconds for a response to each request, and if there isn't one or they get an error, they will retry the connection to a total of 19 times over the next 48 hours. A webhook will be deleted if there are 19 consecutive failures for the exact same webhook. 72 | 73 | ## Fat Payloads 74 | 75 | Shopify sends all their hooks over the wire with full API responses, meaning you do not need to perform another API call in order to get usable data. 76 | 77 | ## Identity Verification 78 | 79 | Webhooks created through the API by a Shopify App can be verified by calculating a digital signature. The digital signature is an HMAC SHA256 hash. Webhooks created manually through the Shopify UI cannot be verified. 80 | 81 | You can read more about the verification procedure (and find sample code) [here](http://docs.shopify.com/api/tutorials/using-webhooks#verify-webhook). 82 | 83 | 84 | ## Shopify Resources 85 | 86 | [Shopify Home Page](http://shopify.com) 87 | [API Documentation](http://docs.shopify.com/api/) 88 | [REST Hooks Documentation](http://docs.shopify.com/api/webhook) 89 | -------------------------------------------------------------------------------- /posts/profiles/how-wufoo-uses-rest-hooks.md: -------------------------------------------------------------------------------- 1 | title: How Wufoo Uses REST Hooks 2 | author: Interview with Timothy Sabat 3 | date: 2013-08-26 4 | comments: false 5 | 6 | ### [Home](/) > {{ title }} 7 | 8 |
We were having trouble with people who were basically calling this API ... every minute or even every 30 seconds.
9 | 10 | 11 | 12 | ## About Wufoo 13 | 14 | Wufoo's HTML form builder helps you create online web forms to power your contact forms, online surveys, event registrations, accept payments and more. 15 | 16 | ## Case Study 17 | 18 | Wufoo, like many others, has been in the API space a long time. They implemented their original non-REST APIs in 2008 and added their modern REST API about 3 years ago. 19 | 20 | REST Hooks were added to the Wufoo API as part of an integrations push once they realized there was a lot of value for them to "cut out the polling middle man." 21 | 22 | Wufoo is form software and the most common integration is putting the form data into another system. Classically, the only way to detect new form submissions was through polling. 23 | 24 | Additionally, MySQL counts were a very expensive of generating an API response. Developers were polling the API every minute or even every 30 seconds looking for new responses in order to provide real-time-like experiences. 25 | 26 | Wufoo had a "huge offenders list" and had to implement more strict API rate limitting so their level of service wouldn't be degraded across the board. But they didn't want to just take something away. Wufoo decided to offer hooks as the obvious alternative for developers who were overloading their servers via polling. 27 | 28 | The other big part of REST Hooks is the subscription mechanism. Wufoo had a lot of big partners telling them they wanted to see their logo listed inside Wufoo's API. But Wufoo did not want to build one-off integrations with all these various services. So they built the subscription mechanism such that when developers registered a REST hook, their app's logo was automatically shown inside Wufoo as an available service to send data to. 29 | 30 | Let's take a look at the details about how Wufoo uses REST Hooks. 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
REST hook featureImplemented?
Webhooks
Subscription based
Intent verification
Identitiy verification
Skinny payloads
Retries
Batching
Order of delivery
70 | 71 | ## REST Hooks 72 | 73 | Wufoo originally offers hooks in several areas of their product. They have a user-facing interface to define hook callbacks and they also have a REST API which can be used to programmatically subscribe (ie. REST Hooks). 74 | 75 | Once a hook has been subscribed to a specific form or user, the user can log in to Wufoo and choose more forms to send to the service that subscribed. This is one of the unique things about Wufoo's REST Hooks. 76 | 77 | ## Identitiy Verifications 78 | 79 | Wufoo optionally allows a handshake key to be sent along with the hook subscription. This allows the receiver to define a secret word or phrase that will always exist inside any POST from Wufoo to the receiver. This can be used to verify the identity of the sender. 80 | 81 | ## Fat Payloads 82 | 83 | Wufoo sends along the entire form response inline inside the hook payload. Optionally, Wufoo can also send along information about the exact field structure of the form that was submitted. 84 | 85 | ## Retries 86 | 87 | Wufoo checks the response for non-200 and retries 3 times before the web hook is dropped off the queue. A back off mechanism is in place, to retry in roughly 5, 10, and 15 min increments. 88 | 89 | ## Order of Delivery 90 | 91 | Wufoo does not have any hook identifiers but they do include a timestamp. Additionally, order of delivery is less important for Wufoo because their forms are atomic and 1-to-1 with each hook. A form can never be sumitted twice and can't be updated by the end user. 92 | 93 | ## Wufoo Resources 94 | 95 | [Wufoo Home Page](http://wufoo.com/) 96 | [API Documentation](http://www.wufoo.com/docs/api/v3/) 97 | [REST Hooks Documentation](http://www.wufoo.com/docs/api/v3/webhooks/) -------------------------------------------------------------------------------- /posts/profiles/how-zapier-uses-rest-hooks.md: -------------------------------------------------------------------------------- 1 | title: How Zapier Uses REST Hooks 2 | author: Mike Knoop 3 | date: 2013-09-09 4 | comments: false 5 | 6 | ### [Home](/) > {{ title }} 7 | 8 |
Stop the Polling Madness!
9 | 10 | ## About Zapier 11 | 12 | Zapier enables you to automate tasks between other online services, 13 | such as Salesforce, Basecamp and Gmail. 14 | 15 | Imagine capturing Wufoo form leads automatically into Salesforce or displaying new Paypal sales in your Campfire team chat room. In all, more than 235 apps are connected through its web automation platform. 16 | 17 | ## Case Study 18 | 19 | Zapier is a bit unique as it integrate with other APIs instead of 20 | hosting its own. While Zapier does not formally have an API, their developer platform enables developers to add APIs to Zapier through a UI. If an API does not conform precisely to the standard Zapier expects, there is a Scripting API available which developer can use to transform requests and responses to conform. 21 | 22 | REST Hooks are a large part of Zapier's developer platform. Instead of needing to constantly poll other APIs looking for changes, REST Hooks enable huge performance optimizations. 23 | 24 | You can find the full published spec about [how Zapier uses REST Hooks here](https://zapier.com/developer/reference/#rest-hooks). 25 | 26 | ## Zapier Resources 27 | 28 | [Zapier Home Page](http://www.zapier.com/) 29 | [API Documentation](https://zapier.com/developer/) 30 | [REST Hooks Documentation](https://zapier.com/developer/reference/#rest-hooks) 31 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask~=0.12.4 2 | Flask-FlatPages==0.3 3 | houdini.py==0.1.0 4 | Jinja2~=2.10.1 5 | misaka==1.0.2 6 | pygments==1.6 7 | simplejson==2.6.2 8 | gunicorn==19.9 -------------------------------------------------------------------------------- /resthooks.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, Markup 2 | from flask import request, send_from_directory, render_template, render_template_string 3 | from werkzeug.routing import BaseConverter 4 | 5 | class RegexConverter(BaseConverter): 6 | def __init__(self, url_map, *items): 7 | super(RegexConverter, self).__init__(url_map) 8 | self.regex = items[0] 9 | 10 | app = Flask(__name__) 11 | app.config.from_pyfile('config.py') 12 | app.url_map.converters['regex'] = RegexConverter 13 | 14 | from flask_flatpages import FlatPages 15 | pages = FlatPages(app) 16 | 17 | import misaka 18 | import operator 19 | import os 20 | import houdini 21 | import datetime 22 | 23 | from pygments import highlight 24 | from pygments.lexers import get_lexer_by_name 25 | from pygments.formatters import HtmlFormatter 26 | 27 | ROOT = os.path.dirname(os.path.realpath(__file__)) 28 | STATIC_URL = app.config['STATIC_URL'] 29 | POSTS_PER_PAGE = app.config['POSTS_PER_PAGE'] 30 | FLATPAGES_EXTENSION = app.config['FLATPAGES_EXTENSION'] 31 | DEBUG = app.config['DEBUG'] 32 | 33 | GLOBAL_CONTEXT = { 34 | 'STATIC_URL': STATIC_URL, 35 | 'now': datetime.datetime.now() 36 | } 37 | 38 | @app.route('/') 39 | def index(): 40 | return render_template('index.html', **GLOBAL_CONTEXT) 41 | 42 | @app.route('/favicon.ico') 43 | def favicon(): 44 | return send_from_directory(ROOT + app.config['STATIC_URL'], 'favicon.ico') 45 | 46 | @app.route('//') 47 | def post(slug): 48 | """ 49 | Render a single post to the screen by slug `post` or 404 50 | """ 51 | p = pages.get(slug) 52 | if p: 53 | context = GLOBAL_CONTEXT.copy() 54 | context.update(p.meta) 55 | md = render_markdown(render_template_string(p.body, **context)) 56 | post = { 57 | 'content': md, 58 | 'meta': p.meta, 59 | 'path': p.path, 60 | 'caption': smart_truncate(md.striptags(), 120, '...'), 61 | } 62 | return render_template('post.html', post=post, **GLOBAL_CONTEXT) 63 | else: 64 | return render_template('404.html', **GLOBAL_CONTEXT) 65 | 66 | @app.route('/static/css/pygments.css') 67 | def pygments_css(): 68 | style = HtmlFormatter(style='monokai').get_style_defs('div.highlight') 69 | if DEBUG: 70 | with open('static/css/pygments.css', 'w') as f: 71 | f.write(style) 72 | return style, 200, {'Content-Type': 'text/css'} 73 | 74 | def smart_truncate(content, length=100, suffix='...'): 75 | if len(content) <= length: 76 | return content 77 | else: 78 | return ' '.join(content[:length+1].split(' ')[0:-1]) + suffix 79 | 80 | def render_markdown(md): 81 | class CustomRenderer(misaka.HtmlRenderer, misaka.SmartyPants): 82 | def block_code(self, text, lang): 83 | if not lang: 84 | return '\n
%s
\n' % \ 85 | houdini.escape_html(text.strip()) 86 | lexer = get_lexer_by_name(lang, stripall=True) 87 | formatter = HtmlFormatter() 88 | return highlight(text, lexer, formatter) 89 | 90 | markdown = misaka.Markdown( 91 | renderer=CustomRenderer(), 92 | extensions=reduce(operator.or_, [misaka.EXT_FENCED_CODE, 93 | misaka.EXT_STRIKETHROUGH, 94 | misaka.EXT_NO_INTRA_EMPHASIS, 95 | misaka.EXT_TABLES, 96 | misaka.EXT_AUTOLINK, 97 | misaka.EXT_SPACE_HEADERS])) 98 | return Markup(markdown.render(md)) 99 | 100 | if __name__ == "__main__": 101 | app.run(threaded=True) 102 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-2.7.16 2 | -------------------------------------------------------------------------------- /static/css/common.css: -------------------------------------------------------------------------------- 1 | html { 2 | background: #f2f2f2 url('../img/pattern.png'); 3 | height: 100%; 4 | width: 100%; 5 | position: relative; 6 | } 7 | 8 | body { 9 | color: #555; 10 | font-family: 'Droid Sans', sans-serif; 11 | margin: 0 auto; 12 | height: 100%; 13 | position: relative; 14 | } 15 | 16 | .clearfix { 17 | clear: both; 18 | } 19 | 20 | .accent { 21 | color: #c23963; 22 | } 23 | 24 | .bold { 25 | font-weight: 700; 26 | } 27 | 28 | .italic { 29 | font-style: italic; 30 | } 31 | 32 | .center { 33 | text-align: center; 34 | } 35 | 36 | a { 37 | color: inherit; 38 | font-weight: 700; 39 | text-decoration: none; 40 | border-bottom: 1px solid; 41 | } 42 | 43 | a:hover { 44 | border-bottom: 0; 45 | } 46 | 47 | /*h1, h2, h3, h4, h5, h6, p { 48 | letter-spacing: 1px; 49 | word-spacing: -2px; 50 | }*/ 51 | 52 | h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { 53 | color: #c23963; 54 | text-decoration: none; 55 | } 56 | 57 | h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a { 58 | color: #c23963; 59 | } 60 | 61 | h1 { 62 | font-size: 1.7em; 63 | line-height: 1.2em; 64 | margin-bottom: 20px; 65 | } 66 | 67 | /*h2 { 68 | font-size: 1.1em; 69 | line-height: 1.2em; 70 | margin: 0 0 20px; 71 | font-weight: normal; 72 | }*/ 73 | 74 | h2 { 75 | color: #444; 76 | font-size: 19px; 77 | letter-spacing: 1px; 78 | word-spacing: -2px; 79 | line-height: 1.75em; 80 | margin-bottom: 10px; 81 | font-weight: normal; 82 | } 83 | 84 | h3 { 85 | line-height: 1.5em; 86 | margin-bottom: 15px; 87 | } 88 | 89 | h4 { 90 | font-size: 16px; 91 | line-height: 1.2em; 92 | margin-bottom: 20px; 93 | } 94 | 95 | p { 96 | font-weight: 400; 97 | font-size: 15px; 98 | line-height: 1.75em; 99 | padding: 0; 100 | margin-bottom: 20px; 101 | } 102 | 103 | p:last-child { 104 | margin-bottom: 0; 105 | } 106 | 107 | blockquote { 108 | background: #ddd; 109 | padding: 10px; 110 | margin: 0 -10px 35px -10px;; 111 | -moz-border-radius: 2px; 112 | -webkit-border-radius: 2px; 113 | border-radius: 2px; 114 | } 115 | blockquote p { 116 | margin: 0; 117 | position: relative; 118 | margin-left: 30px; 119 | padding: 0 10px; 120 | } 121 | blockquote:before { 122 | content: '\201C'; /* unicode left double quote */ 123 | font-size: 4.5em; 124 | position: absolute; 125 | margin-left: 0; 126 | margin-top: -7px; 127 | filter: alpha(opacity=(50)); 128 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=(50))"; 129 | -moz-opacity: 0.5; 130 | -khtml-opacity: 0.5; 131 | opacity: 0.5; 132 | } 133 | 134 | ol, ul { 135 | font-weight: 400; 136 | font-size: 0.9em; 137 | line-height: 2em; 138 | padding: 0; 139 | margin-bottom: 25px; 140 | margin-left: 45px; 141 | list-style: inherit; 142 | } 143 | ol { 144 | list-style-type: decimal; 145 | } 146 | ol li, ul li { 147 | list-style-position: outside; 148 | } 149 | 150 | hr { 151 | margin: 25px 0; 152 | } 153 | 154 | caption { 155 | font-style: italic; 156 | } 157 | 158 | .hover { 159 | -webkit-transition-property:color, color; 160 | -webkit-transition-duration: 0.25s, 0.25s; 161 | -webkit-transition-timing-function: linear, linear; 162 | } 163 | 164 | .hover:hover { 165 | color: #c23963; 166 | } 167 | 168 | div.highlight { 169 | font-size: 0.8em; 170 | line-height: 1.2em; 171 | margin: 45px 0; /* extra padding top/bottom due to backsplash :before offset */; 172 | padding: 10px; 173 | max-width: 580px; 174 | position: relative; 175 | -moz-border-radius: 2px; 176 | -webkit-border-radius: 2px; 177 | border-radius: 2px; 178 | } 179 | div.highlight:before { 180 | position: absolute; 181 | top: -10px; 182 | right: -10px; 183 | bottom: -10px; 184 | left: -10px; 185 | display: block; 186 | content: ""; 187 | background: #eee url('../img/pattern.png'); 188 | -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset, 0 0 20px rgba(0, 0, 0, 0.05) inset; 189 | -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset, 0 0 20px rgba(0, 0, 0, 0.05) inset; 190 | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset, 0 0 20px rgba(0, 0, 0, 0.05) inset; 191 | -moz-border-radius: 2px; 192 | -webkit-border-radius: 2px; 193 | border-radius: 2px; 194 | z-index: -1; 195 | } 196 | pre { 197 | overflow: auto; 198 | font-family: 'Envy Code R', 'Droid Sans Mono', 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Inconsolata, Consolas, monospace; 199 | } 200 | .content img, .content > iframe { 201 | padding: 10px; 202 | margin-left: -10px; 203 | margin-right: -10px; 204 | margin-bottom: 20px; 205 | width: 100%; 206 | } 207 | 208 | .menu { 209 | padding: 40px 0; 210 | text-align: center; 211 | } 212 | 213 | .menu .logo { 214 | padding-right: 40px; 215 | margin-right: 20px; 216 | border-right: 1px solid #ddd; 217 | } 218 | 219 | .logo .rest { 220 | font-size: 34px; 221 | font-weight: bold; 222 | } 223 | 224 | .logo .hooks { 225 | font-size: 26px; 226 | font-weight: bold; 227 | } 228 | 229 | .menu a { 230 | border: 0; 231 | color: #777; 232 | text-decoration: none; 233 | display: inline-block; 234 | vertical-align: middle; 235 | } 236 | 237 | .menu .item { 238 | margin: 0 25px; 239 | font-weight: normal; 240 | font-size: 20px; 241 | } 242 | 243 | .menu .item i { 244 | position: relative; 245 | font-size: 32px; 246 | z-index: 2; 247 | right: 4px; 248 | top: 4px; 249 | } 250 | 251 | .pop { 252 | background: #c23963; 253 | color: #FCBFD2; 254 | margin-bottom: 20px; 255 | padding: 60px 0; 256 | text-align: center; 257 | } 258 | 259 | .pop h1 { 260 | letter-spacing: 1px; 261 | } 262 | .pop h1 span { 263 | font-size: 150px; 264 | font-weight: normal; 265 | line-height: 1em; 266 | } 267 | .pop h2 { 268 | color: #FFD3E1; 269 | font-size: 14px; 270 | letter-spacing: 0.75px; 271 | word-spacing: -1px; 272 | font-weight: normal; 273 | margin-bottom: 30px; 274 | } 275 | .pop h1, .pop a { 276 | color: #fefefe; 277 | } 278 | .pop a:hover { 279 | color: #fefefe; 280 | } 281 | .pop .or { 282 | font-size: 14px; 283 | padding: 0 3px; 284 | } 285 | 286 | .index ul { 287 | margin: 0 20px; 288 | color: #444; 289 | font-size: 1.1em; 290 | } 291 | .index ul li { 292 | list-style-type: none; 293 | font-weight: bold; 294 | line-height: 30px; 295 | } 296 | .index ul li:not(:first-child) { 297 | margin: 40px 0 20px; 298 | } 299 | 300 | /*.index .content p { 301 | font-size: 14px; 302 | }*/ 303 | 304 | .content { 305 | max-width: 600px; 306 | margin: 0 auto; 307 | padding-left: 20px; 308 | padding-right: 20px; 309 | } 310 | 311 | .footer { 312 | color: #777; 313 | text-align: center; 314 | padding: 40px 0 70px; 315 | font-size: 14px; 316 | line-height: 26px; 317 | } 318 | 319 | .footer a { 320 | color: #c23963; 321 | } 322 | 323 | hr { 324 | border: 0; 325 | height: 1px; 326 | background: #333; 327 | background-image: -webkit-linear-gradient(left, #EEE, #CCC, #EEE); 328 | background-image: -moz-linear-gradient(left, #EEE, #CCC, #EEE); 329 | background-image: -ms-linear-gradient(left, #EEE, #CCC, #EEE); 330 | background-image: -o-linear-gradient(left, #EEE, #CCC, #EEE); 331 | max-width: 600px; 332 | margin: 20px auto; 333 | } 334 | 335 | .left { 336 | box-sizing: border-box; 337 | -moz-box-sizing: border-box; /* Firefox */ 338 | float: left; 339 | width: 50%; 340 | padding: 20px 20px; 341 | } 342 | 343 | .right { 344 | box-sizing: border-box; 345 | -moz-box-sizing: border-box; /* Firefox */ 346 | float: right; 347 | width: 50%; 348 | padding: 20px 20px; 349 | } 350 | 351 | .full { 352 | box-sizing: border-box; 353 | -moz-box-sizing: border-box; /* Firefox */ 354 | float: left; 355 | width: 100%; 356 | padding: 20px 20px; 357 | } 358 | 359 | .left a, .right a, .center a, .index .content p a { 360 | color: #c23963; 361 | } 362 | 363 | .index .services { 364 | text-align: center; 365 | padding-bottom: 20px; 366 | } 367 | .index .services a { 368 | font-size: 14px; 369 | line-height: 24px; 370 | } 371 | .index img { 372 | display: block; 373 | width: 80%; 374 | border: 0; 375 | background: none; 376 | margin: 0 auto 10px; 377 | } 378 | .index .services .left, .index .services .right, .index .services .center { 379 | margin-bottom: 20px; 380 | width: 50%; 381 | } 382 | .index .services .center { 383 | margin-left: auto; 384 | margin-right: auto; 385 | } 386 | .index .services img { 387 | filter: url("data:image/svg+xml;utf8,#grayscale"); /* Firefox 10+, Firefox on Android */ 388 | filter: gray; /* IE6-9 */ 389 | -webkit-filter: grayscale(100%); /* Chrome 19+, Safari 6+, Safari 6+ iOS */ 390 | width: 50%; 391 | max-width: 125px; 392 | } 393 | .index .services img:hover { 394 | filter: url("data:image/svg+xml;utf8,#grayscale"); 395 | -webkit-filter: grayscale(0%); 396 | } 397 | 398 | body.post .pop { 399 | padding: 60px 0; 400 | } 401 | body.post .pop h1 { 402 | font-size: 2em; 403 | } 404 | body.post .pop h2 { 405 | margin: 10px 0 0; 406 | } 407 | body.post .pop a { 408 | text-decoration: none; 409 | border: 0; 410 | } 411 | div.post { 412 | padding: 20px 0 40px; 413 | } 414 | div.post a { 415 | color: #c23963; 416 | } 417 | 418 | .post .quote { 419 | color: #c23963; 420 | font-size: 30px; 421 | line-height: 1.2em; 422 | padding: 25px 20px 40px 50px; 423 | position: relative; 424 | font-style: italic; 425 | } 426 | .post .quote:before { 427 | font-style: normal; 428 | content: "\201c"; 429 | display: block; 430 | font-size: 100px; 431 | position: absolute; 432 | top: 45px; 433 | left: 0; 434 | } 435 | .post table { 436 | border: 1px solid #ccc; 437 | background: #ebebeb; 438 | -moz-border-radius: 4px; 439 | -webkit-border-radius: 4px; 440 | border-radius: 4px; 441 | padding: 5px 5px 10px 5px; 442 | margin: 40px auto; 443 | padding: 15px 25px; 444 | } 445 | .post table tr { 446 | padding: 3px 0; 447 | } 448 | .post table a { 449 | font-weight: normal; 450 | } 451 | .post table td { 452 | width: 50%; 453 | padding: 8px; 454 | text-align: center; 455 | } 456 | .post table td:first-child { 457 | text-align: left; 458 | } 459 | .post table th { 460 | padding: 10px 7px; 461 | text-align: center; 462 | } 463 | .post table th:first-child { 464 | text-align: left; 465 | } 466 | .post table i { 467 | color: #c23963; 468 | } 469 | .post table i.icon-shield { 470 | color: #444; 471 | padding-left: 5px; 472 | vertical-align: text-bottom; 473 | } 474 | 475 | @media screen and (max-width: 650px) { 476 | .content, div.post { 477 | padding-left: 20px; 478 | padding-right: 20px; 479 | } 480 | .menu { 481 | padding: 20px; 482 | } 483 | .menu a { 484 | margin: 20px 0; 485 | } 486 | .menu a:first-child { 487 | display: block; 488 | width: 100%; 489 | margin-bottom: 20px 490 | } 491 | .menu .logo { 492 | border-right: 0; 493 | padding: 0; 494 | margin: 0; 495 | } 496 | .menu .logo div { 497 | display: inline; 498 | font-size: 34px; 499 | margin: 0 5px; 500 | } 501 | .left, .right { 502 | float: none; 503 | width: 100% !important; 504 | } 505 | hr { 506 | max-width: 85%; 507 | } 508 | } 509 | 510 | a.button { 511 | background-color: #FFF; 512 | border-bottom: 0 none; 513 | color: #c23963; 514 | padding: 10px 20px; 515 | display: inline-block; 516 | -moz-border-radius: 4px; 517 | -webkit-border-radius: 4px; 518 | border-radius: 4px; 519 | } 520 | 521 | a.button:hover { 522 | background-color: rgba(255, 255, 255, 0.75); 523 | border-bottom: 0 none; 524 | color: #c23963; 525 | } 526 | 527 | a.button:active { 528 | background-color: rgba(255, 255, 255, 0.3); 529 | border-bottom: 0 none; 530 | color: #FFF; 531 | } 532 | 533 | .icon-check-sign.success { 534 | color: #46a546; 535 | } 536 | 537 | .teaser { 538 | max-width:558px; 539 | margin:0 auto; 540 | text-align: center; 541 | } 542 | -------------------------------------------------------------------------------- /static/css/pygments.css: -------------------------------------------------------------------------------- 1 | div.highlight .hll { background-color: #49483e } 2 | div.highlight { background: #272822; color: #f8f8f2 } 3 | div.highlight .c { color: #75715e } /* Comment */ 4 | div.highlight .err { color: #960050; background-color: #1e0010 } /* Error */ 5 | div.highlight .k { color: #66d9ef } /* Keyword */ 6 | div.highlight .l { color: #ae81ff } /* Literal */ 7 | div.highlight .n { color: #f8f8f2 } /* Name */ 8 | div.highlight .o { color: #f92672 } /* Operator */ 9 | div.highlight .p { color: #f8f8f2 } /* Punctuation */ 10 | div.highlight .cm { color: #75715e } /* Comment.Multiline */ 11 | div.highlight .cp { color: #75715e } /* Comment.Preproc */ 12 | div.highlight .c1 { color: #75715e } /* Comment.Single */ 13 | div.highlight .cs { color: #75715e } /* Comment.Special */ 14 | div.highlight .ge { font-style: italic } /* Generic.Emph */ 15 | div.highlight .gs { font-weight: bold } /* Generic.Strong */ 16 | div.highlight .kc { color: #66d9ef } /* Keyword.Constant */ 17 | div.highlight .kd { color: #66d9ef } /* Keyword.Declaration */ 18 | div.highlight .kn { color: #f92672 } /* Keyword.Namespace */ 19 | div.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ 20 | div.highlight .kr { color: #66d9ef } /* Keyword.Reserved */ 21 | div.highlight .kt { color: #66d9ef } /* Keyword.Type */ 22 | div.highlight .ld { color: #e6db74 } /* Literal.Date */ 23 | div.highlight .m { color: #ae81ff } /* Literal.Number */ 24 | div.highlight .s { color: #e6db74 } /* Literal.String */ 25 | div.highlight .na { color: #a6e22e } /* Name.Attribute */ 26 | div.highlight .nb { color: #f8f8f2 } /* Name.Builtin */ 27 | div.highlight .nc { color: #a6e22e } /* Name.Class */ 28 | div.highlight .no { color: #66d9ef } /* Name.Constant */ 29 | div.highlight .nd { color: #a6e22e } /* Name.Decorator */ 30 | div.highlight .ni { color: #f8f8f2 } /* Name.Entity */ 31 | div.highlight .ne { color: #a6e22e } /* Name.Exception */ 32 | div.highlight .nf { color: #a6e22e } /* Name.Function */ 33 | div.highlight .nl { color: #f8f8f2 } /* Name.Label */ 34 | div.highlight .nn { color: #f8f8f2 } /* Name.Namespace */ 35 | div.highlight .nx { color: #a6e22e } /* Name.Other */ 36 | div.highlight .py { color: #f8f8f2 } /* Name.Property */ 37 | div.highlight .nt { color: #f92672 } /* Name.Tag */ 38 | div.highlight .nv { color: #f8f8f2 } /* Name.Variable */ 39 | div.highlight .ow { color: #f92672 } /* Operator.Word */ 40 | div.highlight .w { color: #f8f8f2 } /* Text.Whitespace */ 41 | div.highlight .mf { color: #ae81ff } /* Literal.Number.Float */ 42 | div.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ 43 | div.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ 44 | div.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ 45 | div.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ 46 | div.highlight .sc { color: #e6db74 } /* Literal.String.Char */ 47 | div.highlight .sd { color: #e6db74 } /* Literal.String.Doc */ 48 | div.highlight .s2 { color: #e6db74 } /* Literal.String.Double */ 49 | div.highlight .se { color: #ae81ff } /* Literal.String.Escape */ 50 | div.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ 51 | div.highlight .si { color: #e6db74 } /* Literal.String.Interpol */ 52 | div.highlight .sx { color: #e6db74 } /* Literal.String.Other */ 53 | div.highlight .sr { color: #e6db74 } /* Literal.String.Regex */ 54 | div.highlight .s1 { color: #e6db74 } /* Literal.String.Single */ 55 | div.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ 56 | div.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ 57 | div.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ 58 | div.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ 59 | div.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ 60 | div.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /static/css/reset.css: -------------------------------------------------------------------------------- 1 | /* v1.0 | 20080212 */ 2 | 3 | html, body, div, span, applet, object, iframe, 4 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 5 | a, abbr, acronym, address, big, cite, code, 6 | del, dfn, em, font, img, ins, kbd, q, s, samp, 7 | small, strike, strong, sub, sup, tt, var, 8 | b, u, i, center, 9 | dl, dt, dd, ol, ul, li, 10 | fieldset, form, label, legend, 11 | table, caption, tbody, tfoot, thead, tr, th, td, 12 | container, header, text, showcase, latest, footer { 13 | margin: 0; 14 | padding: 0; 15 | border: 0; 16 | outline: 0; 17 | font-size: 100%; 18 | vertical-align: baseline; 19 | background: transparent; 20 | } 21 | body { 22 | line-height: 1; 23 | } 24 | ol, ul { 25 | list-style: none; 26 | } 27 | blockquote, q { 28 | quotes: none; 29 | } 30 | blockquote:before, blockquote:after, 31 | q:before, q:after { 32 | content: ''; 33 | content: none; 34 | } 35 | 36 | :focus { 37 | outline: 0; 38 | } -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/favicon.ico -------------------------------------------------------------------------------- /static/img/activecampaign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/activecampaign.png -------------------------------------------------------------------------------- /static/img/asana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/asana.png -------------------------------------------------------------------------------- /static/img/emma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/emma.png -------------------------------------------------------------------------------- /static/img/formstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/formstack.png -------------------------------------------------------------------------------- /static/img/glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/glass.png -------------------------------------------------------------------------------- /static/img/hook_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/hook_diagram.png -------------------------------------------------------------------------------- /static/img/logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/logo.xcf -------------------------------------------------------------------------------- /static/img/pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/pattern.png -------------------------------------------------------------------------------- /static/img/pattern.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/pattern.xcf -------------------------------------------------------------------------------- /static/img/podio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/podio.png -------------------------------------------------------------------------------- /static/img/polling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/polling.png -------------------------------------------------------------------------------- /static/img/rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/rest.png -------------------------------------------------------------------------------- /static/img/shopify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/shopify.png -------------------------------------------------------------------------------- /static/img/stripe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/stripe.png -------------------------------------------------------------------------------- /static/img/subscription_handshake_delayed_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/subscription_handshake_delayed_diagram.png -------------------------------------------------------------------------------- /static/img/subscription_handshake_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/subscription_handshake_diagram.png -------------------------------------------------------------------------------- /static/img/webhooks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/webhooks.png -------------------------------------------------------------------------------- /static/img/wufoo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/wufoo.png -------------------------------------------------------------------------------- /static/img/zapier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zapier/resthooks/d066c691263691bbe5f09824acbb898ba935d7ce/static/img/zapier.png -------------------------------------------------------------------------------- /static/js/jquery.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v1.8.2 jquery.com | jquery.org/license */ 2 | (function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.2",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return a!=null?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
t
",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,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%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||p.guid++:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c=0)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c=0)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,d+""),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0:p.find(m,this,null,[f]).length),h[m]&&j.push(l);j.length&&u.push({elem:f,matches:j})}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bc(a,b,c,d){c=c||[],b=b||r;var e,f,i,j,k=b.nodeType;if(!a||typeof a!="string")return c;if(k!==1&&k!==9)return[];i=g(b);if(!i&&!d)if(e=P.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&h(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return w.apply(c,x.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&_&&b.getElementsByClassName)return w.apply(c,x.call(b.getElementsByClassName(j),0)),c}return bp(a.replace(L,"$1"),b,c,d,i)}function bd(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function be(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bf(a){return z(function(b){return b=+b,z(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function bg(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bh(a,b){var c,d,f,g,h,i,j,k=C[o][a];if(k)return b?0:k.slice(0);h=a,i=[],j=e.preFilter;while(h){if(!c||(d=M.exec(h)))d&&(h=h.slice(d[0].length)),i.push(f=[]);c=!1;if(d=N.exec(h))f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=d[0].replace(L," ");for(g in e.filter)(d=W[g].exec(h))&&(!j[g]||(d=j[g](d,r,!0)))&&(f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=g,c.matches=d);if(!c)break}return b?h.length:h?bc.error(a):C(a,i).slice(0)}function bi(a,b,d){var e=b.dir,f=d&&b.dir==="parentNode",g=u++;return b.first?function(b,c,d){while(b=b[e])if(f||b.nodeType===1)return a(b,c,d)}:function(b,d,h){if(!h){var i,j=t+" "+g+" ",k=j+c;while(b=b[e])if(f||b.nodeType===1){if((i=b[o])===k)return b.sizset;if(typeof i=="string"&&i.indexOf(j)===0){if(b.sizset)return b}else{b[o]=k;if(a(b,d,h))return b.sizset=!0,b;b.sizset=!1}}}else while(b=b[e])if(f||b.nodeType===1)if(a(b,d,h))return b}}function bj(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function bk(a,b,c,d,e){var f,g=[],h=0,i=a.length,j=b!=null;for(;h-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==l)||((b=c).nodeType?j(a,c,d):k(a,c,d))}];for(;i1&&bj(m),i>1&&a.slice(0,i-1).join("").replace(L,"$1"),c,i0,f=a.length>0,g=function(h,i,j,k,m){var n,o,p,q=[],s=0,u="0",x=h&&[],y=m!=null,z=l,A=h||f&&e.find.TAG("*",m&&i.parentNode||i),B=t+=z==null?1:Math.E;y&&(l=i!==r&&i,c=g.el);for(;(n=A[u])!=null;u++){if(f&&n){for(o=0;p=a[o];o++)if(p(n,i,j)){k.push(n);break}y&&(t=B,c=++g.el)}d&&((n=!p&&n)&&s--,h&&x.push(n))}s+=u;if(d&&u!==s){for(o=0;p=b[o];o++)p(x,q,i,j);if(h){if(s>0)while(u--)!x[u]&&!q[u]&&(q[u]=v.call(k));q=bk(q)}w.apply(k,q),y&&!h&&q.length>0&&s+b.length>1&&bc.uniqueSort(k)}return y&&(t=B,l=z),x};return g.el=0,d?z(g):g}function bo(a,b,c,d){var e=0,f=b.length;for(;e2&&(j=h[0]).type==="ID"&&b.nodeType===9&&!f&&e.relative[h[1].type]){b=e.find.ID(j.matches[0].replace(V,""),b,f)[0];if(!b)return c;a=a.slice(h.shift().length)}for(g=W.POS.test(a)?-1:h.length-1;g>=0;g--){j=h[g];if(e.relative[k=j.type])break;if(l=e.find[k])if(d=l(j.matches[0].replace(V,""),R.test(h[0].type)&&b.parentNode||b,f)){h.splice(g,1),a=d.length&&h.join("");if(!a)return w.apply(c,x.call(d,0)),c;break}}}return i(a,m)(d,b,f,c,R.test(a)),c}function bq(){}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=String,r=a.document,s=r.documentElement,t=0,u=0,v=[].pop,w=[].push,x=[].slice,y=[].indexOf||function(a){var b=0,c=this.length;for(;be.cacheLength&&delete a[b.shift()],a[c]=d},a)},B=A(),C=A(),D=A(),E="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",G=F.replace("w","w#"),H="([*^$|!~]?=)",I="\\["+E+"*("+F+")"+E+"*(?:"+H+E+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+G+")|)|)"+E+"*\\]",J=":("+F+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+I+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+E+"*((?:-\\d)?\\d*)"+E+"*\\)|)(?=[^-]|$)",L=new RegExp("^"+E+"+|((?:^|[^\\\\])(?:\\\\.)*)"+E+"+$","g"),M=new RegExp("^"+E+"*,"+E+"*"),N=new RegExp("^"+E+"*([\\x20\\t\\r\\n\\f>+~])"+E+"*"),O=new RegExp(J),P=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,Q=/^:not/,R=/[\x20\t\r\n\f]*[+~]/,S=/:not\($/,T=/h\d/i,U=/input|select|textarea|button/i,V=/\\(?!\\)/g,W={ID:new RegExp("^#("+F+")"),CLASS:new RegExp("^\\.("+F+")"),NAME:new RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:new RegExp("^("+F.replace("w","w*")+")"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+J),POS:new RegExp(K,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+E+"*(even|odd|(([+-]|)(\\d*)n|)"+E+"*(?:([+-]|)"+E+"*(\\d+)|))"+E+"*\\)|)","i"),needsContext:new RegExp("^"+E+"*[>+~]|"+K,"i")},X=function(a){var b=r.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},Y=X(function(a){return a.appendChild(r.createComment("")),!a.getElementsByTagName("*").length}),Z=X(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),$=X(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),_=X(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),ba=X(function(a){a.id=o+0,a.innerHTML="
",s.insertBefore(a,s.firstChild);var b=r.getElementsByName&&r.getElementsByName(o).length===2+r.getElementsByName(o+0).length;return d=!r.getElementById(o),s.removeChild(a),b});try{x.call(s.childNodes,0)[0].nodeType}catch(bb){x=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}bc.matches=function(a,b){return bc(a,null,null,b)},bc.matchesSelector=function(a,b){return bc(b,null,null,[a]).length>0},f=bc.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=f(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=f(b);return c},g=bc.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},h=bc.contains=s.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:s.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc.attr=function(a,b){var c,d=g(a);return d||(b=b.toLowerCase()),(c=e.attrHandle[b])?c(a):d||$?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},e=bc.selectors={cacheLength:50,createPseudo:z,match:W,attrHandle:Z?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:d?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:Y?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:ba&&function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:_&&function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(V,""),a[3]=(a[4]||a[5]||"").replace(V,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||bc.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&bc.error(a[0]),a},PSEUDO:function(a){var b,c;if(W.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(b=a[4])O.test(b)&&(c=bh(b,!0))&&(c=b.indexOf(")",b.length-c)-b.length)&&(b=b.slice(0,c),a[0]=a[0].slice(0,c)),a[2]=b;return a.slice(0,3)}},filter:{ID:d?function(a){return a=a.replace(V,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(V,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(V,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=B[o][a];return b||(b=B(a,new RegExp("(^|"+E+")"+a+"("+E+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return function(d,e){var f=bc.attr(d,a);return f==null?b==="!=":b?(f+="",b==="="?f===c:b==="!="?f!==c:b==="^="?c&&f.indexOf(c)===0:b==="*="?c&&f.indexOf(c)>-1:b==="$="?c&&f.substr(f.length-c.length)===c:b==="~="?(" "+f+" ").indexOf(c)>-1:b==="|="?f===c||f.substr(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d){return a==="nth"?function(a){var b,e,f=a.parentNode;if(c===1&&d===0)return!0;if(f){e=0;for(b=f.firstChild;b;b=b.nextSibling)if(b.nodeType===1){e++;if(a===b)break}}return e-=d,e===c||e%c===0&&e/c>=0}:function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b){var c,d=e.pseudos[a]||e.setFilters[a.toLowerCase()]||bc.error("unsupported pseudo: "+a);return d[o]?d(b):d.length>1?(c=[a,a,"",b],e.setFilters.hasOwnProperty(a.toLowerCase())?z(function(a,c){var e,f=d(a,b),g=f.length;while(g--)e=y.call(a,f[g]),a[e]=!(c[e]=f[g])}):function(a){return d(a,0,c)}):d}},pseudos:{not:z(function(a){var b=[],c=[],d=i(a.replace(L,"$1"));return d[o]?z(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)if(f=g[h])a[h]=!(b[h]=f)}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:z(function(a){return function(b){return bc(a,b).length>0}}),contains:z(function(a){return function(b){return(b.textContent||b.innerText||f(b)).indexOf(a)>-1}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!e.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},header:function(a){return T.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:bd("radio"),checkbox:bd("checkbox"),file:bd("file"),password:bd("password"),image:bd("image"),submit:be("submit"),reset:be("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return U.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement},first:bf(function(a,b,c){return[0]}),last:bf(function(a,b,c){return[b-1]}),eq:bf(function(a,b,c){return[c<0?c+b:c]}),even:bf(function(a,b,c){for(var d=0;d=0;)a.push(d);return a}),gt:bf(function(a,b,c){for(var d=c<0?c+b:c;++d",a.querySelectorAll("[selected]").length||e.push("\\["+E+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),X(function(a){a.innerHTML="

",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+E+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=new RegExp(e.join("|")),bp=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a))){var i,j,k=!0,l=o,m=d,n=d.nodeType===9&&a;if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){i=bh(a),(k=d.getAttribute("id"))?l=k.replace(c,"\\$&"):d.setAttribute("id",l),l="[id='"+l+"'] ",j=i.length;while(j--)i[j]=l+i[j].join("");m=R.test(a)&&d.parentNode||d,n=i.join(",")}if(n)try{return w.apply(f,x.call(m.querySelectorAll(n),0)),f}catch(p){}finally{k||d.removeAttribute("id")}}return b(a,d,f,g,h)},h&&(X(function(b){a=h.call(b,"div");try{h.call(b,"[test!='']:sizzle"),f.push("!=",J)}catch(c){}}),f=new RegExp(f.join("|")),bc.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!g(b)&&!f.test(c)&&(!e||!e.test(c)))try{var i=h.call(b,c);if(i||a||b.document&&b.document.nodeType!==11)return i}catch(j){}return bc(c,null,null,[b]).length>0})}(),e.pseudos.nth=e.pseudos.eq,e.filters=bq.prototype=e.pseudos,e.setFilters=new bq,bc.attr=p.attr,p.find=bc,p.expr=bc.selectors,p.expr[":"]=p.expr.pseudos,p.unique=bc.uniqueSort,p.text=bc.getText,p.isXMLDoc=bc.isXML,p.contains=bc.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
","
"]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); -------------------------------------------------------------------------------- /static/js/jquery.sharrre-1.3.4.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Sharrre.com - Make your sharing widget! 3 | * Version: beta 1.3.4 4 | * Author: Julien Hany 5 | * License: MIT http://en.wikipedia.org/wiki/MIT_License or GPLv2 http://en.wikipedia.org/wiki/GNU_General_Public_License 6 | */ 7 | eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}(';(6($,g,h,i){l j=\'1Y\',23={3i:\'1Y\',L:{O:C,E:C,z:C,I:C,p:C,K:C,N:C,B:C},2a:0,18:\'\',12:\'\',3:h.3h.1a,x:h.12,1p:\'1Y.3d\',y:{},1q:0,1w:w,3c:w,3b:w,2o:C,1X:6(){},38:6(){},1P:6(){},26:6(){},8:{O:{3:\'\',15:C,1j:\'37\',13:\'35-4Y\',2p:\'\'},E:{3:\'\',15:C,R:\'1L\',11:\'4V\',H:\'\',1A:\'C\',2c:\'C\',2d:\'\',1B:\'\',13:\'4R\'},z:{3:\'\',15:C,y:\'33\',2m:\'\',16:\'\',1I:\'\',13:\'35\'},I:{3:\'\',15:C,Q:\'4K\'},p:{3:\'\',15:C,1j:\'37\'},K:{3:\'\',15:C,11:\'1\'},N:{3:\'\',15:C,22:\'\'},B:{3:\'\',1s:\'\',1C:\'\',11:\'33\'}}},1n={O:"",E:"1D://4J.E.o/4x?q=4u%2X,%4j,%4i,%4h,%4f,%4e,46,%45,%44%42%41%40%2X=%27{3}%27&1y=?",z:"S://3W.3P.z.o/1/3D/y.2G?3={3}&1y=?",I:"S://3l.I.o/2.0/5a.59?54={3}&Q=1c&1y=?",p:\'S://52.p.o/4Q/2G/4B/m?3={3}&1y=?\',K:"",N:"S://1o.N.o/4z/y/L?4r=4o&3={3}&1y=?",B:""},2A={O:6(b){l c=b.4.8.O;$(b.r).X(\'.8\').Z(\'\');g.3Z={13:b.4.8.O.13};l d=0;9(A 2x===\'F\'&&d==0){d=1;(6(){l a=h.1g(\'P\');a.Q=\'x/1c\';a.1r=w;a.17=\'//3w.2w.o/Y/25.Y\';l s=h.1d(\'P\')[0];s.1e.1f(a,s)})()}J{2x.25.3X()}},E:6(c){l e=c.4.8.E;$(c.r).X(\'.8\').Z(\'\');l f=0;9(A 1i===\'F\'&&f==0){f=1;(6(d,s,a){l b,2s=d.1d(s)[0];9(d.3x(a)){1v}b=d.1g(s);b.2T=a;b.17=\'//4c.E.4n/\'+e.13+\'/4t.Y#4C=1\';2s.1e.1f(b,2s)}(h,\'P\',\'E-5g\'))}J{1i.3n.3p()}},z:6(b){l c=b.4.8.z;$(b.r).X(\'.8\').Z(\'3q\');l d=0;9(A 2j===\'F\'&&d==0){d=1;(6(){l a=h.1g(\'P\');a.Q=\'x/1c\';a.1r=w;a.17=\'//1M.z.o/1N.Y\';l s=h.1d(\'P\')[0];s.1e.1f(a,s)})()}J{$.3C({3:\'//1M.z.o/1N.Y\',3E:\'P\',3F:w})}},I:6(a){l b=a.4.8.I;$(a.r).X(\'.8\').Z(\'\');l c=0;9(A 43===\'F\'&&c==0){c=1;(6(){l s=h.1g(\'2z\'),24=h.1d(\'2z\')[0];s.Q=\'x/1c\';s.1r=w;s.17=\'//1N.I.o/8.Y\';24.1e.1f(s,24)})()}},p:6(a){9(a.4.8.p.1j==\'4g\'){l b=\'H:2r;\',2e=\'D:2B;H:2r;1B-1j:4y;1t-D:2B;\',2l=\'D:2C;1t-D:2C;2k-50:1H;\'}J{l b=\'H:53;\',2e=\'2g:58;2f:0 1H;D:1u;H:5c;1t-D:1u;\',2l=\'2g:5d;D:1u;1t-D:1u;\'}l c=a.1w(a.4.y.p);9(A c==="F"){c=0}$(a.r).X(\'.8\').Z(\'\'+\'\'+c+\'\'+\'\'+\'<2N 17="S://1o.p.o/3M/2N/p.3N.3O" D="10" H="10" 3Q="3R" /> 3S\');$(a.r).X(\'.p\').3T(\'1P\',6(){a.2O(\'p\')})},K:6(b){l c=b.4.8.K;$(b.r).X(\'.8\').Z(\'<2P:28 11="\'+c.11+\'" 3h="\'+(c.3!==\'\'?c.3:b.4.3)+\'">\');l d=0;9(A 1E===\'F\'&&d==0){d=1;(6(){l a=h.1g(\'P\');a.Q=\'x/1c\';a.1r=w;a.17=\'//1M.K.o/1/1N.Y\';l s=h.1d(\'P\')[0];s.1e.1f(a,s)})();s=g.3Y(6(){9(A 1E!==\'F\'){1E.2Q();21(s)}},20)}J{1E.2Q()}},N:6(b){l c=b.4.8.N;$(b.r).X(\'.8\').Z(\'

\');l d=0;9(A g.2R===\'F\'&&d==0){d=1;(6(){l a=h.1g(\'P\');a.Q=\'x/1c\';a.1r=w;a.17=\'//1M.N.o/1Z.Y\';l s=h.1d(\'P\')[0];s.1e.1f(a,s)})()}J{g.2R.1W()}},B:6(b){l c=b.4.8.B;$(b.r).X(\'.8\').Z(\'48 49\');(6(){l a=h.1g(\'P\');a.Q=\'x/1c\';a.1r=w;a.17=\'//4a.B.o/Y/4b.Y\';l s=h.1d(\'P\')[0];s.1e.1f(a,s)})()}},2S={O:6(){},E:6(){1V=g.2v(6(){9(A 1i!==\'F\'){1i.2t.2q(\'2U.2u\',6(a){1m.1l([\'1k\',\'E\',\'1L\',a])});1i.2t.2q(\'2U.4k\',6(a){1m.1l([\'1k\',\'E\',\'4l\',a])});1i.2t.2q(\'4m.1A\',6(a){1m.1l([\'1k\',\'E\',\'1A\',a])});21(1V)}},2V)},z:6(){2W=g.2v(6(){9(A 2j!==\'F\'){2j.4p.4q(\'1J\',6(a){9(a){1m.1l([\'1k\',\'z\',\'1J\'])}});21(2W)}},2V)},I:6(){},p:6(){},K:6(){},N:6(){6 4s(){1m.1l([\'1k\',\'N\',\'L\'])}},B:6(){}},2Y={O:6(a){g.19("1D://4v.2w.o/L?4w="+a.8.O.13+"&3="+V((a.8.O.3!==\'\'?a.8.O.3:a.3)),"","1b=0, 1G=0, H=2Z, D=20")},E:6(a){g.19("S://1o.E.o/30/30.3d?u="+V((a.8.E.3!==\'\'?a.8.E.3:a.3))+"&t="+a.x+"","","1b=0, 1G=0, H=2Z, D=20")},z:6(a){g.19("1D://z.o/4A/1J?x="+V(a.x)+"&3="+V((a.8.z.3!==\'\'?a.8.z.3:a.3))+(a.8.z.16!==\'\'?\'&16=\'+a.8.z.16:\'\'),"","1b=0, 1G=0, H=31, D=32")},I:6(a){g.19("S://I.o/4D/4E/2y?3="+V((a.8.I.3!==\'\'?a.8.I.3:a.3))+"&12="+a.x+"&1I=w&1T=w","","1b=0, 1G=0, H=31, D=32")},p:6(a){g.19(\'S://1o.p.o/4F?v=5&4G&4H=4I&3=\'+V((a.8.p.3!==\'\'?a.8.p.3:a.3))+\'&12=\'+a.x,\'p\',\'1b=1F,H=1h,D=1h\')},K:6(a){g.19(\'S://1o.K.o/28/?3=\'+V((a.8.p.3!==\'\'?a.8.p.3:a.3)),\'K\',\'1b=1F,H=1h,D=1h\')},N:6(a){g.19(\'1D://1o.N.o/4L/L?3=\'+V((a.8.p.3!==\'\'?a.8.p.3:a.3))+\'&4M=&4N=w\',\'N\',\'1b=1F,H=1h,D=1h\')},B:6(a){g.19(\'S://B.o/1K/2u/U/?3=\'+V((a.8.B.3!==\'\'?a.8.B.3:a.3))+\'&1s=\'+V(a.8.B.1s)+\'&1C=\'+a.8.B.1C,\'B\',\'1b=1F,H=4O,D=4P\')}};6 T(a,b){7.r=a;7.4=$.4S(w,{},23,b);7.4.L=b.L;7.4T=23;7.4U=j;7.1W()};T.W.1W=6(){l c=7;9(7.4.1p!==\'\'){1n.O=7.4.1p+\'?3={3}&Q=O\';1n.K=7.4.1p+\'?3={3}&Q=K\';1n.B=7.4.1p+\'?3={3}&Q=B\'}$(7.r).4W(7.4.3i);9(A $(7.r).m(\'12\')!==\'F\'){7.4.12=$(7.r).4X(\'m-12\')}9(A $(7.r).m(\'3\')!==\'F\'){7.4.3=$(7.r).m(\'3\')}9(A $(7.r).m(\'x\')!==\'F\'){7.4.x=$(7.r).m(\'x\')}$.1z(7.4.L,6(a,b){9(b===w){c.4.2a++}});9(c.4.3b===w){$.1z(7.4.L,6(a,b){9(b===w){4Z{c.34(a)}51(e){}}})}J 9(c.4.18!==\'\'){7.4.26(7,7.4)}J{7.2n()}$(7.r).1X(6(){9($(7).X(\'.8\').36===0&&c.4.3c===w){c.2n()}c.4.1X(c,c.4)},6(){c.4.38(c,c.4)});$(7.r).1P(6(){c.4.1P(c,c.4);1v C})};T.W.2n=6(){l c=7;$(7.r).Z(\'\');$.1z(c.4.L,6(a,b){9(b==w){2A[a](c);9(c.4.2o===w){2S[a]()}}})};T.W.34=6(c){l d=7,y=0,3=1n[c].1x(\'{3}\',V(7.4.3));9(7.4.8[c].15===w&&7.4.8[c].3!==\'\'){3=1n[c].1x(\'{3}\',7.4.8[c].3)}9(3!=\'\'&&d.4.1p!==\'\'){$.55(3,6(a){9(A a.y!=="F"){l b=a.y+\'\';b=b.1x(\'\\56\\57\',\'\');y+=1Q(b,10)}J 9(a.m&&a.m.36>0&&A a.m[0].39!=="F"){y+=1Q(a.m[0].39,10)}J 9(A a.3a!=="F"){y+=1Q(a.3a,10)}J 9(A a[0]!=="F"){y+=1Q(a[0].5b,10)}J 9(A a[0]!=="F"){}d.4.y[c]=y;d.4.1q+=y;d.2i();d.1R()}).5e(6(){d.4.y[c]=0;d.1R()})}J{d.2i();d.4.y[c]=0;d.1R()}};T.W.1R=6(){l a=0;5f(e 1Z 7.4.y){a++}9(a===7.4.2a){7.4.26(7,7.4)}};T.W.2i=6(){l a=7.4.1q,18=7.4.18;9(7.4.1w===w){a=7.1w(a)}9(18!==\'\'){18=18.1x(\'{1q}\',a);$(7.r).1U(18)}J{$(7.r).1U(\'\'+a+\'\'+(7.4.12!==\'\'?\'\'+7.4.12+\'\':\'\')+\'\')}};T.W.1w=6(a){9(a>=3e){a=(a/3e).3f(2)+"M"}J 9(a>=3g){a=(a/3g).3f(1)+"k"}1v a};T.W.2O=6(a){2Y[a](7.4);9(7.4.2o===w){l b={O:{14:\'5m\',R:\'+1\'},E:{14:\'E\',R:\'1L\'},z:{14:\'z\',R:\'1J\'},I:{14:\'I\',R:\'29\'},p:{14:\'p\',R:\'29\'},K:{14:\'K\',R:\'29\'},N:{14:\'N\',R:\'L\'},B:{14:\'B\',R:\'1K\'}};1m.1l([\'1k\',b[a].14,b[a].R])}};T.W.5o=6(){l a=$(7.r).1U();$(7.r).1U(a.1x(7.4.1q,7.4.1q+1))};T.W.5p=6(a,b){9(a!==\'\'){7.4.3=a}9(b!==\'\'){7.4.x=b}};$.5q[j]=6(b){l c=5r;9(b===i||A b===\'5s\'){1v 7.1z(6(){9(!$.m(7,\'2h\'+j)){$.m(7,\'2h\'+j,5u T(7,b))}})}J 9(A b===\'5v\'&&b[0]!==\'5w\'&&b!==\'1W\'){1v 7.1z(6(){l a=$.m(7,\'2h\'+j);9(a 5x T&&A a[b]===\'6\'){a[b].5y(a,5z.W.5A.5B(c,1))}})}}})(5C,5D,5E);',62,351,'|||url|options||function|this|buttons|if||||||||||||var|data|div|com|delicious||element|||||true|text|count|twitter|typeof|pinterest|false|height|facebook|undefined|class|width|digg|else|stumbleupon|share||linkedin|googlePlus|script|type|action|http|Plugin|button|encodeURIComponent|prototype|find|js|append||layout|title|lang|site|urlCount|via|src|template|open|href|toolbar|javascript|getElementsByTagName|parentNode|insertBefore|createElement|550|FB|size|_trackSocial|push|_gaq|urlJson|www|urlCurl|total|async|media|line|20px|return|shorterTotal|replace|callback|each|send|font|description|https|STMBLPN|no|status|3px|related|tweet|pin|like|platform|widgets|border|click|parseInt|rendererPerso|color|style|html|fb|init|hover|sharrre|in|500|clearInterval|counter|defaults|s1|plusone|render||badge|add|shareTotal|align|faces|colorscheme|cssCount|padding|float|plugin_|renderer|twttr|margin|cssShare|hashtags|loadButtons|enableTracking|annotation|subscribe|50px|fjs|Event|create|setInterval|google|gapi|submit|SCRIPT|loadButton|35px|18px|display|block|none|json|background|fff|center|1px|solid|radius|img|openPopup|su|processWidgets|IN|tracking|id|edge|1000|tw|20url|popup|900|sharer|650|360|horizontal|getSocialJson|en|length|medium|hide|total_count|shares|enableCounter|enableHover|php|1e6|toFixed|1e3|location|className|it|pointer|services|666666|XFBML|inline|parse|Tweet|normal|indent|vertical|show|baseline|apis|getElementById|bottom|5px|overflow|hidden|ajax|urls|dataType|cache|ccc|DiggThisButton|decoration|7EACEE|40679C|rel|static|small|gif|api|alt|Delicious|Add|on|nofollow|external|cdn|go|setTimeout|___gcfg|20WHERE|20link_stat|20FROM|__DBW|20click_count|20comments_fbid|commentsbox_count|root|Pin|It|assets|pinit|connect|googleplus|20total_count|20comment_count|tall|20like_count|20share_count|20normalized_url|remove|unlike|message|net|jsonp|events|bind|format|LinkedInShare|all|SELECT|plus|hl|fql|15px|countserv|intent|urlinfo|xfbml|tools|diggthis|save|noui|jump|close|graph|DiggCompact|cws|token|isFramed|700|300|v2|en_US|extend|_defaults|_name|button_count|addClass|attr|US|try|top|catch|feeds|93px|links|getJSON|u00c2|u00a0|right|getInfo|story|total_posts|26px|left|error|for|jssdk|box|12px|Arial|Helvetica|sans|Google|serif|simulateClick|update|fn|arguments|object|cursor|new|string|_|instanceof|apply|Array|slice|call|jQuery|window|document'.split('|'),0,{})) 8 | -------------------------------------------------------------------------------- /static/js/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.5.0 2 | // http://underscorejs.org 3 | // (c) 2009-2011 Jeremy Ashkenas, DocumentCloud Inc. 4 | // (c) 2011-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 5 | // Underscore may be freely distributed under the MIT license. 6 | !function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,v=e.reduce,h=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,w=i.bind,j=function(n){return n instanceof j?n:this instanceof j?(this._wrapped=n,void 0):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.5.0";var A=j.each=j.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(j.has(n,a)&&t.call(e,n[a],a,n)===r)return};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var E="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduce===v)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(E);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduceRight===h)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(E);return r},j.find=j.detect=function(n,t,r){var e;return O(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var O=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:O(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,function(n){return n[t]})},j.where=function(n,t,r){return j.isEmpty(t)?r?void 0:[]:j[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},j.findWhere=function(n,t){return j.where(n,t,!0)},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);if(!t&&j.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>e.computed&&(e={value:n,computed:a})}),e.value},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);if(!t&&j.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;ae||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;r.call(e,n[o])=0})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){return j.unzip.apply(j,o.call(arguments))},j.unzip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var M=function(){};j.bind=function(n,t){var r,e;if(w&&n.bind===w)return w.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));M.prototype=n.prototype;var u=new M;M.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},j.bindAll=function(n){var t=o.call(arguments,1);if(0===t.length)throw new Error("bindAll must be passed function names");return A(t,function(t){n[t]=j.bind(n[t],n)}),n},j.memoize=function(n,t){var r={};return t||(t=j.identity),function(){var e=t.apply(this,arguments);return j.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},j.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},j.defer=function(n){return j.delay.apply(j,[n,1].concat(o.call(arguments,1)))},j.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var c=function(){o=new Date,a=null,i=n.apply(e,u)};return function(){var l=new Date;o||r.leading!==!1||(o=l);var f=t-(l-o);return e=this,u=arguments,0>=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u)):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u=null;return function(){var i=this,a=arguments,o=function(){u=null,r||(e=n.apply(i,a))},c=r&&!u;return clearTimeout(u),u=setTimeout(o,t),c&&(e=n.apply(i,a)),e}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){var t=[];for(var r in n)j.has(n,r)&&t.push(n[r]);return t},j.pairs=function(n){var t=[];for(var r in n)j.has(n,r)&&t.push([r,n[r]]);return t},j.invert=function(n){var t={};for(var r in n)j.has(n,r)&&(t[n[r]]=r);return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o))return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var I={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};I.unescape=j.invert(I.escape);var T={escape:new RegExp("["+j.keys(I.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(I.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(T[n],function(t){return I[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},z=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(z,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var D=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}.call(this); 7 | //# sourceMappingURL=underscore-min.map -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Page Not Found | REST Hooks 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 31 | 32 | 33 | 34 | 35 | 36 | 67 | 68 |
69 |
70 |

404 - Page Not Found

71 |

The page you requested does not exist, back to safety!

72 |
73 |
74 | 75 |
76 |
77 | 78 |
79 | 80 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% if post and post.meta.title %}{{ post.meta.title }} | {% endif %}REST Hooks 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 31 | 32 | 33 | 34 | 35 | 36 | 67 | 68 | Fork me on GitHub 69 | 70 |
71 |
72 |

Reduce your REST API server load by 66x

73 |

REST Hooks are a lightweight subscription layer on top of your existing REST API.

74 | Start Coding or try a demo app 75 |
76 |
77 | 78 |
79 |
80 |
81 |

What are REST Hooks?

82 |

REST Hooks itself is not a specification, it is a collection of patterns that treat webhooks like subscriptions. These subscriptions are manipulated via a REST API just like any other resource. That's it. Really.

83 | 84 |

Read the docs.

85 |
86 |
87 | 88 |
89 |
90 |
91 |

Stop the polling madness

92 |

With most modern REST APIs, polling is the only way to detect changes. But it doesn't have to be. On average, 98.5% of polls are wasted. There is a better way. REST Hooks enable real-time communication and eliminate polling.

93 | Read the docs. 94 |
95 |
96 | 97 |
98 |
99 |
100 | 101 |
102 | 103 |
104 |
105 |

Go beyond webhooks

106 |

Webhooks are great, but really hard for users to setup. To combat this conundrum, REST Hooks are subscription-based. With REST Hooks, users need do little more than click a button to enable real-time updating.

107 | Read the docs. 108 |
109 |
110 | 111 |
112 |
113 |
114 | 115 |
116 | 117 |
118 |
119 |

Peace, love and REST

120 |

You already embrace REST. Why re-invent the wheel? REST Hooks are a simple, lightweight extension to what you're already familiar with. Skip the pedantic arguments about standards and implementation details - just use a real solution that works.

121 | Read the docs. 122 |
123 |
124 | 125 |
126 |
127 |
128 | 129 |
130 | 131 |
132 |
133 |

The real-time web is here, but REST APIs haven't kept up.

134 |

Many major players have already begun standardizing on subscription webhooks. REST Hooks consolidate this momentum and push it to a broader audience.

135 |
136 |
137 |
138 | 139 |
140 |
141 |
    142 |
  • 143 |

    1. REST Hooks are 66 times more efficient than traditional polling.

    144 |

    Case study: Zapier is a SaaS which connects hundreds of other web apps together. Polling is a necessity because it is the most ubiquitous way to detect changes.

    145 |

    Over a representative time period, Zapier polled for changes 30 million times but only took action on 460,000 of those polls (1.5% efficient).

    146 |

    Contrast this with REST Hooks, where 547,000 hooks were received over the same period and all of them were acted upon (100% efficient).

    147 |

    In other words, if everyone implemented REST Hooks, server load for both the sender and receiver could be reduced by 66x.

    148 |
  • 149 | 150 |
  • 151 |

    2. User conversion rates improve 24% with REST Hooks.

    152 |

    Case study: Wufoo is a popular survey product service which integrates with Zapier. Wufoo supports both classic webhooks (the user must copy and paste a URL from one app to the other) and REST Hooks (the webhook is automatically created).

    153 |

    Users on Zapier tried to set up integrations with Wufoo using classic webhooks and were successful 95 times out of 215 tries, a 44% conversation rate.

    154 |

    When users tried REST Hooks, however, 261 of 477 were successful, a 54.7% conversion. REST Hooks improved conversion rates by 24%.

    155 |

    With REST Hooks, user experience is also greatly improved because users don't need to go offsite to set up an integration.

    156 |
  • 157 | 158 |
  • 159 |

    3. Over 80% of developers prefer hooks.

    160 |

    Survey: When a group of 150 developers were surveyed, 80% preferred to consume a third-party REST API by being notified of changes via webhooks instead of polling.

    161 |

    Polling requires developers to maintain state and infrastructure. Support for real-time change notifications is strong because polling is very difficult and time-intensive.

    162 |

    With REST Hooks, developers can spend their time doing meaningful things with your REST API instead of fighting with the quirks of polling.

    163 |
  • 164 |
      165 |
166 |
167 |
168 | 169 |
170 | 171 |
172 |
173 |

Who's already using REST Hooks?

174 |
175 |
176 | 177 | 181 |
182 | 183 | Learn how Podio uses REST Hooks 184 | 185 |
186 | 187 |
188 | 189 | 193 | 197 | 198 |
199 | 200 |
201 | 202 | Learn how Emma uses REST Hooks 203 | 204 |
205 | 209 | 210 |
211 | 212 | 216 | 217 |
218 | 219 |
220 | 221 |
222 | 223 |
224 |
225 |

REST Hooks reduce server load,
make devs happy, and improve conversion.

226 |

Are you ready to learn how? Get started with REST Hooks!

227 |
228 |
229 |
230 | 231 |
232 | 233 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /templates/post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% if post and post.meta.title %}{{ post.meta.title }} | {% endif %}REST Hooks 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 31 | 32 | 33 | 34 | 35 | 36 | 67 | 68 | Fork me on GitHub 69 | 70 |
71 |
72 |

{{ post.meta.title }}

73 |

{{ post.meta.author}}   -   {{ post.meta.date.strftime('%b %-d %Y') }}

74 |
75 |
76 | 77 |
78 | {{ post.content }} 79 |
80 | 81 |
82 | 83 | 89 | 90 | 91 | 92 | --------------------------------------------------------------------------------